如何在Flutter全局Snackbar中实现带下载百分比的进度指示器?
问题根因
- 你每次触发进度回调时都直接调用
showSnackBar,没有先隐藏之前的SnackBar,新的SnackBar会进入队列等待旧的自动消失才会显示,自然看不到进度更新 - 你用的是当前触发下载的页面的
BuildContext,跳转到其他页面后,这个context对应的ScaffoldMessenger已经不在前台,SnackBar会跟着旧页面消失,无法跨页面显示 snacBarDownloading是静态传入progress值,构建完成后不会自动响应静态变量的变化
修复步骤
第一步:配置全局ScaffoldMessenger,实现跨页面显示SnackBar
在项目的MaterialApp入口配置全局key,不需要依赖页面context就能调用SnackBar:
// 全局定义顶层ScaffoldMessenger的key final GlobalKey<ScaffoldMessengerState> scaffoldMessengerKey = GlobalKey<ScaffoldMessengerState>(); // 注册到MaterialApp MaterialApp( scaffoldMessengerKey: scaffoldMessengerKey, // 其余原有配置保持不变 )
第二步:修改CustomSnackBar逻辑
每次更新进度前先移除旧的下载SnackBar,再显示新的,同时适配全局ScaffoldMessenger:
class CustomSnackBar { static bool isDownloading = false; static String progress = "0%"; CustomSnackBar._(); static Future<void> downloadFile(String fileUrl, String fileName) async { Dio dio = Dio(); isDownloading = true; progress = "0%"; try { var dir = await getApplicationDocumentsDirectory(); // 先确保下载目录存在,避免写入失败 await Directory("${dir.path}/notifications_files").create(recursive: true); await dio.download( fileUrl, "${dir.path}/notifications_files/$fileName.pdf", onReceiveProgress: (rec, total) { if(total <= 0) return; progress = '${((rec / total) * 100).toStringAsFixed(0)} %'; updateDownloadSnackBar(); } ); isDownloading = false; // 下载完成后隐藏进度条,显示成功提示 scaffoldMessengerKey.currentState?.hideCurrentSnackBar(); scaffoldMessengerKey.currentState?.showSnackBar( const SnackBar(content: Text("下载完成"), duration: Duration(seconds: 2)) ); } catch (e) { isDownloading = false; scaffoldMessengerKey.currentState?.hideCurrentSnackBar(); scaffoldMessengerKey.currentState?.showSnackBar( SnackBar(content: Text("下载失败:${e.toString()}"), duration: const Duration(seconds: 2)) ); } } static updateDownloadSnackBar() { // 先移除之前的下载SnackBar,再显示最新进度的SnackBar scaffoldMessengerKey.currentState?.hideCurrentSnackBar(); scaffoldMessengerKey.currentState?.showSnackBar( SnackBar( duration: const Duration(days: 100), backgroundColor: kPrimaryColor, content: snacBarDownloading(progress), action: SnackBarAction( textColor: kWhiteColor, label: 'Dismiss', onPressed: () { scaffoldMessengerKey.currentState?.hideCurrentSnackBar(); }, ), ), ); } } Widget snacBarDownloading(String progress) { return Row(children: [ const SizedBox( width: 25, height: 25, child: CircularProgressIndicator(color: kWhiteColor)), const SizedBox( width: 10, ), Text("Downloading $progress ...", style: const TextStyle(color: kWhiteColor)) ]); }
第三步:修改调用逻辑
不需要再传入当前页面的context:
return InkWell( onLongPress: () { if (!CustomSnackBar.isDownloading && widget.notificationList[index].fileUrl != null && widget.notificationList[index].fileUrl!.isNotEmpty) { CustomSnackBar.downloadFile( widget.notificationList[index].fileUrl!, widget.notificationList[index].serviceId!); } }, // 其余原有配置保持不变 );
内容的提问来源于stack exchange,提问作者Jed
相关产品推荐
相关产品推荐

