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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 20:07:52