【Flutter错误锦囊】
在Flutter开发中遇到的异常ConcurrentModificationException,是发生在使用List集合数据时的情况,因操作不合理而抛出的异常,通常异常日志如下:
E/flutter (15758): [ERROR:flutter/lib/ui/ui_dart_state.cc(157)] Unhandled Exception: Concurrent modification during iteration: Instance(length:3) of '_GrowableList'.
E/flutter (15758): #0 ListIterator.moveNext (dart:_internal/iterable.dart:340:7)
E/flutter (15758): #1 SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:1012:44)
E/flutter (15758): #2 SchedulerBinding._handleDrawFrame (package:flutter/src/scheduler/binding.dart:929:5)
E/flutter (15758): #3 _rootRun (dart:async/zone.dart:1184:13)
E/flutter (15758): #4 _CustomZone.run (dart:async/zone.dart:1077:19)
E/flutter (15758): #5 _CustomZone.runGuarded (dart:async/zone.dart:979:7)
E/flutter (15758): #6 _invoke (dart:ui/hooks.dart:261:10)
E/flutter (15758): #7 _drawFrame (dart:ui/hooks.dart:219:3)
1 情景一 遍历list集合时删除元素出现的异常
代码如下:
List list = [...];
for (int i = 0; i < list.length; i++) {
if(...){
list.remove(...)
}
}
解决方案一 是新创建一个集合然后将符合条件的数据保存到新的集合中,然后遍历结束后使用新的集合数据,代码如下:
///原数据集合
List list = [...];
///临时的空集合
List themList =[];
///清空临时集合
themList.clear();
for (int i = 0; i < list.length; i++) {
///将符合条件的数据添加到临时集合中
if(...){
themList.add();
}
}
///重新赋值
list = themList;
解决方案二 :使用Dar提供的循环并删除的方法
for (int i = 0; i < list.length; i++) {
///将符合条件的数据添加到临时集合中
if(...){
// 使用箭头函数,后面的表达式为true时会删除当前值
list.removeWhere((value) => value == 2);
// 或者使用
list.removeWhere((value) {
retrun value == 2;
});
}
}
2 情景二 使用 WidgetsBinding添加addPersistentFrameCallback出现的Concurrent modification 异常
在Flutter开发中通常通过 WidgetsBinding 添加 一个页面实时绘制Frame的回调兼听,一般放在初始化 initState 函数中。
WidgetsBinding.instance.addPersistentFrameCallback((Duration timeStamp){
debugPrint(" 生命周期 实时 Frame 绘制回调"); // 每帧都回调
});
当兼听的页面中有初始化的List数据时,会出现此异常,解决方法是在页面的第一帧绘制完成后再添加这个兼听:
@override
void initState(){
super.initState();
WidgetsBinding.instance.addObserver(this);
///单次 Frame 绘制回调,通过 addPostFrameCallback 实现。
///它会在当前 Frame 绘制完成后进行回调,并只会回调一次,如果要再次监听则需要再设置一次。
WidgetsBinding.instance.addPostFrameCallback((Duration timeStamp){
debugPrint(" 生命周期 单次 Frame 绘制回调"); // 只回调一次
///在第一次绘制完成时再添加实时回调的监听
addPersistentFrameCallbackFunction();
});
}
///在页面的每帧绘制完成后添加的实时
void addPersistentFrameCallbackFunction(){
///实时 Frame 绘制回调,则通过 addPersistentFrameCallback 实现。
///这个函数会在每次绘制 Frame 结束后进行回调,可以用作 FPS 检测。
WidgetsBinding.instance.addPersistentFrameCallback((Duration timeStamp){
debugPrint(" 生命周期 实时 Frame 绘制回调"); // 每帧都回调
});
}