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

Flutter列表增删自定义对象时contains方法失效如何解决

问题根因

Dart中contains方法默认对自定义对象/集合类型判断的是内存引用相等,而非值相等:

  • 你每次调用{teacherId, subjectId}都会生成一个全新的Set对象,哪怕两个Set的内容完全一致,内存地址也不同,所以contains永远返回false
  • 如果你用的Teachers、Subjectt是自定义实体类,没有重写==运算符和hashCode的话,即使内容相同的类实例也会被判定为不同对象

可行解决方案

下面提供三种不同复杂度的实现方案,可根据自己的需求选择:

方案1:用组合唯一键(最简便,无需改实体类)

直接将教师和学科的唯一ID拼接为字符串存储,字符串的contains是值判断,不会有引用问题:

// 改列表类型为String,存储格式示例:["1_2", "4_9"]
List<String> selectedList = [];

void addTeacher(Teachers teacher, Subjectt subject) {
  // 用两个实体的唯一ID拼接成唯一键,注意替换.id为你实际的主键字段名
  String uniqueKey = '${teacher.id}_${subject.id}';
  if (selectedList.contains(uniqueKey)) {
    selectedList.remove(uniqueKey);
    myColor = Colors.grey;
    print('removed teacher => ${teacher.toString()}');
  } else {
    selectedList.add(uniqueKey);
    myColor = AsasColors().blue;
    print('added teacher => ${teacher.toString()}');
  }
  notifyListeners();
  print(selectedList);
}

方案2:自定义数据类(最规范,适合后续扩展)

定义专门的存储类,重写==和hashCode,让contains按照内容判断:

// 第一步:定义存储实体类
class SelectedRelation {
  final Teachers teacher;
  final Subjectt subject;

  SelectedRelation({required this.teacher, required this.subject});

  // 重写==:只要教师ID和学科ID都相同就判定为同一个对象
  @override
  bool operator ==(Object other) =>
      identical(this, other) ||
      other is SelectedRelation &&
          runtimeType == other.runtimeType &&
          teacher.id == other.teacher.id &&
          subject.id == other.subject.id;

  // 重写hashCode,和==规则保持一致
  @override
  int get hashCode => Object.hash(teacher.id, subject.id);
}

// 第二步:修改列表和方法
List<SelectedRelation> selectedList = [];

void addTeacher(Teachers teacher, Subjectt subject) {
  final target = SelectedRelation(teacher: teacher, subject: subject);
  if (selectedList.contains(target)) {
    selectedList.remove(target);
    myColor = Colors.grey;
    print('removed teacher => ${teacher.toString()}');
  } else {
    selectedList.add(target);
    myColor = AsasColors().blue;
    print('added teacher => ${teacher.toString()}');
  }
  notifyListeners();
  print(selectedList);
}

如果项目中引入了equatable库,也可以直接继承Equatable省略手动重写的步骤。

方案3:手动遍历判断(无需修改现有存储结构)

如果你不想改动现有的Set存储逻辑,可以自己遍历列表匹配内容,绕过默认的contains判断:

List<dynamic> selectedList = [];

void addTeacher(Teachers teacher, Subjectt subject) {
  // 遍历查找内容匹配的Set
  final existIndex = selectedList.indexWhere((item) {
    if (item is! Set) return false;
    // 匹配Set内的教师和学科,替换.id为你实际的主键字段名
    final hasTeacher = item.any((e) => e is Teachers && e.id == teacher.id);
    final hasSubject = item.any((e) => e is Subjectt && e.id == subject.id);
    return hasTeacher && hasSubject;
  });

  if (existIndex != -1) {
    selectedList.removeAt(existIndex);
    myColor = Colors.grey;
    print('removed teacher => ${teacher.toString()}');
  } else {
    selectedList.add({teacher, subject});
    myColor = AsasColors().blue;
    print('added teacher => ${teacher.toString()}');
  }
  notifyListeners();
  print(selectedList);
}

内容的提问来源于stack exchange,提问作者Faris Shomali

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.01 23:09:02