Bloc: Events from MultiBlocProvider are not sent

Created on 19 Apr 2020  路  5Comments  路  Source: felangel/bloc

@felangel Hi, I have added a new bloc SyncBloc to a MultiBlocProvider that creates all the blocs needed for MapScreen and for some it also adds Events needed to present data in the screen itself. The problem is that while location events (for LocationBloc) are added correctly from the MultiBlocProvider itself, sync events (for SyncBloc) are not. If I instead add them from MapScreen's MultiBlocListener as

BlocProvider.of<SyncBloc>(context).add(SyncLanguages());

they work as expected so looks like SyncBloc has been provided correctly.. Can you spot what I'm doing wrong with the newer SyncBloc or point me in the right direction? As always thank you very much for your time and help.

This is the MultiBlocProvider in main():

@override
  Widget build(BuildContext context) {
    return MaterialApp(
      localizationsDelegates: [
        const AppLocalizationsDelegate(),
        GlobalMaterialLocalizations.delegate,
        GlobalWidgetsLocalizations.delegate,
      ],
      supportedLocales: [
        const Locale('en', ''),
        const Locale('it', ''),
        const Locale('es', ''),
      ],
      localeResolutionCallback:
          (Locale locale, Iterable<Locale> supportedLocales) {
        for (Locale supportedLocale in supportedLocales) {
          if (supportedLocale.languageCode == locale.languageCode ||
              supportedLocale.countryCode == locale.countryCode) {
            return supportedLocale;
          }
        }
        return supportedLocales.first;
      },
      debugShowCheckedModeBanner: false,
      home: BlocBuilder<AuthenticationBloc, AuthenticationState>(
        builder: (context, state) {
          if (state is Unauthenticated) {
            return LoginScreen(userRepository: _userRepository);
          }
          if (state is Authenticated) {
            return MultiBlocProvider(
              providers: [
                BlocProvider<TrackingBloc>(
                  create: (context) => TrackingBloc(),
                ),
                BlocProvider<DirectionsBloc>(
                  create: (context) => DirectionsBloc(),
                ),
                BlocProvider<GeoBloc>(
                  create: (context) => GeoBloc(),
                ),
                BlocProvider<RouteBloc>(
                  create: (context) => RouteBloc(),
                ),
                BlocProvider<SchedulerBloc>(
                  create: (context) => SchedulerBloc(),
                ),
                BlocProvider<CheckerBloc>(
                  create: (context) => CheckerBloc(),
                ),
                BlocProvider<LocationBloc>(
                    create: (context) => LocationBloc(
                          mapRepository: _mapRepository,
                        )
                          ..add(GetLocationStream())
                          ..add(GetLocation())
                          ..add(GetIsoLocationUser())),
                BlocProvider<SyncBloc>(
                    create: (context) =>
                        SyncBloc()..add(SyncLanguages())..add(SyncIcons())),
                BlocProvider<AlertBloc>(create: (context) {
                  return AlertBloc(
                    alertRepository: _alertRepository,
                  );
                }),
              ],
              child: MapScreen(
//                mapRepository: _mapRepository,
                user: state.user,
//              alertRepository: FirebaseAlertRepository(),
              ),
            );
          }
          return SplashScreen();
        },
      ),
      navigatorObservers: [
        FirebaseAnalyticsObserver(analytics: analytics),
      ],
    );

SyncEvent:

abstract class SyncEvent {
  const SyncEvent();

  @override
  List<Object> get props => [];
}

class SyncLanguages extends SyncEvent {}

class SyncIcons extends SyncEvent {}

and SyncBloc:

class SyncBloc extends Bloc<SyncEvent, SyncState> {
  @override
  SyncState get initialState => InitialState();

  Stream<SyncState> mapEventToState(SyncEvent event) async* {
    if (event is SyncLanguages) {
      print('SyncLanguages received');
    }
    if (event is SyncIcons) {
      print('SyncIcons received');
    }
  }
}

I put together a sample app with the same structure of my app:

https://github.com/vinnytwice/bloc_test

BlocA is the equivalent of AuthenticationBloc and Home is the equivalent of my MapScreen.
As you can see none of the events in the second MultiblocProvider are sent, dough in my app location events are sent..
Hope you have all the needed info and code.
Thank you again.
Cheers

question

Most helpful comment

@felangel I see, thank you very much for the explanation and the fix. Still I don't understand then why in the first MultiBlocProvider events are sent even if BLocProvider lazy parameter for BLocA is not set to false, as also is in my app. Events from bloc in first MultiBlocProvider are sent while in the second MultiBLocProvider some events are sent and some are not, but none of them has a lazy parameter set to false.

All 5 comments

Hi @vinnytwice 馃憢
Thanks for opening an issue!

The issue is since BlocProvider uses Provider internally, the create method of BlocProvider is lazy by default (which means it's only called when the bloc is requested via BlocProvider.of). In this case, none of the blocs in your second MultiBlocProvider are used anywhere in the application so by default they are never created. You can set lazy to false to override this behavior.

I've opened a pull request with the fix to your sample app.

Hope that helps! 馃憤

@felangel I see, thank you very much for the explanation and the fix. Still I don't understand then why in the first MultiBlocProvider events are sent even if BLocProvider lazy parameter for BLocA is not set to false, as also is in my app. Events from bloc in first MultiBlocProvider are sent while in the second MultiBLocProvider some events are sent and some are not, but none of them has a lazy parameter set to false.

@vinnytwice thats because you have BlocBuilders etc which perform lookups for those blocs and triggers create to be called.

Right I see.. thank you for the explanations, I'm still a beginner ;)

No problem!

Was this page helpful?
0 / 5 - 0 ratings