使用MultiBlocProvider时无法调用Bloc事件的问题
Flutter中MultiBlocProvider管理Bloc时GeolocationBloc事件未执行的问题
我在Flutter里用MultiBlocProvider管理GeolocationBloc和WeatherInfoBloc,逻辑是先通过GeolocationBloc获取位置,再用该位置调用WeatherInfoBloc获取天气详情。但实际运行时,GeolocationBloc的LoadGeolocationEvent根本没执行,调试一直停在默认的GeolocationLoading状态。
主文件代码
Widget build(BuildContext context) { return MultiBlocProvider( providers: [ BlocProvider<GeolocationBloc>(create: (context) => locator<GeolocationBloc>()), 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(), ), ), ), ); }
WeatherInfoPage代码
class WeatherInfoPage extends StatefulWidget { const WeatherInfoPage({super.key}); @override State<WeatherInfoPage> createState() => _WeatherInfoPageState(); } class _WeatherInfoPageState extends State<WeatherInfoPage> { late WeatherInfoBloc _weatherInfoBloc; late GeolocationBloc _geolocationBloc; @override void initState() { super.initState(); _weatherInfoBloc = locator<WeatherInfoBloc>(); _geolocationBloc = locator<GeolocationBloc>()..add(LoadGeolocationEvent()); } @override Widget build(BuildContext context) { return BlocBuilder<GeolocationBloc, GeolocationState>( // bloc: _geolocationBloc, builder: (context, state) { if (state is GeolocationLoading) { return const Center( child: CircularProgressIndicator(),//调试卡在这里 ); } else if (state is GeolocationLoaded) { _weatherInfoBloc.add(WeatherInfoFetchEvent()); return WeatherDisplayPage(); } return Container(); }, ); } } class WeatherDisplayPage extends StatefulWidget { const WeatherDisplayPage({super.key}); @override State<WeatherDisplayPage> createState() => _WeatherDisplayPageState(); } class _WeatherDisplayPageState extends State<WeatherDisplayPage> { @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.weatherInfoList); } return Container(); } ); } Widget _buildWeatherForcastView(BuildContext context, List<WeatherInfoViewModel> weatherInfoList) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Container(alignment: Alignment.center, child: Text(weatherInfoList.first.day ?? "")), Text(weatherInfoList.first.weather ?? ""), Image.asset(weatherInfoList.first.titleImage), Container(alignment: Alignment.center, child: Text(weatherInfoList.first.temp ?? "")), Text(weatherInfoList.first.pressure.toString()), Text(weatherInfoList.first.wind.toString()), SizedBox( height: 100, child: ListView.builder( scrollDirection: Axis.horizontal, itemBuilder: (context, index){ return ForecastTile( weatherInfoViewModel: weatherInfoList[index], ); }, itemCount: weatherInfoList.length, ) ) ], ); } Widget _buildLoading() => const Center(child: CircularProgressIndicator()); }
依赖注入容器代码
final locator = GetIt.instance; Future<void> initializeDependencies() async { locator.registerFactory(() => WeatherInfoBloc(locator())); locator.registerLazySingleton<WeatherInfoUseCase>(() => WeatherInfoUseCase(weatherInfoRepository: locator())); locator.registerLazySingleton<WeatherInfoRepository>(() => WeatherInfoRepositoryImpl(weatherInfoRemoteDataSource: locator())); locator.registerLazySingleton<WeatherInfoRemoteDataSource>(() => WeatherInfoRemoteDataSourceImpl(client: locator())); locator.registerLazySingleton<BaseClient>(() => BaseClient()); locator.registerLazySingleton(() => http.Client()); locator.registerFactory(() => GeolocationBloc(locator())); locator.registerLazySingleton<GeolocationRepository>(() => GeolocationRepository()); }
问题原因与解决办法
核心问题:Bloc实例不匹配
你在MultiBlocProvider中通过locator<GeolocationBloc>()创建了一个Bloc实例并注入到上下文,但在initState里又通过locator<GeolocationBloc>()获取了另一个新实例(因为你用了registerFactory,每次调用都会生成新实例)。你给这个新实例添加了事件,但BlocBuilder监听的是上下文里的那个实例,所以永远看不到状态变化,一直停留在初始的GeolocationLoading。
解决步骤:
- 统一Bloc实例来源:不要直接从locator获取Bloc,而是通过上下文获取MultiBlocProvider注入的实例。修改
WeatherInfoPage的initState:
@override void initState() { super.initState(); WidgetsBinding.instance.addPostFrameCallback((_) { _weatherInfoBloc = context.read<WeatherInfoBloc>(); _geolocationBloc = context.read<GeolocationBloc>()..add(LoadGeolocationEvent()); }); }
- 验证Bloc事件处理逻辑:检查
GeolocationBloc的事件处理方法是否正确响应LoadGeolocationEvent,确保完成后发出GeolocationLoaded状态。示例:
class GeolocationBloc extends Bloc<GeolocationEvent, GeolocationState> { final GeolocationRepository repository; GeolocationBloc(this.repository) : super(GeolocationLoading()) { on<LoadGeolocationEvent>((event, emit) async { try { final location = await repository.getLocation(); emit(GeolocationLoaded(location)); } catch (e) { emit(GeolocationError(e.toString())); } }); } }
- 避免重复触发天气事件:当前代码在
BlocBuilder的GeolocationLoaded分支中每次build都会添加事件,导致重复请求。改用BlocListener监听状态变化:
@override Widget build(BuildContext context) { return BlocListener<GeolocationBloc, GeolocationState>( listener: (context, state) { if (state is GeolocationLoaded) { context.read<WeatherInfoBloc>().add(WeatherInfoFetchEvent()); } }, child: BlocBuilder<GeolocationBloc, GeolocationState>( builder: (context, state) { if (state is GeolocationLoading) { return const Center(child: CircularProgressIndicator()); } else if (state is GeolocationLoaded) { return WeatherDisplayPage(); } return Container(); }, ), ); }
内容的提问来源于stack exchange,提问作者mars_dev
相关产品推荐
相关产品推荐

