Flutter使用Cubit+Freezed时BlocSelector找不到对应Provider报错
问题根源&修复方案
直接报错原因
你代码中错误调用了context.read<HState>():
context.read<T>()要求祖先节点必须提供对应T类型的Provider,你仅在项目中注入了BlocProvider<HCubit>,没有注入HState类型的Provider,自然会抛出找不到对应Provider的异常。- 读取状态不需要调用
read方法,BlocSelector的builder参数已经返回了筛选后的状态;更新状态需要调用HCubit的实例方法,也不需要读取状态。
其他关联错误
1. BottomSheet上下文不在BlocProvider子树范围
你将BlocProvider<HCubit>放在了MaterialApp的child参数中,而Flutter的showModalBottomSheet/showBottomSheet默认将弹窗组件挂载在MaterialApp的路由上下文中,不在HomePage子树内,也会导致无法获取HCubit实例。
2. TextEditingController位置错误
你把TextEditingController直接定义在build方法中,每次组件rebuild都会重新创建控制器,已输入的内容会被清空。
3. 密封类状态互斥逻辑缺陷
当前的HState是三个独立的密封类变体,每次emit新状态会完全覆盖旧状态,比如你触发isClicked状态后再输入文本emitinputTextValue,isClicked的状态就会丢失。
修复步骤
第一步:修复TextInput组件的错误调用
// 建议改成StatefulWidget管理控制器,或从外部传入控制器 class TextInput extends StatelessWidget { final TextEditingController controller; const TextInput({super.key, required this.controller}); @override Widget build(BuildContext context) { return BlocSelector<HCubit, HState, String?>( selector: (state) { return state.maybeMap( orElse: () => null, inputTextValue: (val) => val.inputTextValue.input.fold((l) => l.msg, (r) => null), ); }, builder: (context, errorMsg) { return TextFormField( controller: controller, textAlign: TextAlign.start, onChanged: (String value) { // 读取HCubit实例调用更新方法 context.read<HCubit>().textInputNote(textNote: value); }, // 直接使用BlocSelector返回的错误信息,不需要额外读取状态 validator: (_) => errorMsg, ); }, ); } }
第二步:修复BlocProvider注入范围
二选一即可:
方案A:全局注入HCubit
修改AppWidget代码,将BlocProvider放到MaterialApp的父级:
class AppWidget extends StatelessWidget { @override Widget build(BuildContext context) { return BlocProvider<HCubit>( lazy: false, create: (context) => HCubit(), child: Sizer( builder: (context, orientation, deviceType) { return MaterialApp( home: const HomePage(), ); }, ), ); } }
方案B:打开BottomSheet时传递HCubit实例
如果不需要全局使用HCubit,在打开BottomSheet的代码中注入现有实例:
// 在HomePage中打开BottomSheet的位置调用 showModalBottomSheet( context: context, builder: (bottomSheetContext) { return BlocProvider.value( // 传递当前HomePage上下文中已有的HCubit实例 value: context.read<HCubit>(), child: 你的BottomSheet组件( child: TextInput(controller: yourController), ), ); }, );
第三步(可选优化):重构HState避免状态互斥
abstract class HState with _$HState { const factory HState({ required bool isClicked, required Note? inputTextValue, }) = _HState; factory HState.initial() => const HState( isClicked: false, inputTextValue: null, ); }
同步修改HCubit的方法,更新状态时保留其他字段的值:
class HCubit extends Cubit<HState> { HCubit() : super(const HState.initial()); void addNewNoteButton({required bool isClicked}) => emit(state.copyWith(isClicked: isClicked)); void textInputNote({required String textNote}) => emit(state.copyWith(inputTextValue: Note(textInput: textNote))); }
内容的提问来源于stack exchange,提问作者Mariam Albarghouti
相关产品推荐
相关产品推荐

