Flutter双页上传多图到Firebase时环形进度指示器不显示问题
Flutter图片上传页环形进度指示器不显示问题排查修复
业务场景
开发房屋管理应用时采用双页面结构处理房源提交逻辑:
- 表单页:填写房屋基础通用信息
- 图片上传页:处理图片选择、上传至Firebase的逻辑
表单页通过路由跳转等待图片上传页返回的图片链接列表,实现代码如下:
// 表单页等待图片上传页返回结果 images = await Navigator.push(context, MaterialPageRoute(builder: (context) => AddPictures())); // 图片上传页完成操作后返回链接列表 Navigator.pop(context,imageStrings);
问题现象
图片上传完成后可正常返回Firebase存储的图片链接列表,但点击上传按钮时CircularProgressIndicator环形进度指示器始终无法正常显示,常规排查方案均未定位到原因。
现有实现代码
图片上传页布局代码
// Widget build 外定义的两个列表 List<XFile> imagesXFiles = []; // 存储从相册/相机选择的原始图片文件 List<String> imageStrings = []; // 存储Firebase返回的图片访问链接 // body部分布局 body: isLoading == true ? CircularProgressIndicator() : Column( children: [ Expanded( // 网格展示已选图片,第一个格子为选图入口按钮 child: GridView.builder( gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 3, crossAxisSpacing: 2.0, mainAxisSpacing: 2.0 ), itemCount: imagesXFiles.length + 1, itemBuilder: (BuildContext context, int index) { return index == 0 ? GestureDetector( onTap: (){ // 唤起选图弹窗,选择的图片存入imagesXFiles列表 _showPicker(context); }, child: Container( decoration: BoxDecoration( color: Colors.black12, borderRadius: BorderRadius.circular(5.0), ), child: Icon( Icons.add, color: Colors.black, size: 30.0, ), ), ): Container( child: Image( image: FileImage(File(imagesXFiles[index-1].path)), fit: BoxFit.fill ), ); }, ), ), TextButton( onPressed: ()async{ setState(() { isLoading = true; }); imageStrings = await uploadImages(imagesXFiles).whenComplete(() { setState(() { isLoading = false; Navigator.pop(context,imageStrings); }); }); }, child: Text("Upload",style: TextStyle(color: Colors.black,fontSize: 25),)), ], ),
Firebase图片上传封装函数
Future<List<String>> uploadImages(List<XFile> imagesXFiles) async { imagesXFiles.forEach((image) async { final storageRef = storage.ref().child(Random().nextInt(100).toString()); await storageRef.putFile(File(image.path)); String imageURL = await storageRef.getDownloadURL(); imageStrings.add(imageURL); firebaseFirestore .collection("housePictures") .add({ "imageURL" : imageURL, }); }); return imageStrings; }
问题根因
uploadImages函数中使用forEach执行异步上传任务时,forEach不会等待内部async回调的异步任务执行完成,函数会在启动所有上传任务后立刻提前返回未完成填充的列表,导致isLoading状态刚被设为true就马上被whenComplete回调设为false,UI线程来不及渲染进度指示器就已经重置状态,最终进度条无法显示。
修复方案
- 替换
forEach为Future.wait+map的组合,确保所有上传任务执行完成后再返回结果 - 优化上传逻辑,增加异常捕获、空判断,避免上传失败时loading状态卡死、文件名重复覆盖等问题
修复后的上传函数代码:
Future<List<String>> uploadImages(List<XFile> imagesXFiles) async { List<String> imageStrings = []; // 等待所有图片上传任务全部完成 await Future.wait(imagesXFiles.map((image) async { // 文件名加入时间戳避免随机数重复覆盖文件 final storageRef = storage.ref().child("house_images/${DateTime.now().millisecondsSinceEpoch}_${Random().nextInt(100)}.jpg"); await storageRef.putFile(File(image.path)); String imageURL = await storageRef.getDownloadURL(); imageStrings.add(imageURL); // 等待firestore写入完成再执行后续逻辑 await firebaseFirestore.collection("housePictures").add({ "imageURL" : imageURL, }); })); return imageStrings; }
优化后的上传按钮点击逻辑:
TextButton( onPressed: ()async{ // 未选择图片时直接提示,不触发上传 if (imagesXFiles.isEmpty) { ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text("请先选择需要上传的图片"))); return; } setState(() { isLoading = true; }); try { List<String> uploadedUrls = await uploadImages(imagesXFiles); if (mounted) { Navigator.pop(context, uploadedUrls); } } catch (e) { // 上传失败时重置loading状态,提示错误 if (mounted) { ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text("上传失败:${e.toString()}"))); setState(() { isLoading = false; }); } } }, child: const Text("Upload",style: TextStyle(color: Colors.black,fontSize: 25), )
内容的提问来源于stack exchange,提问作者Zuher Abud Said
相关产品推荐
相关产品推荐

