feat(缓存): 添加本地缓存功能并优化网络状态提示

实现本地缓存管理器,支持预加载开关和排行榜数据缓存
优化网络状态提示文案和错误处理
移除调试日志打印,改进用户体验
This commit is contained in:
Developer
2026-03-30 04:07:31 +08:00
parent 71e853587c
commit 37a2c92a16
5 changed files with 142 additions and 12 deletions

View File

@@ -0,0 +1,90 @@
import 'dart:convert';
import 'package:shared_preferences/shared_preferences.dart';
/// 时间: 2026-03-30
/// 功能: 本地缓存管理器
/// 介绍: 管理应用的本地缓存数据,包括排行榜数据等
/// 最新变化: 新建本地缓存管理器
class LocalCacheManager {
static final LocalCacheManager _instance = LocalCacheManager._internal();
factory LocalCacheManager() => _instance;
LocalCacheManager._internal();
static const String _preloadEnabledKey = 'preload_enabled';
static const String _popularListCachePrefix = 'popular_list_cache_';
/// 获取预加载开关状态
Future<bool> isPreloadEnabled() async {
final prefs = await SharedPreferences.getInstance();
return prefs.getBool(_preloadEnabledKey) ?? true;
}
/// 设置预加载开关状态
Future<void> setPreloadEnabled(bool enabled) async {
final prefs = await SharedPreferences.getInstance();
await prefs.setBool(_preloadEnabledKey, enabled);
}
/// 缓存排行榜数据
Future<void> cachePopularList(
String type,
List<Map<String, dynamic>> data,
) async {
final prefs = await SharedPreferences.getInstance();
final key = _popularListCachePrefix + type;
final jsonData = jsonEncode(data);
await prefs.setString(key, jsonData);
final timestampKey = '${key}_timestamp';
await prefs.setInt(timestampKey, DateTime.now().millisecondsSinceEpoch);
}
/// 获取缓存的排行榜数据
Future<List<Map<String, dynamic>>?> getCachedPopularList(String type) async {
final prefs = await SharedPreferences.getInstance();
final key = _popularListCachePrefix + type;
final jsonData = prefs.getString(key);
if (jsonData == null) {
return null;
}
try {
final decoded = jsonDecode(jsonData);
if (decoded is List) {
return decoded.cast<Map<String, dynamic>>();
}
} catch (e) {
print('解析缓存数据失败: $e');
}
return null;
}
/// 获取缓存时间戳
Future<int?> getCacheTimestamp(String type) async {
final prefs = await SharedPreferences.getInstance();
final key = '${_popularListCachePrefix}${type}_timestamp';
return prefs.getInt(key);
}
/// 清除指定类型的缓存
Future<void> clearCache(String type) async {
final prefs = await SharedPreferences.getInstance();
final key = _popularListCachePrefix + type;
await prefs.remove(key);
await prefs.remove('${key}_timestamp');
}
/// 清除所有排行榜缓存
Future<void> clearAllPopularCache() async {
final prefs = await SharedPreferences.getInstance();
final keys = prefs.getKeys();
for (final key in keys) {
if (key.startsWith(_popularListCachePrefix)) {
await prefs.remove(key);
}
}
}
}

View File

