Flutter调用setState后fileUrl未更新致Firestore存储错误求解
问题描述
实现Firebase Storage文件上传功能时,获取到文件对应的Firebase访问URL后,调用setState将URL赋值给字符串变量fileUrl,计划后续将该地址写入Firestore数据库,但实际运行时fileUrl始终为初始值'temp',未更新为正确的Firebase URL,无法存储正确的附件地址,相关实现代码如下:
文件选择与上传逻辑
... String fileUrl = 'temp'; ... Future uploadFile() async { if (file == null) return Container(); final fileName = basename(file!.path); final destination = 'files/$fileName'; task = FirebaseApi.uploadFile(destination, file!); if (task == null) return; final shot = await task!.whenComplete(() {}); final urlDownload = await shot.ref.getDownloadURL(); print('Download-Link: $urlDownload'); // 此处可打印出正确的URL地址 setState(() { fileUrl = urlDownload.toString(); }); }
Firestore数据库上传逻辑
void postToFirebase(school, String fileName) { print('fileUrl') ; //此处打印fileUrl始终输出初始值'temp' FirebaseFirestore.instance .collection("collection_name") .doc(doc_name) .collection("collection_name") .add({ "attachmentUrl": fileUrl, "attachment_name": fileName, }) }
按钮点击触发逻辑
onPressed: () async { uploadFile(); postToFirebase(school, fileName); }
问题原因
核心为异步调用时序错误:
uploadFile()是被async标记的异步函数,内部的文件上传、请求下载链接、setState更新变量都属于耗时异步逻辑,需要等待执行完成才能拿到结果- 按钮点击逻辑中调用
uploadFile()时未添加await关键字,程序不会阻塞等待上传流程执行完毕,会直接同步运行后续的postToFirebase(),此时fileUrl还未被赋值,因此始终读取到初始值'temp'。
修复方案
方案1:调用异步方法时添加
await,等待执行完成后再走后续逻辑
修改按钮点击触发逻辑,等待上传全流程结束、fileUrl更新完成后,再调用Firestore上传方法:onPressed: () async { await uploadFile(); // 等待上传、变量更新全部完成后再执行下一步 postToFirebase(school, fileName); }注意:需要对空场景做兼容处理,比如
file为null、上传任务创建失败的分支提前返回时,要增加判断避免将无效的初始URL写入数据库。方案2(逻辑更稳妥):直接通过方法返回值传递下载URL,不依赖类成员变量传值
从根源规避异步时序、Widget重建带来的状态不同步问题:- 修改
uploadFile方法,直接返回最终拿到的下载URL:
Future<String?> uploadFile() async { if (file == null) return null; final fileName = basename(file!.path); final destination = 'files/$fileName'; task = FirebaseApi.uploadFile(destination, file!); if (task == null) return null; final shot = await task!.whenComplete(() {}); final urlDownload = await shot.ref.getDownloadURL(); print('Download-Link: $urlDownload'); setState(() { fileUrl = urlDownload.toString(); }); // 直接返回获取到的URL return urlDownload.toString(); }- 修改按钮点击逻辑,拿到返回的有效URL后再执行数据库上传:
onPressed: () async { final? uploadedUrl = await uploadFile(); if (uploadedUrl != null) { postToFirebase(school, fileName, uploadedUrl); } }- 对应修改
postToFirebase方法,增加URL参数接收上传地址:
void postToFirebase(school, String fileName, String attachmentUrl) { FirebaseFirestore.instance .collection("collection_name") .doc(doc_name) .collection("collection_name") .add({ "attachmentUrl": attachmentUrl, "attachment_name": fileName, }) }- 修改
内容的提问来源于stack exchange,提问作者Garth
相关产品推荐
相关产品推荐

