禁用嵌套Freezed对象copyWith致构建失败的解决问询
问题场景
当使用Freezed创建嵌套对象时,若给内层对象BlocScreenNavigator禁用copyWith,外层的RenewLeaseState自动生成的copyWith代码会尝试调用内层对象的copyWith相关类型和方法,导致构建失败。
代码示例:
@freezed class RenewLeaseState with _$RenewLeaseState { const factory RenewLeaseState({ @Default(BlocScreenNavigator()) BlocScreenNavigator navigator, }) = _RenewLeaseState; } @Freezed(copyWith: false) class BlocScreenNavigator with _$BlocScreenNavigator { const factory BlocScreenNavigator({ @Default(RenewLeaseScreen.init) RenewLeaseScreen screen, @Default(false) bool isPop, }) = _BlocScreenNavigator; }
报错信息
lib/renew_lease/cubit/renew_lease_cubit.freezed.dart:56:3: Error: Type '$BlocScreenNavigatorCopyWith' not found.
$BlocScreenNavigatorCopyWith<$Res> get navigator;
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
lib/renew_lease/cubit/renew_lease_cubit.freezed.dart:116:3: Error: Type '$BlocScreenNavigatorCopyWith' not found.
$BlocScreenNavigatorCopyWith<$Res> get navigator {
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
lib/renew_lease/cubit/renew_lease_cubit.freezed.dart:142:3: Error: Type '$BlocScreenNavigatorCopyWith' not found.
$BlocScreenNavigatorCopyWith<$Res> get navigator;
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
lib/renew_lease/cubit/renew_lease_cubit.freezed.dart:117:12: Error: The method '$BlocScreenNavigatorCopyWith' isn't defined for the class '_$RenewLeaseStateCopyWithImpl<$Res>'.
- '_$RenewLeaseStateCopyWithImpl' is from 'package:tenant_app/renew_lease/cubit/renew_lease_cubit.dart' ('lib/renew_lease/cubit/renew_lease_cubit.dart'). Try correcting the name to the name of an existing method, or defining a method named '$BlocScreenNavigatorCopyWith'.
return $BlocScreenNavigatorCopyWith<$Res>(_value.navigator, (value) {
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
FAILURE: Build failed with an exception.
原因分析
这是Freezed的已知未解决问题,外层类自动生成copyWith时,默认会为嵌套的Freezed对象生成链式调用的copyWith逻辑,但内层对象禁用copyWith后,相关的$XXXCopyWith类型和方法不会生成,导致外层代码找不到依赖,编译报错。
解决办法
方案1:手动实现外层类的copyWith方法
给外层类RenewLeaseState添加@Freezed(copyWith: false),然后手动实现需要的copyWith逻辑,避免自动生成的代码调用内层对象的copyWith:
@Freezed(copyWith: false) class RenewLeaseState with _$RenewLeaseState { const factory RenewLeaseState({ @Default(BlocScreenNavigator()) BlocScreenNavigator navigator, }) = _RenewLeaseState; // 手动实现copyWith,按需处理字段 RenewLeaseState copyWith({ BlocScreenNavigator? navigator, }) { return RenewLeaseState( navigator: navigator ?? this.navigator, ); } } @Freezed(copyWith: false) class BlocScreenNavigator with _$BlocScreenNavigator { const factory BlocScreenNavigator({ @Default(RenewLeaseScreen.init) RenewLeaseScreen screen, @Default(false) bool isPop, }) = _BlocScreenNavigator; }
方案2:将内层对象改为手动实现的不可变类
放弃用Freezed生成BlocScreenNavigator,手动编写不可变类并禁用copyWith,这样外层类生成copyWith时只会直接赋值对象,不会尝试调用其copyWith:
@freezed class RenewLeaseState with _$RenewLeaseState { const factory RenewLeaseState({ @Default(BlocScreenNavigator()) BlocScreenNavigator navigator, }) = _RenewLeaseState; } // 手动实现不可变类,不提供copyWith class BlocScreenNavigator { final RenewLeaseScreen screen; final bool isPop; const BlocScreenNavigator({ this.screen = RenewLeaseScreen.init, this.isPop = false, }); // 不实现copyWith,强制用户手动创建新对象 }
方案3:使用Freezed的@CustomCopyWith自定义逻辑
通过@CustomCopyWith注解,为外层类的navigator字段自定义copy处理逻辑,避免自动生成链式copyWith:
@freezed class RenewLeaseState with _$RenewLeaseState { const factory RenewLeaseState({ @Default(BlocScreenNavigator()) @CustomCopyWith( resolver: _navigatorCopyWithResolver, ) BlocScreenNavigator navigator, }) = _RenewLeaseState; } // 自定义copyWith解析器,直接返回传入的对象或原对象 T _navigatorCopyWithResolver<T>(T value, T? Function(T) updates) { return updates(value) ?? value; } @Freezed(copyWith: false) class BlocScreenNavigator with _$BlocScreenNavigator { const factory BlocScreenNavigator({ @Default(RenewLeaseScreen.init) RenewLeaseScreen screen, @Default(false) bool isPop, }) = _BlocScreenNavigator; }
内容的提问来源于stack exchange,提问作者genericUser

