feat(缓存): 添加本地缓存功能并优化网络状态提示
实现本地缓存管理器,支持预加载开关和排行榜数据缓存 优化网络状态提示文案和错误处理 移除调试日志打印,改进用户体验
This commit is contained in:
90
lib/controllers/load/locally.dart
Normal file
90
lib/controllers/load/locally.dart
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
);
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -445,7 +445,7 @@ class _OfflineDataPageState extends State<OfflineDataPage> {
|
||||
children: [
|
||||
CircularProgressIndicator(),
|
||||
SizedBox(width: 16),
|
||||
Text('正在获取服务器信息...'),
|
||||
Text('正在检测网络状态...'),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user