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/responsive_layout.dart';
|
||||||
import '../../utils/http/http_client.dart';
|
import '../../utils/http/http_client.dart';
|
||||||
import '../../models/poetry_model.dart';
|
import '../../models/poetry_model.dart';
|
||||||
|
import '../../controllers/load/locally.dart';
|
||||||
|
|
||||||
/// 时间: 2026-03-25
|
/// 时间: 2026-03-25
|
||||||
/// 功能: 热门页面
|
/// 功能: 热门页面
|
||||||
@@ -130,7 +131,7 @@ class _PopularPageState extends State<PopularPage>
|
|||||||
}
|
}
|
||||||
|
|
||||||
return RefreshIndicator(
|
return RefreshIndicator(
|
||||||
onRefresh: () async => await _loadRankList(),
|
onRefresh: () async => await _loadRankList(forceRefresh: true),
|
||||||
child: NotificationListener<ScrollNotification>(
|
child: NotificationListener<ScrollNotification>(
|
||||||
onNotification: (scrollNotification) {
|
onNotification: (scrollNotification) {
|
||||||
if (scrollNotification is ScrollEndNotification &&
|
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(() {
|
setState(() {
|
||||||
_loading = true;
|
_loading = true;
|
||||||
_errorMessage = '';
|
_errorMessage = '';
|
||||||
@@ -333,6 +334,26 @@ class _PopularPageState extends State<PopularPage>
|
|||||||
? 'day'
|
? 'day'
|
||||||
: 'month';
|
: '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');
|
print('正在请求排行榜数据: type=$type, period=$type');
|
||||||
|
|
||||||
final response = await HttpClient.get(
|
final response = await HttpClient.get(
|
||||||
@@ -349,6 +370,12 @@ class _PopularPageState extends State<PopularPage>
|
|||||||
if (response.isSuccess && response.code == 0) {
|
if (response.isSuccess && response.code == 0) {
|
||||||
final data = response.data;
|
final data = response.data;
|
||||||
final rankData = data['list'] as List<dynamic>? ?? [];
|
final rankData = data['list'] as List<dynamic>? ?? [];
|
||||||
|
final rankDataList = rankData.cast<Map<String, dynamic>>();
|
||||||
|
|
||||||
|
if (isPreloadEnabled) {
|
||||||
|
print('保存数据到本地缓存');
|
||||||
|
await LocalCacheManager().cachePopularList(type, rankDataList);
|
||||||
|
}
|
||||||
|
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
setState(() {
|
setState(() {
|
||||||
@@ -361,7 +388,9 @@ class _PopularPageState extends State<PopularPage>
|
|||||||
} else {
|
} else {
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_errorMessage = response.message ?? '获取排行榜失败';
|
_errorMessage = response.message.isNotEmpty == true
|
||||||
|
? response.message
|
||||||
|
: '获取排行榜失败';
|
||||||
_loading = false;
|
_loading = false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -191,7 +191,6 @@ class _HomePageState extends State<HomePage>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
debugPrint('加载诗词失败: $e');
|
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_loading = false;
|
_loading = false;
|
||||||
@@ -269,7 +268,6 @@ class _HomePageState extends State<HomePage>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
debugPrint('加载指定诗词失败: $e');
|
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_poetryData = _createDefaultPoetryData();
|
_poetryData = _createDefaultPoetryData();
|
||||||
@@ -500,7 +498,7 @@ class _HomePageState extends State<HomePage>
|
|||||||
if (mounted) {
|
if (mounted) {
|
||||||
PoetryStateManager.showSnackBar(
|
PoetryStateManager.showSnackBar(
|
||||||
context,
|
context,
|
||||||
'加载下一条失败',
|
'加载下一条失败,建议开启离线模式',
|
||||||
backgroundColor: AppConstants.errorColor,
|
backgroundColor: AppConstants.errorColor,
|
||||||
duration: const Duration(milliseconds: 200),
|
duration: const Duration(milliseconds: 200),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import 'package:shared_preferences/shared_preferences.dart';
|
|||||||
import '../../../constants/app_constants.dart';
|
import '../../../constants/app_constants.dart';
|
||||||
import './widgets.dart';
|
import './widgets.dart';
|
||||||
import '../../home/home-load.dart';
|
import '../../home/home-load.dart';
|
||||||
|
import '../../../controllers/load/locally.dart';
|
||||||
|
|
||||||
/// 时间: 2026-03-26
|
/// 时间: 2026-03-26
|
||||||
/// 功能: 功能设置页面
|
/// 功能: 功能设置页面
|
||||||
@@ -22,6 +23,7 @@ class _AppFunSettingsPageState extends State<AppFunSettingsPage> {
|
|||||||
bool _soundEnabled = true;
|
bool _soundEnabled = true;
|
||||||
bool _vibrationEnabled = true;
|
bool _vibrationEnabled = true;
|
||||||
bool _darkModeEnabled = false;
|
bool _darkModeEnabled = false;
|
||||||
|
bool _preloadEnabled = true;
|
||||||
bool _notificationEnabled = true;
|
bool _notificationEnabled = true;
|
||||||
int _cacheSize = 128;
|
int _cacheSize = 128;
|
||||||
|
|
||||||
@@ -40,6 +42,7 @@ class _AppFunSettingsPageState extends State<AppFunSettingsPage> {
|
|||||||
setState(() {
|
setState(() {
|
||||||
_autoRefreshEnabled = prefs.getBool(_autoRefreshKey) ?? false;
|
_autoRefreshEnabled = prefs.getBool(_autoRefreshKey) ?? false;
|
||||||
_debugInfoEnabled = prefs.getBool(_debugInfoKey) ?? 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
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
@@ -106,11 +118,12 @@ class _AppFunSettingsPageState extends State<AppFunSettingsPage> {
|
|||||||
),
|
),
|
||||||
_buildSwitchItem(
|
_buildSwitchItem(
|
||||||
'预加载',
|
'预加载',
|
||||||
'开启后优先使用本地缓存,\n减少与服务器的通信次数',
|
_preloadEnabled
|
||||||
//关闭后,优先使用云端数据,无延迟,但刷新缓慢
|
? '开启后 部分数据优先使用本地缓存,减少与服务器的通信次数'
|
||||||
Icons.notifications_active,
|
: '关闭后,优先使用云端数据,无延迟,但刷新缓慢',
|
||||||
_notificationEnabled,
|
Icons.storage,
|
||||||
(value) => setState(() => _notificationEnabled = value),
|
_preloadEnabled,
|
||||||
|
_setPreload,
|
||||||
),
|
),
|
||||||
]),
|
]),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
|
|||||||
@@ -445,7 +445,7 @@ class _OfflineDataPageState extends State<OfflineDataPage> {
|
|||||||
children: [
|
children: [
|
||||||
CircularProgressIndicator(),
|
CircularProgressIndicator(),
|
||||||
SizedBox(width: 16),
|
SizedBox(width: 16),
|
||||||
Text('正在获取服务器信息...'),
|
Text('正在检测网络状态...'),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user