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

Flutter更新Firebase Firestore数组时报lost connection错误致模拟器崩溃

问题根因

触发更新后模拟器崩溃报连接丢失,核心是4个代码逻辑错误引发内存溢出、死循环,和Firestore服务本身无关:

  • 全局定义的listTest没有清空逻辑,每次点击列表项都会执行listTest.addAll(data['allItems']),多次点击后列表会无限累加重复数据,内存占用持续上涨直到进程崩溃
  • myTest写入方法的数据结构写错:把整个listTest数组又套了一层方括号,存到Firestore的allItems字段变成了二维数组[[{item1}, {item2}...]],和代码预期的一维对象数组结构完全不匹配,后续读取时会报类型错误
  • 你用StreamBuilder实时监听目标文档,每次写入Firestore都会触发stream推送新快照,但是点击逻辑里没有加重复提交拦截,会触发「写入→刷新→自动触发写入→再次刷新」的死循环,短时间大量请求直接打崩模拟器连接
  • set写入方法注释掉了SetOptions(merge:true)配置,默认会全量覆盖整个文档,没传入的字段会被直接删除,很容易丢数据
分步修复方案

1. 修正状态变量定义,避免全局污染

删掉全局声明的List listTest =[];,把列表变量放到_DetailScreen_CheckList_V3State类内部作为页面私有状态,不要用全局变量存单页面状态,避免多页面实例互相干扰。
另外不要自己在State类里写构造函数传docID,Flutter的State类直接通过widget.docID就能拿到组件传入的参数,自定义构造传参很容易出现状态不同步问题。

class _DetailScreen_CheckList_V3State extends State<DetailScreen_CheckList_V3> {
  // 删掉自定义的docID变量和对应的构造函数,统一用widget.docID拿参数
  List<Map<String, dynamic>> listTest = [];
  // 加更新锁,防止重复提交触发死循环
  bool isUpdating = false;

2. 修复列表点击逻辑,避免无限累加

点击逻辑里每次给临时列表赋值前先清空旧数据,同时用更新锁做拦截,上一次写入没完成的时候不允许重复触发操作:

onTap: () async {
  // 重复点击直接拦截
  if(isUpdating) return;
  isUpdating = true;
  String checked = data['allItems'][index]['itemChecked'];
  String myListName = data['listName'];

  // 核心修复:每次赋值前清空列表,避免多次addAll导致数据无限累加
  listTest.clear();
  listTest.addAll(List<Map<String,dynamic>>.from(data['allItems']));

  setState(() {
    checked = checked == 'Yes' ? 'No' : 'Yes';
    listTest[index]['itemChecked'] = checked;
  });

  // 等写入完成后再放开锁
  await myTest(myListName);
  isUpdating = false;
}

3. 修正Firestore写入逻辑

去掉allItems字段外层多余的数组套嵌,打开merge配置避免全量覆盖文档:

Future myTest(currentListName) async {
  await FirebaseFirestore.instance
      .collection('Users')
      .doc(FirebaseAuth.instance.currentUser.uid)
      .collection('lists')
      .doc(widget.docID)
      .set(
    {
      'listName': currentListName,
      // 核心修复:listTest本身就是数组,不要再套一层[],否则会变成二维数组
      "allItems": listTest,
    },
    // 打开merge配置,只更新传入的字段,不会覆盖文档里其他未传的字段
    SetOptions(merge: true),
  );
}

4. 其他必改的小问题

  • AddObjectItemToArray方法不要包在setState里调用,setState只用来做同步的页面状态更新,异步Firestore操作直接触发即可,stream会自动推送新数据刷新页面,不需要手动调用setState
  • 删掉MyBody外层套的SingleChildScrollView,ListView本身自带滚动能力,两层滚动组件嵌套会出现滚动冲突、滑动卡顿问题
  • 不要把组件构建逻辑写在State类里当普通方法传context,抽成独立的StatelessWidget能大幅减少不必要的组件重建,提升页面性能
性能优化方案

如果只是更新数组内单个元素的属性,不需要每次把整个数组读出来改完再全量覆盖,Firestore支持直接通过数组下标更新指定字段,连临时列表都不需要创建,性能更高也不会出现数据结构写错、累加的问题:

// 直接更新指定下标的字段,无需拉取全量数组
FirebaseFirestore.instance
  .collection('Users')
  .doc(FirebaseAuth.instance.currentUser.uid)
  .collection('lists')
  .doc(widget.docID)
  .update({"allItems.$index.itemChecked": checked == 'Yes' ? 'No' : 'Yes'});

内容的提问来源于stack exchange,提问作者Laurent Thomas

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 12:54:16