Flutter for OpenHarmony:从零搭建今日资讯App(十九)缓存管理功能的实现
摘要:Flutter缓存管理功能设计与实现 本文介绍了Flutter应用中缓存管理功能的设计思路和实现方法。缓存管理应包含缓存大小展示、分类清理、一键清除等功能。设计要点包括: 分类型展示缓存大小(图片、文章、视频等) 提供单类清理和全部清理两种操作 清除操作需二次确认并显示释放空间大小 操作完成后给予即时反馈 实现上采用ListView布局,顶部卡片显示总缓存大小,中间列表展示分类缓存,底部设置

缓存是把双刃剑。用得好,能让应用秒开,体验飞起;用不好,占满存储空间,用户骂娘。今天咱们就来聊聊,怎么做一个既实用又友好的缓存管理功能。
缓存管理的设计思路
做缓存管理之前,得先想清楚要展示什么、怎么操作。
展示缓存大小是最基本的。用户要知道应用占了多少空间,心里才有数。而且要分类展示:图片缓存多少、文章缓存多少、视频缓存多少。这样用户能有针对性地清理。
清除操作要分级。可以清除单个类型的缓存,也可以一键清除全部。给用户选择的权利,不要强制清除所有缓存。
操作要有确认。清除缓存是不可逆的,必须二次确认。而且要告诉用户会释放多少空间,让用户知道清除的价值。
反馈要及时。清除完成后,要立即更新显示的缓存大小,并提示用户操作成功。不要让用户等待或困惑。
页面整体结构
缓存管理页面用StatelessWidget,因为缓存大小是模拟数据,不需要管理状态:
class CacheSettingsScreen extends StatelessWidget {
const CacheSettingsScreen({super.key});
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('缓存管理'),
),
body: ListView(
children: [
_buildCacheSizeCard(),
const Divider(),
_buildCacheItem('图片缓存', '32.5 MB', Icons.image),
_buildCacheItem('文章缓存', '8.3 MB', Icons.article),
_buildCacheItem('视频缓存', '4.4 MB', Icons.video_library),
const Divider(),
_buildClearAllButton(),
],
),
);
}
}
用ListView包含多个部分:顶部的缓存大小卡片、中间的分类缓存列表、底部的清除全部按钮。用Divider分隔不同区域。
实际项目中,应该用StatefulWidget,因为清除缓存后要更新显示。但咱们这里用模拟数据,就用StatelessWidget简化代码。
缓存大小卡片的设计
顶部的卡片要醒目,让用户一眼就能看到总缓存大小:
Padding(
padding: const EdgeInsets.all(16),
child: Card(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
children: [
const Icon(
Icons.storage,
size: 64,
color: Colors.blue,
),
const SizedBox(height: 16),
const Text(
'45.2 MB',
style: TextStyle(
fontSize: 32,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 8),
Text(
'当前缓存大小',
style: TextStyle(
fontSize: 14,
color: Colors.grey[600],
),
),
],
),
),
),
)
Card提供了卡片的背景和阴影,让内容有层次感。Padding: 16让卡片不贴边,视觉上更舒服。
Icon用存储图标,size: 64很大,视觉冲击力强。color: Colors.blue用蓝色,表示这是信息展示,不是警告。
Text显示缓存大小,fontSize: 32很大,fontWeight: FontWeight.bold加粗。这是页面最重要的信息,要突出显示。
下面的"当前缓存大小"用小字号、灰色,作为说明文字。
这个卡片的设计很清晰:大图标、大数字、小说明,用户一眼就能看懂。
分类缓存列表的实现
每个缓存类型用ListTile展示:
ListTile(
leading: const Icon(Icons.image),
title: const Text('图片缓存'),
subtitle: const Text('32.5 MB'),
trailing: TextButton(
onPressed: () {
_showClearDialog(context, '图片缓存');
},
child: const Text('清除'),
),
)
leading是左侧的图标,不同类型用不同图标:图片用Icons.image,文章用Icons.article,视频用Icons.video_library。图标要直观,让用户一眼就知道是什么类型。
title是缓存类型的名称,subtitle是缓存大小。这样信息层次清晰,不会混在一起。
trailing是右侧的清除按钮。用TextButton而不是IconButton,因为"清除"两个字比图标更明确。用户不用猜这个按钮是干什么的。
清除单个缓存的对话框
点击清除按钮,弹出确认对话框:
void _showClearDialog(BuildContext context, String type) {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text('清除$type'),
content: Text('确定要清除$type吗?'),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('取消'),
),
TextButton(
onPressed: () {
Navigator.pop(context);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('已清除$type')),
);
},
child: const Text('确定'),
),
],
),
);
}
title和content都用了字符串插值,动态显示缓存类型。这样一个方法就能处理所有类型,不用写三个重复的方法。
actions有两个按钮:取消和确定。取消只是关闭对话框,确定会关闭对话框并显示提示。
ScaffoldMessenger显示SnackBar提示用户操作成功。这个反馈很重要,让用户知道清除完成了。
清除全部缓存的按钮
底部有个醒目的按钮,清除所有缓存:
Padding(
padding: const EdgeInsets.all(16),
child: ElevatedButton(
onPressed: () {
_showClearAllDialog(context);
},
style: ElevatedButton.styleFrom(
padding: const EdgeInsets.symmetric(vertical: 16),
),
child: const Text('清除全部缓存'),
),
)
用ElevatedButton而不是TextButton,因为这是主要操作,要突出显示。padding: vertical 16让按钮更高,更容易点击。
Padding: 16让按钮不贴边,和上面的内容有间距。
清除全部缓存的对话框
清除全部缓存的对话框要更详细:
void _showClearAllDialog(BuildContext context) {
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Text('清除全部缓存'),
content: const Text('确定要清除所有缓存吗?这将释放 45.2 MB 空间。'),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('取消'),
),
TextButton(
onPressed: () {
Navigator.pop(context);
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('已清除全部缓存')),
);
},
child: const Text('确定'),
),
],
),
);
}
content不仅确认操作,还告诉用户"这将释放 45.2 MB 空间"。这个信息很重要,让用户知道清除的价值,更愿意点确定。
实际计算缓存大小
上面的代码用的是模拟数据,实际项目要真的计算缓存大小。可以用path_provider获取缓存目录:
import 'package:path_provider/path_provider.dart';
import 'dart:io';
Future<int> _getCacheSize() async {
final cacheDir = await getTemporaryDirectory();
return _calculateDirectorySize(cacheDir);
}
int _calculateDirectorySize(Directory dir) {
int totalSize = 0;
try {
if (dir.existsSync()) {
dir.listSync(recursive: true, followLinks: false)
.forEach((FileSystemEntity entity) {
if (entity is File) {
totalSize += entity.lengthSync();
}
});
}
} catch (e) {
print('计算缓存大小失败: $e');
}
return totalSize;
}
getTemporaryDirectory获取临时目录,这是Flutter存放缓存的地方。listSync遍历目录下的所有文件,lengthSync获取文件大小,累加起来就是总缓存大小。
格式化缓存大小
缓存大小要格式化成易读的格式:
String _formatBytes(int bytes) {
if (bytes < 1024) {
return '$bytes B';
} else if (bytes < 1024 * 1024) {
return '${(bytes / 1024).toStringAsFixed(1)} KB';
} else if (bytes < 1024 * 1024 * 1024) {
return '${(bytes / (1024 * 1024)).toStringAsFixed(1)} MB';
} else {
return '${(bytes / (1024 * 1024 * 1024)).toStringAsFixed(1)} GB';
}
}
根据大小选择合适的单位:小于1KB显示字节,小于1MB显示KB,小于1GB显示MB,否则显示GB。**toStringAsFixed(1)**保留一位小数。
这样显示的缓存大小更友好,用户一眼就能看懂。
清除缓存的实现
真正清除缓存要删除文件:
Future<void> _clearCache() async {
try {
final cacheDir = await getTemporaryDirectory();
if (cacheDir.existsSync()) {
cacheDir.deleteSync(recursive: true);
cacheDir.createSync(); // 重新创建空目录
}
} catch (e) {
print('清除缓存失败: $e');
}
}
deleteSync(recursive: true)递归删除目录下的所有文件。删除后要createSync重新创建目录,否则应用可能出错。
注意这里用了try-catch,因为删除文件可能失败(权限不足、文件被占用等)。失败时不要让应用崩溃,打印错误日志就行。
分类清除缓存
如果要分类清除缓存,需要把不同类型的缓存放在不同目录:
Future<void> _clearImageCache() async {
final cacheDir = await getTemporaryDirectory();
final imageDir = Directory('${cacheDir.path}/images');
if (imageDir.existsSync()) {
imageDir.deleteSync(recursive: true);
imageDir.createSync();
}
}
Future<void> _clearArticleCache() async {
final cacheDir = await getTemporaryDirectory();
final articleDir = Directory('${cacheDir.path}/articles');
if (articleDir.existsSync()) {
articleDir.deleteSync(recursive: true);
articleDir.createSync();
}
}
应用运行时,把图片缓存到images目录,文章缓存到articles目录。清除时就能分类清除了。
用StatefulWidget管理状态
实际项目要用StatefulWidget,清除缓存后更新显示:
class CacheSettingsScreen extends StatefulWidget {
const CacheSettingsScreen({super.key});
State<CacheSettingsScreen> createState() => _CacheSettingsScreenState();
}
class _CacheSettingsScreenState extends State<CacheSettingsScreen> {
int _totalSize = 0;
int _imageSize = 0;
int _articleSize = 0;
int _videoSize = 0;
bool _isLoading = true;
void initState() {
super.initState();
_loadCacheSizes();
}
Future<void> _loadCacheSizes() async {
setState(() {
_isLoading = true;
});
_totalSize = await _getCacheSize();
_imageSize = await _getImageCacheSize();
_articleSize = await _getArticleCacheSize();
_videoSize = await _getVideoCacheSize();
setState(() {
_isLoading = false;
});
}
Future<void> _clearImageCache() async {
await _clearImageCacheFiles();
await _loadCacheSizes(); // 重新加载缓存大小
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('已清除图片缓存')),
);
}
}
}
_isLoading控制加载状态,加载时显示进度条。_loadCacheSizes计算所有缓存大小。清除缓存后调用**_loadCacheSizes**重新加载,显示的数字就会更新。
mounted检查Widget是否还在树上,避免清除缓存后页面已经关闭,调用setState报错。
加载状态的显示
计算缓存大小可能需要时间,要显示加载状态:
Widget build(BuildContext context) {
if (_isLoading) {
return Scaffold(
appBar: AppBar(
title: const Text('缓存管理'),
),
body: const Center(
child: CircularProgressIndicator(),
),
);
}
return Scaffold(
appBar: AppBar(
title: const Text('缓存管理'),
),
body: ListView(
children: [
_buildCacheSizeCard(),
// ...
],
),
);
}
加载时显示进度条,加载完显示内容。这样用户不会觉得应用卡住了。
清除缓存的进度提示
清除缓存也可能需要时间,可以显示进度对话框:
Future<void> _clearAllCache() async {
// 显示进度对话框
showDialog(
context: context,
barrierDismissible: false,
builder: (context) => const AlertDialog(
content: Row(
children: [
CircularProgressIndicator(),
SizedBox(width: 16),
Text('正在清除缓存...'),
],
),
),
);
// 清除缓存
await _clearCacheFiles();
await _loadCacheSizes();
// 关闭进度对话框
if (mounted) {
Navigator.pop(context);
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('已清除全部缓存')),
);
}
}
barrierDismissible: false防止用户点击外部关闭对话框。清除完成后手动关闭对话框,显示成功提示。
缓存策略的优化
除了清除缓存,还可以优化缓存策略。
设置缓存大小限制:
class CacheManager {
static const int maxCacheSize = 100 * 1024 * 1024; // 100MB
Future<void> checkAndClearCache() async {
final size = await _getCacheSize();
if (size > maxCacheSize) {
await _clearOldestCache();
}
}
Future<void> _clearOldestCache() async {
final cacheDir = await getTemporaryDirectory();
final files = cacheDir.listSync(recursive: true)
.whereType<File>()
.toList();
// 按修改时间排序
files.sort((a, b) =>
a.lastModifiedSync().compareTo(b.lastModifiedSync()));
// 删除最旧的文件,直到缓存小于限制
int currentSize = await _getCacheSize();
for (final file in files) {
if (currentSize <= maxCacheSize) break;
final fileSize = file.lengthSync();
file.deleteSync();
currentSize -= fileSize;
}
}
}
设置缓存过期时间:
Future<void> _clearExpiredCache() async {
final cacheDir = await getTemporaryDirectory();
final now = DateTime.now();
final expireDuration = const Duration(days: 7);
cacheDir.listSync(recursive: true)
.whereType<File>()
.forEach((file) {
final lastModified = file.lastModifiedSync();
if (now.difference(lastModified) > expireDuration) {
file.deleteSync();
}
});
}
按使用频率清除:
class CacheItem {
final String path;
int accessCount = 0;
DateTime lastAccess = DateTime.now();
}
Future<void> _clearLeastUsedCache() async {
// 根据访问次数和最后访问时间计算分数
// 删除分数最低的缓存
}
图片缓存的特殊处理
图片缓存通常用cached_network_image插件,它有自己的缓存管理:
import 'package:cached_network_image/cached_network_image.dart';
Future<void> _clearImageCache() async {
await DefaultCacheManager().emptyCache();
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('已清除图片缓存')),
);
}
}
Future<int> _getImageCacheSize() async {
final cacheDir = await DefaultCacheManager().getDirectory();
return _calculateDirectorySize(cacheDir);
}
DefaultCacheManager是cached_network_image的缓存管理器,emptyCache清除所有图片缓存。
缓存统计信息
可以显示更详细的缓存统计:
class CacheStats {
final int totalSize;
final int fileCount;
final DateTime oldestFile;
final DateTime newestFile;
CacheStats({
required this.totalSize,
required this.fileCount,
required this.oldestFile,
required this.newestFile,
});
}
Future<CacheStats> _getCacheStats() async {
final cacheDir = await getTemporaryDirectory();
final files = cacheDir.listSync(recursive: true)
.whereType<File>()
.toList();
int totalSize = 0;
DateTime? oldest;
DateTime? newest;
for (final file in files) {
totalSize += file.lengthSync();
final modified = file.lastModifiedSync();
if (oldest == null || modified.isBefore(oldest)) {
oldest = modified;
}
if (newest == null || modified.isAfter(newest)) {
newest = modified;
}
}
return CacheStats(
totalSize: totalSize,
fileCount: files.length,
oldestFile: oldest ?? DateTime.now(),
newestFile: newest ?? DateTime.now(),
);
}
显示这些统计信息,让用户更了解缓存情况。
自动清理缓存
可以在应用启动时自动清理过期缓存:
class MyApp extends StatelessWidget {
Widget build(BuildContext context) {
_autoCleanCache();
return MaterialApp(
home: MainScreen(),
);
}
Future<void> _autoCleanCache() async {
final prefs = await SharedPreferences.getInstance();
final lastClean = prefs.getInt('lastCacheClean') ?? 0;
final now = DateTime.now().millisecondsSinceEpoch;
// 7天清理一次
if (now - lastClean > 7 * 24 * 60 * 60 * 1000) {
await _clearExpiredCache();
await prefs.setInt('lastCacheClean', now);
}
}
}
常见问题
缓存大小计算很慢:可以在后台线程计算,或者缓存计算结果。
清除缓存后应用出错:可能是删除了正在使用的文件。清除前要确保文件没有被占用。
缓存大小不准确:可能是有些缓存不在临时目录。要检查所有可能的缓存位置。
写在最后
缓存管理看起来简单,但要做好不容易。要准确计算缓存大小,要安全地清除缓存,要及时更新显示,要给用户友好的反馈。
最重要的是用户体验。操作要简单,反馈要及时,信息要清晰。这些细节做好了,用户才会觉得应用专业、可靠。
代码写完了,记得多测试。不同大小的缓存显示对不对?清除缓存会不会出错?清除后显示更新了吗?这些场景都要测试到。
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
在这里你可以找到更多Flutter开发资源,与其他开发者交流经验,共同进步。
更多推荐



所有评论(0)