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

枚举类型BLoC事件管理及emit报错与BlocBuilder适配问题

问题解决方案

一、解决“emit was called after an event handler completed normally”错误及加载状态不更新

1. 错误原因

该错误源于Bloc事件处理函数中,异步操作未通过await等待完成,导致emit调用在事件处理函数执行结束后才触发,违反了flutter_bloc的状态更新规则。

2. 修复步骤

(1)正确处理异步事件(新版flutter_bloc写法)

确保事件处理回调用async修饰,并await所有异步操作(比如API请求):

class TmdbBloc extends Bloc<TmdbEvent, TmdbState> {
  final TmdbRepository tmdbRepository;

  TmdbBloc(this.tmdbRepository) : super(const TmdbState.loading()) {
    on<GetPopularMovies>((event, emit) async {
      emit(const TmdbState.loading());
      try {
        final movies = await tmdbRepository.getPopularMovies();
        emit(TmdbState.success(movies));
      } catch (e) {
        emit(TmdbState.error(e.toString()));
      }
    });
  }
}

(2)检查状态更新逻辑

界面持续显示CircularProgressIndicator说明状态一直停留在loading,需排查:

  • 确认页面初始化时已触发事件:
    在页面initState中添加事件触发代码:
    @override
    void initState() {
      super.initState();
      context.read<TmdbBloc>().add(const GetPopularMovies());
    }
    
  • 确保Bloc状态覆盖所有场景(结合freezed):
    用freezed定义完整状态,并在BlocBuilder中处理所有分支:
    // freezed状态定义示例
    @freezed
    class TmdbState with _$TmdbState {
      const factory TmdbState.initial() = _Initial;
      const factory TmdbState.loading() = _Loading;
      const factory TmdbState.success(List<Movie> movies) = _Success;
      const factory TmdbState.error(String message) = _Error;
    }
    
    // BlocBuilder中处理所有状态
    BlocBuilder<TmdbBloc, TmdbState>(
      builder: (context, state) {
        return state.when(
          initial: () => SizedBox.shrink(),
          loading: () => CircularProgressIndicator(),
          success: (movies) => ListView.builder(
            itemCount: movies.length,
            itemBuilder: (_, i) => Text(movies[i].title),
          ),
          error: (msg) => Text('出错了:$msg'),
        );
      },
    )
    

二、让BlocBuilder关联特定TmdbEvent(如GetPopularMovies)

BlocBuilder监听的是状态变化,而非直接关联事件,要关联特定事件的执行结果,可通过以下方式实现:

1. 为不同事件定义专属状态(结合freezed)

针对不同事件的结果,定义对应的状态类,明确区分事件触发的状态:

@freezed
class TmdbState with _$TmdbState {
  const factory TmdbState.loading() = _Loading;
  // 对应GetPopularMovies事件的成功状态
  const factory TmdbState.popularMoviesSuccess(List<Movie> movies) = _PopularMoviesSuccess;
  // 对应其他事件的成功状态
  const factory TmdbState.topRatedMoviesSuccess(List<Movie> movies) = _TopRatedMoviesSuccess;
  const factory TmdbState.error(String message) = _Error;
}

2. 在Bloc中处理事件并emit对应状态

class TmdbBloc extends Bloc<TmdbEvent, TmdbState> {
  final TmdbRepository repo;

  TmdbBloc(this.repo) : super(const TmdbState.loading()) {
    on<GetPopularMovies>((event, emit) async {
      emit(const TmdbState.loading());
      try {
        final movies = await repo.getPopularMovies();
        emit(TmdbState.popularMoviesSuccess(movies));
      } catch (e) {
        emit(TmdbState.error(e.toString()));
      }
    });

    on<GetTopRatedMovies>((event, emit) async {
      emit(const TmdbState.loading());
      try {
        final movies = await repo.getTopRatedMovies();
        emit(TmdbState.topRatedMoviesSuccess(movies));
      } catch (e) {
        emit(TmdbState.error(e.toString()));
      }
    });
  }
}

3. 在BlocBuilder中针对性渲染UI

方式一:直接匹配状态

BlocBuilder<TmdbBloc, TmdbState>(
  builder: (context, state) {
    return state.when(
      loading: () => CircularProgressIndicator(),
      popularMoviesSuccess: (movies) => PopularMoviesList(movies: movies),
      topRatedMoviesSuccess: (movies) => TopRatedMoviesList(movies: movies),
      error: (msg) => Text(msg),
    );
  },
)

方式二:用buildWhen过滤状态

如果只想让BlocBuilder响应GetPopularMovies事件的状态变化,可通过buildWhen限定:

BlocBuilder<TmdbBloc, TmdbState>(
  buildWhen: (prev, curr) {
    // 仅当当前状态是PopularMoviesSuccess时重新构建UI
    return curr is _PopularMoviesSuccess;
  },
  builder: (context, state) {
    if (state is _PopularMoviesSuccess) {
      return PopularMoviesList(movies: state.movies);
    }
    return CircularProgressIndicator();
  },
)

内容的提问来源于stack exchange,提问作者Ουιλιαμ Αρκευα

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 04:23:21