You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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线程来不及渲染进度指示器就已经重置状态,最终进度条无法显示。

修复方案

  1. 替换forEach为Future.wait + map的组合,确保所有上传任务执行完成后再返回结果
  2. 优化上传逻辑,增加异常捕获、空判断,避免上传失败时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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 21:42:14