Flutter Bloc:如何正确调用Event?
正确处理Flutter Bloc中Event的调用问题
问题场景
我有一个结构复杂的Flutter应用,顶层AppWrapper已经通过BlocProvider提供了EventsBloc实例,并在初始化时触发了LoadEventsEvent:
return BlocProvider( create: (context) => EventsBloc( RepositoryProvider.of<EventRepository>(context), RepositoryProvider.of<SocketRepository>(context), )..add(LoadEventsEvent()), child: RootScreen() ...
现在需要在第二层的RootScreen中,当NavigationCubit的状态切换到FMNavigation.home时,再次触发LoadEventsEvent,但之前的两种实现方式都存在问题:
- 方案1:重复创建
BlocProvider提供EventsBloc,导致多个实例,违背了BlocProvider作为依赖注入组件提供单一实例的设计意图:
BlocBuilder<DifferentBloc, DifferentBlocState>( builder: (context, state) { if (state.navbarItem == FMNavigation.home) { return BlocProvider( create: (context) => EventsBloc( RepositoryProvider.of<EventRepository>(context), RepositoryProvider.of<SocketRepository>(context), )..add(LoadEventsEvent()), child: HomeScreen(), ); } else if (state.navbarItem == FMNavigation.chat) { // ... } } )
- 方案2:在
build方法中直接用context.read()触发事件,违反了Bloc的使用规范(context.read()不能在build内调用,即使仅用于事件):
BlocBuilder<NavigationCubit, NavigationState>( builder: (context, state) { if (state.navbarItem == FMNavigation.home) { context.read<EventsBloc>().add(LoadEventsEvent()); return HomeScreen(); } else if (state.navbarItem == FMNavigation.chat) { // ... } } )
正确实现方案
方法1:用BlocListener监听Navigation状态变化
利用BlocListener专门处理状态变化带来的副作用(比如触发其他Bloc事件),它的listener回调不会在build流程中执行,完全符合规范:
BlocListener<NavigationCubit, NavigationState>( listener: (context, state) { // 仅当导航切换到home时触发事件 if (state.navbarItem == FMNavigation.home) { context.read<EventsBloc>().add(LoadEventsEvent()); } }, child: BlocBuilder<NavigationCubit, NavigationState>( builder: (context, state) { if (state.navbarItem == FMNavigation.home) { return HomeScreen(); } else if (state.navbarItem == FMNavigation.chat) { return ChatScreen(); } return SizedBox(); }, ), )
方法2:在NavigationCubit中直接触发事件(需权衡耦合度)
如果架构允许Cubit之间存在依赖,可以在NavigationCubit中注入EventsBloc,当切换到home状态时直接触发事件。这种方式会增加组件间的耦合,适合业务逻辑紧密关联的场景:
class NavigationCubit extends Cubit<NavigationState> { final EventsBloc _eventsBloc; NavigationCubit(this._eventsBloc) : super(NavigationState(navbarItem: FMNavigation.home)); void navigateTo(FMNavigation item) { emit(NavigationState(navbarItem: item)); if (item == FMNavigation.home) { _eventsBloc.add(LoadEventsEvent()); } } }
在AppWrapper提供NavigationCubit时传入已有的EventsBloc实例:
BlocProvider( create: (context) => NavigationCubit(context.read<EventsBloc>()), child: RootScreen(), )
补充疑问解答
你提到的以下写法完全可行:
child: BlocListener<BlocA, BlocAState>( listener: (context, state) { if (state is LiveeventFinishedState) { print('LiveeventFinishedState'); BlocProvider.of<BlocB>(context).add(LoadJoinableEvent()); BlocProvider.of<BlocC>(context).add(LoadEventsEvent()); } }, // ... )
BlocListener的核心作用就是响应状态变化执行副作用操作,触发其他Bloc的事件是典型的使用场景。只要BlocB和BlocC已经在当前上下文的上层通过BlocProvider提供了实例,就可以正常获取并触发事件。另外,在listener中推荐使用context.read<BlocB>()替代BlocProvider.of<BlocB>(context),写法更简洁且语义明确(仅获取实例,不监听状态)。
内容的提问来源于stack exchange,提问作者Marcel Dz
相关产品推荐
相关产品推荐

