You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 12:08:09