Flutter中GetStorage持久化List后List.contains方法失效问题求助
问题根源与解决方案
我一眼就看出你遇到的List.contains()失效问题,核心是Map作为引用类型的特性在搞鬼:
- 首次启动时,
guestList里的Map和你添加到fixGuestList的是同一个内存对象,引用地址完全一致,所以contains()能精准识别重复; - 但当你用GetStorage持久化后,数据会被序列化为JSON字符串存储,重启读取时会重新生成全新的Map对象——哪怕内容和原来一模一样,它们的内存引用地址已经不同了,而
List.contains()默认是按引用比较的,自然会判定“不存在”。
下面是具体的解决办法,分两种方案供你选择:
方案一:快速修复(基于现有Map结构)
不需要重构代码,只需要把默认的contains()替换为基于宾客唯一标识的自定义检查逻辑(假设你的宾客Map里有id这类唯一字段,根据实际业务调整):
修改addToLane方法
void addToLane({required int index, required BuildContext context}) { final selectedGuest = guestList![index]; // 自定义判断:通过唯一ID检查是否已添加 bool isAlreadyAdded = fixGuestList.any((guest) => guest != null && guest['id'] == selectedGuest['id'] ); if (isAlreadyAdded) { ScaffoldMessenger.of(context).showSnackBar(_snackBar); } else { final emptyIndex = _returnIndex(); if (emptyIndex != 3) { // 确保还有空位再添加 fixGuestList[emptyIndex] = selectedGuest; storageBox.write('FixGuestList', fixGuestList); } } update(); }
顺便修正初始化的小bug
你的onInitAssign方法里,当_boxValue为null时,定义了一个局部的fixGuestList,并没有赋值给类成员变量,修正后:
void onInitAssign() async { final _boxValue = storageBox.read('FixGuestList'); if (_boxValue == null) { // 直接初始化类成员变量,不要定义局部变量 fixGuestList = List.filled(3, null, growable: false); } else { fixGuestList = _boxValue; } update(); }
方案二:规范重构(使用实体类替代Map)
从长远来看,把松散的Map封装成实体类,能彻底避免这类引用问题,代码可读性也更强:
1. 创建宾客实体类
class Guest { final String id; final String name; // 根据你的业务添加其他字段 Guest({required this.id, required this.name}); // 从Map转实体 factory Guest.fromMap(Map<String, dynamic> map) { return Guest( id: map['id'], name: map['name'], ); } // 实体转Map(用于持久化) Map<String, dynamic> toMap() { return { 'id': id, 'name': name, }; } // 重写==运算符,按内容比较而非引用 @override bool operator ==(Object other) => identical(this, other) || other is Guest && runtimeType == other.runtimeType && id == other.id; // 配合==重写hashCode @override int get hashCode => id.hashCode; }
2. 修改控制器逻辑
class GuestController extends GetxController { final _userDataController = Get.find<UserDataController>(); List<Guest?> fixGuestList = List.filled(3, null, growable: false); List<Guest>? guestList; @override void onInit() { // 监听API数据,自动转为Guest实体列表 ever(_userDataController.userDataModel, (model) { guestList = model.totalGuest?.map((map) => Guest.fromMap(map)).toList(); }); onInitAssign(); super.onInit(); } void onInitAssign() async { final _boxValue = storageBox.read('FixGuestList'); if (_boxValue == null) { fixGuestList = List.filled(3, null, growable: false); } else { // 把存储的Map列表转为Guest实体列表 fixGuestList = _boxValue.map((map) => map == null ? null : Guest.fromMap(map) ).toList().cast<Guest?>(); } update(); } void addToLane({required int index, required BuildContext context}) { final selectedGuest = guestList![index]; // 现在可以直接用contains(),因为重写了比较逻辑 if (fixGuestList.contains(selectedGuest)) { ScaffoldMessenger.of(context).showSnackBar(_snackBar); } else { final emptyIndex = _returnIndex(); if (emptyIndex != 3) { fixGuestList[emptyIndex] = selectedGuest; // 把Guest实体转为Map再存储 storageBox.write('FixGuestList', fixGuestList.map((guest) => guest?.toMap() ).toList()); } } update(); } // 其他方法保持不变 }
这个方案下,不管是首次启动还是从持久化读取,contains()都能正确识别相同内容的宾客,因为我们重写了对象的比较逻辑。
内容的提问来源于stack exchange,提问作者Shakun's
相关产品推荐
相关产品推荐

