Flutter使用进度条时如何处理Future等待列表及下载任务管控问题
方案1:导航离开时自动终止下载任务
原生Dart的Future确实没有取消机制,你可以配合dart:async包自带的CancelableOperation实现任务终止:
- 在页面State中定义取消操作实例
CancelableOperation? _downloadOperation;
- 改造初始化逻辑,将下载任务包裹进可取消操作
Future<void> _initialize() async { final waitList = await service.getWaitList(_listCourseWords); _downloadOperation = CancelableOperation.fromFuture( progressWait(waitList, (completed, total) { // 任务已取消则跳过后续逻辑 if (_downloadOperation?.isCanceled == true) return; setState(() { _progressValue = completed / total; }); if (completed == total && mounted) { setState(() { isFilesDownloaded = true; }); } }), onCancel: () { // 可在此处补充终止逻辑:关闭网络请求流、删除未完成临时文件 }, ); await _downloadOperation?.valueOrCancellation(); }
- 重写dispose方法,离开页面时触发取消
@override void dispose() { _downloadOperation?.cancel(); super.dispose(); }
该方案无需全局存储,页面销毁时自动终止无效任务,适合不需要后台下载的场景。
方案2:保留下载任务,可随时返回查看进度
用单例追踪进度是可行的,更推荐你用独立下载管理类+状态监听的实现,比纯单例更易维护且避免内存泄漏:
- 封装全局下载管理单例,独立存储下载状态
class DownloadManager { DownloadManager._internal(); static final DownloadManager instance = DownloadManager._internal(); // 用ValueNotifier存储状态,支持多页面监听 final ValueNotifier<double> currentProgress = ValueNotifier(0); final ValueNotifier<bool> isDownloadCompleted = ValueNotifier(false); Future<void> startDownload(List waitList) async { await progressWait(waitList, (completed, total) { currentProgress.value = completed / total; if (completed == total) { isDownloadCompleted.value = true; } }); } }
- 页面中监听全局状态更新UI,避免重复触发下载
@override void initState() { super.initState(); // 绑定全局状态监听 DownloadManager.instance.currentProgress.addListener(_syncProgress); DownloadManager.instance.isDownloadCompleted.addListener(_syncStatus); // 未完成下载才触发新任务 if (!DownloadManager.instance.isDownloadCompleted.value) { _initialize(); } else { // 已完成直接同步状态到页面 _syncProgress(); _syncStatus(); } } void _syncProgress() { if (mounted) { setState(() { _progressValue = DownloadManager.instance.currentProgress.value; }); } } void _syncStatus() { if (mounted) { setState(() { isFilesDownloaded = DownloadManager.instance.isDownloadCompleted.value; }); } } @override void dispose() { // 仅移除当前页面的监听,不终止下载任务 DownloadManager.instance.currentProgress.removeListener(_syncProgress); DownloadManager.instance.isDownloadCompleted.removeListener(_syncStatus); super.dispose(); }
该方案下载逻辑和页面完全解耦,页面重建后会自动同步最新进度,不会出现进度不更新的问题。
普通单例方案的注意事项
如果直接用单例存储进度变量,需要注意两个问题:
- 页面初始化时要主动拉取一次当前最新进度赋值给本地变量,否则首次进入页面会显示0进度,直到下一次进度回调才更新
- 必须在页面dispose时移除所有进度回调的监听,避免持有旧页面的State引用导致内存泄漏
内容的提问来源于stack exchange,提问作者Emma Alden
相关产品推荐
相关产品推荐

