Flutter BLoC问题:emit触发后Listener未生效排查
问题描述
调试时已确认emit(DataLoadedState(result));执行,但对应的BlocListener未触发响应。在SavedSearchWidget的initState中添加LoadSavedSearchesEvent时,Listener工作正常;但应用运行期间触发ShowLandingState后,再次向同一个SavedSearchesBloc添加该事件,emit执行后Listener无任何反应。
已知连续发送相同State时Listener不会触发,但未重写DataLoadedState的hashCode或equals方法,理论上Bloc应判定两次State为不同实例。
相关代码
Widget代码
class SavedSearchWidget extends StatefulWidget { final SavedSearchesBloc bloc; final SearchScreenBloc searchScreenBloc; const SavedSearchWidget({ super.key, required this.bloc, required this.searchScreenBloc, }); @override State<SavedSearchWidget> createState() => _SavedSearchWidgetState(); } class _SavedSearchWidgetState extends State<SavedSearchWidget> { List<SavedSearchPresentation>? savedSearches; @override void initState() { widget.bloc.add(LoadSavedSearchesEvent()); super.initState(); } @override Widget build(BuildContext context) { return MultiBlocListener( listeners: [ BlocListener( bloc: widget.searchScreenBloc, listener: (context, state) { if (state is ShowLandingState) { widget.bloc.add(LoadSavedSearchesEvent()); } }, ), BlocListener( bloc: widget.bloc, listener: (context, state) { if (state is DataLoadedState) { savedSearches = state.savedSearches; } }, ), ], child: BlocBuilder( bloc: widget.bloc, builder: (context, state) { // 绘制UI }, ), ); } }
Bloc代码
abstract class SavedSearchesState {} class InitState extends SavedSearchesState {} class DataLoadedState extends SavedSearchesState { SavedSearchesResponse savedSearches; DataLoadedState(this.savedSearches); } class SavedSearchesBloc extends Bloc<SavedSearchesEvent, SavedSearchesState> { final ISavedSearchRepository _repository; SavedSearchesBloc(this._repository) : super(InitState()) { on<LoadSavedSearchesEvent>(onLoadSavedSearchesEvent); } Future<void> onLoadSavedSearchesEvent( LoadSavedSearchesEvent event, Emitter<SavedSearchesState> emit, ) async { final result = await _repository.loadAll(); emit(DataLoadedState(result)); } }
解决方案
1. 检查Widget存活状态
触发ShowLandingState时,SavedSearchWidget可能已被移出Widget树(比如页面跳转后被dispose),此时BlocListener已取消订阅,自然不会响应状态更新。可以在触发事件前判断Widget是否仍挂载:
BlocListener( bloc: widget.searchScreenBloc, listener: (context, state) { if (state is ShowLandingState && mounted) { widget.bloc.add(LoadSavedSearchesEvent()); } }, ),
2. 强制State实例判定为不同
若两次_repository.loadAll()返回的SavedSearchesResponse是同一个实例,可能导致Bloc误判状态未变化。可以给DataLoadedState添加唯一标识,确保每次状态都是新实例:
class DataLoadedState extends SavedSearchesState { final SavedSearchesResponse savedSearches; final DateTime timestamp; // 添加时间戳作为唯一标识 DataLoadedState(this.savedSearches) : timestamp = DateTime.now(); }
3. 修复UI更新逻辑(额外提示)
即使Listener触发,当前代码中更新savedSearches后未调用setState(),UI不会同步刷新。若需更新UI,应在Listener中补充:
BlocListener( bloc: widget.bloc, listener: (context, state) { if (state is DataLoadedState) { setState(() { savedSearches = state.savedSearches; }); } }, ),
内容的提问来源于stack exchange,提问作者anber
相关产品推荐
相关产品推荐