@@ -3,6 +3,7 @@ import '../../constants/app_constants.dart';
import '../../utils/responsive_layout.dart';
import '../../utils/http/http_client.dart';
import '../../models/poetry_model.dart';
import '../../controllers/load/locally.dart';
/// 时间: 2026-03-25
/// 功能: 热门页面
@@ -130,7 +131,7 @@ class _PopularPageState extends State<PopularPage>
}
return RefreshIndicator(
onRefresh: () async => await _loadRankList(),
onRefresh: () async => await _loadRankList(forceRefresh: true),
child: NotificationListener<ScrollNotification>(
onNotification: (scrollNotification) {
if (scrollNotification is ScrollEndNotification &&
@@ -318,7 +319,7 @@ class _PopularPageState extends State<PopularPage>
);
}
Future<void> _loadRankList() async {
Future<void> _loadRankList({bool forceRefresh = false}) async {
setState(() {
_loading = true;
_errorMessage = '';
@@ -333,6 +334,26 @@ class _PopularPageState extends State<PopularPage>
? 'day'
: 'month';
final isPreloadEnabled = await LocalCacheManager().isPreloadEnabled();
if (isPreloadEnabled && !forceRefresh) {
print('预加载模式:尝试从本地缓存加载数据');
final cachedData = await LocalCacheManager().getCachedPopularList(type);
if (cachedData != null && cachedData.isNotEmpty) {
print('从本地缓存加载数据成功');
if (mounted) {
setState(() {
_rankList = cachedData
.map((item) => PoetryModel.fromJson(item))
.toList();
_loading = false;
});
}
return;
}
print('本地缓存为空,从服务器加载');
}
print('正在请求排行榜数据: type=$type, period=$type');
final response = await HttpClient.get(
@@ -349,6 +370,12 @@ class _PopularPageState extends State<PopularPage>
if (response.isSuccess && response.code == 0) {
final data = response.data;
final rankData = data['list'] as List<dynamic>? ?? [];
final rankDataList = rankData.cast<Map<String, dynamic>>();
if (isPreloadEnabled) {
print('保存数据到本地缓存');
await LocalCacheManager().cachePopularList(type, rankDataList);
}
if (mounted) {
setState(() {
@@ -361,7 +388,9 @@ class _PopularPageState extends State<PopularPage>
} else {
if (mounted) {
setState(() {
_errorMessage = response.message ?? '获取排行榜失败';
_errorMessage = response.message.isNotEmpty == true
? response.message
: '获取排行榜失败';
_loading = false;
});
}

View File

@@ -191,7 +191,6 @@ class _HomePageState extends State<HomePage>
}
}
} catch (e) {
debugPrint('加载诗词失败: $e');
if (mounted) {
setState(() {
_loading = false;
@@ -269,7 +268,6 @@ class _HomePageState extends State<HomePage>
}
}
} catch (e) {
debugPrint('加载指定诗词失败: $e');
if (mounted) {
setState(() {
_poetryData = _createDefaultPoetryData();
@@ -500,7 +498,7 @@ class _HomePageState extends State<HomePage>
if (mounted) {
PoetryStateManager.showSnackBar(
context,
'加载下一条失败',
'加载下一条失败,建议开启离线模式',
backgroundColor: AppConstants.errorColor,
duration: const Duration(milliseconds: 200),
);

View File

@@ -3,6 +3,7 @@ import 'package:shared_preferences/shared_preferences.dart';
import '../../../constants/app_constants.dart';
import './widgets.dart';
import '../../home/home-load.dart';
import '../../../controllers/load/locally.dart';
/// 时间: 2026-03-26
/// 功能: 功能设置页面
@@ -22,6 +23,7 @@ class _AppFunSettingsPageState extends State<AppFunSettingsPage> {
bool _soundEnabled = true;
bool _vibrationEnabled = true;
bool _darkModeEnabled = false;
bool _preloadEnabled = true;
bool _notificationEnabled = true;
int _cacheSize = 128;
@@ -40,6 +42,7 @@ class _AppFunSettingsPageState extends State<AppFunSettingsPage> {
setState(() {
_autoRefreshEnabled = prefs.getBool(_autoRefreshKey) ?? false;
_debugInfoEnabled = prefs.getBool(_debugInfoKey) ?? false;
_preloadEnabled = prefs.getBool('preload_enabled') ?? true;
});
}
}
@@ -66,6 +69,15 @@ class _AppFunSettingsPageState extends State<AppFunSettingsPage> {
}
}
Future<void> _setPreload(bool value) async {
await LocalCacheManager().setPreloadEnabled(value);
if (mounted) {
setState(() {
_preloadEnabled = value;
});
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
@@ -106,11 +118,12 @@ class _AppFunSettingsPageState extends State<AppFunSettingsPage> {
),
_buildSwitchItem(
'预加载',
'开启后优先使用本地缓存,\n减少与服务器的通信次数',
//关闭后,优先使用云端数据,无延迟,但刷新缓慢
Icons.notifications_active,
_notificationEnabled,
(value) => setState(() => _notificationEnabled = value),
_preloadEnabled
? '开启后 部分数据优先使用本地缓存,减少与服务器的通信次数'
: '关闭后,优先使用云端数据,无延迟,但刷新缓慢',
Icons.storage,
_preloadEnabled,
_setPreload,
),
]),
const SizedBox(height: 16),

View File

@@ -445,7 +445,7 @@ class _OfflineDataPageState extends State<OfflineDataPage> {
children: [
CircularProgressIndicator(),
SizedBox(width: 16),
Text('正在获取服务器信息...'),
Text('正在检测网络状态...'),
],
),
);