Flutter SQLite报错Unsupported operation: read-only,如何在FutureBuilder中移除列表项
解决Flutter SQLite组队功能中的"Unsupported operation: read-only"错误
问题根源
从SQLite查询返回的列表默认是不可变的(框架会用List.unmodifiable包装查询结果),直接调用removeWhere这类修改方法自然会触发"read-only"错误。另外你直接在FutureBuilder的builder回调里给allplayer赋值,每次setState都会重新执行builder,之前对列表的修改会被覆盖,还会引发状态不一致。
修复步骤
- 将查询结果转为可变列表:拿到
snapshot.data后,用List.from()或toList()转成可修改的列表 - 把
allplayer设为状态变量:在组件的initState或第一次拿到数据时初始化,避免每次builder重新赋值 - 优化组队逻辑:避免直接操作原列表时的索引越界,同步更新选择状态数组
checks
修改后的完整代码示例
// 组件类中声明状态变量 late List<Map<String, dynamic>> allplayer; late List<bool> checks; List<String> team1 = []; List<String> team2 = []; bool enable = true; String teamname = 'team 1'; final Future readdata; // 你的SQLite查询Future bool _initialized = false; // 标记是否完成初始化 @override void initState() { super.initState(); } // FutureBuilder部分 FutureBuilder( future: readdata, builder: (BuildContext context, AsyncSnapshot snapshot) { if (snapshot.hasData) { // 仅第一次拿到数据时初始化可变列表和选择状态 if (!_initialized) { allplayer = List.from(snapshot.data); // 转为可变列表 checks = List.filled(allplayer.length, false); _initialized = true; } return ListView.builder( scrollDirection: Axis.vertical, shrinkWrap: true, itemCount: allplayer.length, itemBuilder: (BuildContext context, int i) { return enable ? Card( child: CheckboxListTile( title: Text(allplayer[i]['name']), value: checks[i], onChanged: (value) { setState(() { if (value == true) { team1.add(allplayer[i]['name']); if (team1.length >= 2) { final removeNames = [...team1]; // 从可变列表移除选中玩家 allplayer.removeWhere((element) => removeNames.contains(element['name'])); // 同步更新选择状态数组 checks = checks.where((_, idx) => !removeNames.contains(allplayer[idx]['name'])).toList(); teamname = 'team 2'; enable = false; team1.clear(); } } else if (value == false) { team1.removeWhere((element) => element == allplayer[i]['name']); } checks[i] = value!; }); }, ), ) : Card( child: CheckboxListTile( title: Text(allplayer[i]['name']), value: checks[i], onChanged: (value) { setState(() { if (value == true) { team2.add(allplayer[i]['name']); } else if (value == false) { team2.removeWhere((element) => element == allplayer[i]['name']); } checks[i] = value!; }); }, ), ); }, ); } return const Center(child: CircularProgressIndicator()); }, )
额外说明
- 新增
_initialized状态变量,确保allplayer和checks只在第一次拿到数据时初始化,避免每次setState被重置 - 用
List.from(snapshot.data)将不可变列表转为可变列表,解决只读错误 - 移除玩家时同步更新
checks数组,避免ListView的索引和选择状态不匹配 - 凑够2人后清空team1,防止后续重复处理
内容的提问来源于stack exchange,提问作者Yous y
相关产品推荐
相关产品推荐

