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

Flutter中BlocBuilder无法接收Bloc状态的问题排查

问题描述

我有一个需要展示列表的页面,通过API获取数据并在加载阶段显示加载器。当前初始状态可正常触发加载器显示,但从Bloc发出的其他状态无法传递至页面,BlocBuilder仅响应默认的初始状态。

相关代码

页面代码

class _WeatherInfoPageState extends State<WeatherInfoPage> {
  late WeatherInfoBloc _weatherInfoBloc;

  @override
  void initState() {
    super.initState();
    _weatherInfoBloc = locator<WeatherInfoBloc>();
    _weatherInfoBloc.add(WeatherInfoFetchEvent());
  }

  @override
  Widget build(BuildContext context) {
    return BlocBuilder<WeatherInfoBloc, WeatherInfoState>(
        builder: (context, state) {
          if (state is WeatherInfoInitial || state is WeatherInfoFetchingState) {
            return _buildLoading();
          } else if (state is WeatherInfoFetchingSuccessState) {
            return _buildWeatherForcastView(context, state.response);
          }
          return Container();
        }
    );
  }

  Widget _buildWeatherForcastView(BuildContext context, WeatherInfo model) {
    return Container();
  }

  Widget _buildLoading() => const Center(child: CircularProgressIndicator());
}

Bloc代码

WeatherInfoBloc(this._weatherInfoUseCase) : super(WeatherInfoInitial()) {
on<WeatherInfoFetchEvent>((event, emit) async {
  emit(WeatherInfoFetchingState());
  final response = await _weatherInfoUseCase.getWeatherInfo(
      "52.521992", "13.413244");
  response.fold(
          (failure) =>
      {
        emit(WeatherInfoFetchingFailureState(failure.message))
      },
          (success) =>
      {
        emit(WeatherInfoFetchingSuccessState(success))
      });
});
}

主页面代码

Widget build(BuildContext context) {
return BlocProvider<WeatherInfoBloc>(
    create: (context) => locator<WeatherInfoBloc>(),
    child: MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'Flutter Demo',
      themeMode: ThemeMode.system,
      theme: AppTheme.lightTheme,
      darkTheme: AppTheme.darkTheme,
      home: const Scaffold(
        backgroundColor: Colors.white,
        body: SafeArea(
          child: WeatherInfoPage(),
        ),
      ),
    ));
}

问题原因

核心问题是你在页面中通过locator获取的Bloc实例,和BlocProvider提供给Widget树的实例不是同一个:

  • 如果locator中WeatherInfoBloc是按factory模式注册(每次调用locator<WeatherInfoBloc>()都会生成新实例),那么主页面BlocProvider创建的是一个实例,页面initState里拿到的是另一个独立实例。
  • 你给后者发送了WeatherInfoFetchEvent,但BlocBuilder监听的是前者的状态变化,自然无法接收到后续状态更新。

修复方案

方案1:通过Context获取Bloc实例(推荐)

不要直接用locator获取Bloc,而是从Widget树的Context中获取,确保事件发送和状态监听的是同一个实例:

修改后的页面代码:

class _WeatherInfoPageState extends State<WeatherInfoPage> {
  @override
  void initState() {
    super.initState();
    // 等待Widget挂载到树后,从Context获取Bloc并发送事件
    WidgetsBinding.instance.addPostFrameCallback((_) {
      context.read<WeatherInfoBloc>().add(WeatherInfoFetchEvent());
    });
  }

  @override
  Widget build(BuildContext context) {
    return BlocBuilder<WeatherInfoBloc, WeatherInfoState>(
        builder: (context, state) {
          if (state is WeatherInfoInitial || state is WeatherInfoFetchingState) {
            return _buildLoading();
          } else if (state is WeatherInfoFetchingSuccessState) {
            return _buildWeatherForcastView(context, state.response);
          } else if (state is WeatherInfoFetchingFailureState) {
            // 增加失败状态的UI展示
            return Center(child: Text(state.message));
          }
          return Container();
        }
    );
  }

  Widget _buildWeatherForcastView(BuildContext context, WeatherInfo model) {
    return Container();
  }

  Widget _buildLoading() => const Center(child: CircularProgressIndicator());
}

方案2:将Bloc注册为单例(不推荐,耦合性高)

如果一定要用locator,需确保每次获取的是同一个实例。以GetIt为例,修改注册逻辑:

// 原factory模式注册(每次生成新实例)
// locator.registerFactory(() => WeatherInfoBloc(locator()));

// 修改为单例模式
locator.registerSingleton<WeatherInfoBloc>(WeatherInfoBloc(locator()));

这种方式虽能解决实例不一致问题,但会增加代码耦合,不符合Bloc的最佳实践。

额外优化

  • 增加WeatherInfoFetchingFailureState的UI处理,避免请求失败时页面显示空白容器。
  • 可以用BlocProvider.of<WeatherInfoBloc>(context)替代context.read,效果一致。

内容的提问来源于stack exchange,提问作者mars_dev

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 09:27:42