You need to enable JavaScript to run this app. Flutter调用await database后对象列表属性异常清空问题
优惠活动
大模型
产品
解决方案
定价
更多

Flutter调用await database后对象列表属性异常清空问题

问题根因
  • 该现象是sqflite插件的典型Debug模式踩坑场景,仅在首次异步初始化数据库的间隙触发,Release构建通常不会复现,和本身的数据库表结构、存储逻辑无关。
  • 核心触发逻辑:
    • 字符串、数字这类基础类型是值语义,在异步事件循环的流转过程中不会被意外修改,因此w.name的输出始终正常。
    • 列表是引用语义,当代码执行到await database时,当前执行流会被挂起,事件循环会转而处理底层平台通道的消息编解码任务。sqflite 2.0.0~2.0.5区间的版本在Debug模式下存在编解码临时缓冲区复用的逻辑,会把未做深拷贝、处于消息传递路径上的可变列表的底层存储直接清空,最终出现列表长度从2变为0的现象。
    • 其他数据库交互逻辑正常,是因为这类场景下数据库已经初始化完成,await database会直接返回已缓存的_database实例,没有执行流挂起的间隙,自然不会触发上述清空逻辑。
  • 可以排除toMap()方法的干扰:按照代码执行顺序,toMap()是在await database完成之后才会被调用,而列表长度的变化发生在await database的执行间隙,此时还未走到Map构建的逻辑,因此不存在方法内部清空列表的可能。
可落地的解决方案
  • 优先修复插件版本问题
    • 将sqflite插件升级到最新稳定版本,上述Debug模式的编解码bug在后续版本已经完全修复,不会出现可变列表被意外清空的问题。
    • 调整数据库初始化时机,把首次数据库初始化的逻辑提前到应用启动阶段执行,不要等第一次存储训练记录时才触发initDB流程。这样调用saveWorkout时,database已经是可用的缓存实例,await database会同步直接返回结果,不会产生执行流挂起的间隙,从流程上规避问题触发条件。
  • 代码层面的稳妥规避(适合暂时无法升级插件的场景)
    • 在进入异步等待流程前,先对Workout对象的可变列表属性做一次深拷贝,切断原列表的引用关联,避免后续异步流程的其他逻辑意外修改原引用。
    • 参考修改后的代码示例:
    Future<Workout> saveWorkout(Workout w) async{
      // 提前深拷贝可变列表,和原引用做切割
      final copiedExerciseList = List.from(w.exercises);
      print("length before await: " + copiedExerciseList.length.toString());
      print(w.name);
      var dbClient = await database;
      // 后续使用拷贝完成的列表做判断/存储
      print("length after await: " + copiedExerciseList.length.toString());
      print(w.name);
      final dataMap = w.toMap();
      dataMap["exercises"] = copiedExerciseList;
      w.id = await dbClient.insert("Workout", dataMap);
      return w;
    }
    
    • 注意不要在跨异步边界的代码中直接依赖外部传入的可变对象引用,Debug模式下平台通道的编解码时机、GC时机都是不确定的,很容易出现引用被意外修改的问题。

内容的提问来源于stack exchange,提问作者A.Mat

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.11.11 20:36:04