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
相关产品推荐
相关产品推荐

