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

Flutter Bloc状态类中如何实现真正不可修改的final列表字段?

实现Flutter Bloc状态类中真正不可变的Final列表

要解决状态类中列表可被修改的问题,核心是确保无论传入什么列表,状态类存储的都是不可变实例,同时兼容const构造和@CopyWith生成的方法,以下是几种可行方案:

方案1:拆分构造函数(兼容const默认状态)

通过私有const构造处理默认空列表,公开工厂构造将传入的列表强制转为不可变类型,同时手动实现copyWith确保新列表也被处理:

@immutable
@CopyWith()
class MyState extends Equatable {
  // 私有const构造,仅用于创建默认不可变状态
  const MyState._({
    required this.list1,
    required this.list2,
  });

  // 公开工厂构造,将传入的列表转为不可变实例
  factory MyState({
    List<Reservation> list1 = const [],
    List<Institution> list2 = const [],
  }) {
    return MyState._(
      list1: List.unmodifiable(list1),
      list2: List.unmodifiable(list2),
    );
  }

  // 预定义的const初始状态
  static const MyState initial = MyState._(
    list1: const [],
    list2: const [],
  );

  final List<Reservation> list1;
  final List<Institution> list2;

  @override
  List<Object?> get props => [list1, list2];

  // 手动实现copyWith,确保新传入的列表被转为不可变
  MyState copyWith({
    List<Reservation>? list1,
    List<Institution>? list2,
  }) {
    return MyState(
      list1: list1 ?? this.list1,
      list2: list2 ?? this.list2,
    );
  }
}

说明

  • 默认的initial状态是const,满足编译期不可变要求;
  • 工厂构造会把任何传入的列表(可变/不可变)转为List.unmodifiable,运行时尝试修改会直接抛出异常;
  • 手动实现copyWith复用工厂构造的逻辑,确保修改状态时新列表也会被处理。

方案2:使用不可变列表包装类(类型层面禁止修改)

借助collection包的UnmodifiableListView,从类型上限制列表无法被修改,同时拆分构造函数兼容const默认状态:

import 'package:collection/collection.dart';

@immutable
@CopyWith()
class MyState extends Equatable {
  const MyState._({
    required this.list1,
    required this.list2,
  });

  factory MyState({
    List<Reservation> list1 = const [],
    List<Institution> list2 = const [],
  }) {
    return MyState._(
      list1: UnmodifiableListView(list1),
      list2: UnmodifiableListView(list2),
    );
  }

  static const MyState initial = MyState._(
    list1: UnmodifiableListView(const []),
    list2: UnmodifiableListView(const []),
  );

  // 字段类型直接设为不可变包装类
  final UnmodifiableListView<Reservation> list1;
  final UnmodifiableListView<Institution> list2;

  @override
  List<Object?> get props => [list1, list2];

  MyState copyWith({
    List<Reservation>? list1,
    List<Institution>? list2,
  }) {
    return MyState(
      list1: list1 ?? this.list1.toList(),
      list2: list2 ?? this.list2.toList(),
    );
  }
}

说明

  • UnmodifiableListView没有add/remove等修改方法,从类型层面直接禁止修改操作;
  • 同样通过const私有构造创建默认状态,工厂构造处理传入的可变列表。

方案3:简化构造(放弃非默认值的const)

如果不需要非默认状态的const构造,可以直接在初始化列表中强制转换,同时手动实现copyWith:

@immutable
@CopyWith()
class MyState extends Equatable {
  // 非const构造,初始化时将传入列表转为不可变
  MyState({
    List<Reservation> list1 = const [],
    List<Institution> list2 = const [],
  })  : list1 = List.unmodifiable(list1),
        list2 = List.unmodifiable(list2);

  // 预定义初始状态(非const)
  static final MyState initial = MyState();

  final List<Reservation> list1;
  final List<Institution> list2;

  @override
  List<Object?> get props => [list1, list2];

  MyState copyWith({
    List<Reservation>? list1,
    List<Institution>? list2,
  }) {
    return MyState(
      list1: list1 ?? this.list1,
      list2: list2 ?? this.list2,
    );
  }
}

说明

  • 实现最简单,但所有状态实例都无法是const;
  • 适合对const状态没有硬性要求的场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 02:07:01