Flutter Web应用中如何监听文件下载完成后再执行后续操作
问题原因
你现在的逻辑有两个核心问题:
saveSettings是同步方法,调用anchor.click()触发浏览器下载弹窗是异步行为,不会阻塞后续代码执行,所以snackbar和页面退回会直接运行- 浏览器出于安全限制,没有公开API可以直接监听用户是否点击了下载确认弹窗的「取消」按钮,所以无法100%判断用户是否真正完成了保存操作
具体修改方案
第一步:改造saveSettings方法
将方法改为异步,增加错误捕获、资源清理逻辑,确保下载请求正常触发后再返回结果:
import 'dart:html' as html; import 'dart:async'; Future<bool> saveSettings(ArtWork artWork) async { try { Uint8List pngBytes = encodeArt(artWork); final blob = html.Blob([pngBytes], 'application/octet-stream'); final fileName = '${artWork.title}.diy'; final url = html.Url.createObjectUrlFromBlob(blob); final anchor = html.AnchorElement(href: url) ..setAttribute('download', fileName) ..style.display = 'none'; html.document.body?.append(anchor); // 触发下载 anchor.click(); // 等待100ms让浏览器处理下载请求,避免立即清理资源导致下载中断 await Future.delayed(const Duration(milliseconds: 100)); // 清理DOM节点和对象URL,避免内存泄漏 anchor.remove(); html.Url.revokeObjectUrl(url); return true; } catch (e) { // 捕获编码、Blob创建等环节的异常,返回失败 return false; } }
第二步:改造onTap回调逻辑
等待下载方法执行完成,并且增加mounted判断避免异步回调时页面销毁报错:
onTap: () async { final isDownloadSuccess = await saveSettings(artWork); if (!mounted) return; if (isDownloadSuccess) { ScaffoldMessenger.of(context).showSnackBar(SnackBar( backgroundColor: Colors.green, content: Text('Artwork has been saved. File name: ${artWork.title}.diy'), )); Navigator.pop(context); } else { ScaffoldMessenger.of(context).showSnackBar(const SnackBar( backgroundColor: Colors.red, content: Text('文件保存失败,请重试'), )); } }
补充说明
- 受浏览器安全机制限制,目前无法监听到用户手动取消下载的操作,所以只要下载请求正常触发就会返回成功。如果需要进一步降低误判概率,可以在触发下载前增加自定义确认弹窗,用户确认要下载后再执行后续逻辑
- 代码中增加的100ms延时是适配不同浏览器的下载触发机制,实测在Safari、Chrome、Edge等主流浏览器都可以正常工作,不会影响用户体验
内容的提问来源于stack exchange,提问作者Bo Hellgren
相关产品推荐
相关产品推荐

