Flutter循环迭代中正确更新Firebase批次号(batchNo)并实现分文档存储的方案
Flutter循环迭代中正确更新Firebase批次号(batchNo)并实现分文档存储的方案
我完全理解你现在的困扰——循环里批次号始终不更新,所有数据都挤到同一个Firebase文档里,试了好几种方法都没解决对吧?咱们先拆解问题根源,再一步步给出可行的解决方案。
问题核心原因
你遇到的问题本质是异步操作的同步问题:
- 最初的循环里调用
loadAndUploadCsvData时没有加await,导致所有上传请求几乎同时发起,它们都会去Firebase查询当前最新的批次号,自然拿到的都是同一个值。 - 后来尝试维护本地
_batchNo,但循环里的updateBatchNo也是异步函数,你没等它执行完成就进入下一次迭代,所以本地变量根本没来得及更新。
解决方案一:串行异步循环(最稳妥,适用于多场景)
这个方案的核心是让每一次上传任务都等上一次完成后再执行,确保Firebase里的批次号已经更新,下一次查询能拿到正确的值。
修改按钮点击事件为异步函数
把原来的循环改成异步循环,用await等待每一次上传完成:
onPressed: () async { // 遍历批次数据,每次等待上一个上传完成再进行下一个 for (int index = 0; index < _batchDataList.length; index++) { final batchData = _batchDataList[index]; await loadAndUploadCsvData(batchData.name, batchData.bytes); log("已完成第 ${index+1} 批次上传"); } },
优化loadAndUploadCsvData函数
把函数改成返回Future<void>,并且把批次文档的创建操作只执行一次(原来你在Excel行循环里重复执行set,这完全没必要):
Future<void> loadAndUploadCsvData(String name, Uint8List? bytes) async { String extension = name.split('.').last; // 先获取当前最新的批次号 int batchNo = await getNextBatchNo(); // 创建批次文档,只需要执行一次 await _firestore .collection('availableBatches') .doc("batch$batchNo") .set({"batchNo": batchNo}); if (extension == 'xls' || extension == "xlsx") { var excel = Excel.decodeBytes(bytes!); for (var table in excel.tables.keys) { for (var row in excel.tables[table]!.rows) { // 跳过表头行 if (row[0]!.colIndex == 0 && row[0]!.rowIndex == 0) { continue; } // 创建联系人模型 ContactModel currentContact = ContactModel( name: row[0]!.value, number: row[1]!.value, type: row[2]!.value, isCalled: false, ); // 添加联系人到当前批次的子集合 await _firestore .collection('availableBatches') .doc("batch$batchNo") .collection("batchCollection") .add(currentContact.toMap()); } } } }
解决方案二:预生成批次号(更高效,适用于单一操作场景)
如果你的上传过程中没有其他地方会修改Firebase的批次号,那可以只查询一次初始批次号,之后本地自增,减少Firebase查询次数:
修改按钮点击事件
onPressed: () async { // 先获取初始批次号 int startBatchNo = await getNextBatchNo(); // 遍历批次数据,用本地自增的批次号 for (int index = 0; index < _batchDataList.length; index++) { final batchData = _batchDataList[index]; int currentBatchNo = startBatchNo + index; await loadAndUploadCsvData(batchData.name, batchData.bytes, currentBatchNo); log("已完成第 ${index+1} 批次上传,批次号:$currentBatchNo"); } },
修改loadAndUploadCsvData函数
新增批次号参数,不用再查询Firebase:
Future<void> loadAndUploadCsvData(String name, Uint8List? bytes, int batchNo) async { String extension = name.split('.').last; // 创建批次文档 await _firestore .collection('availableBatches') .doc("batch$batchNo") .set({"batchNo": batchNo}); if (extension == 'xls' || extension == "xlsx") { var excel = Excel.decodeBytes(bytes!); for (var table in excel.tables.keys) { for (var row in excel.tables[table]!.rows) { if (row[0]!.colIndex == 0 && row[0]!.rowIndex == 0) { continue; } ContactModel currentContact = ContactModel( name: row[0]!.value, number: row[1]!.value, type: row[2]!.value, isCalled: false, ); await _firestore .collection('availableBatches') .doc("batch$batchNo") .collection("batchCollection") .add(currentContact.toMap()); } } } }
关键注意点
- 异步操作一定要用
await等待完成,否则多个请求会“抢”同一个批次号。 - 避免重复的Firebase写入操作(比如原来每行都执行
set),既浪费资源又可能导致数据覆盖。 - 如果有并发修改批次号的场景(比如多个用户同时上传),建议用Firebase的事务或者递增计数器来保证批次号的唯一性,不过你的场景应该是单一用户操作,上面的方案足够。
备注:内容来源于stack exchange,提问作者Joey Leo
相关产品推荐
相关产品推荐

