Flutter bloc how to call event the right way?

Issue

I have a quite complex app structure and im wondering how to handle calling my events the right way. Imagine my widget tree. On top of everything I have the following file AppWrapper with this

1. level file AppWrapper

...
return BlocProvider(
        create: (context) => EventsBloc(
              RepositoryProvider.of<EventRepository>(context),
              RepositoryProvider.of<SocketRepository>(context),
                )..add(LoadEventsEvent()), 
...
child: RootScreen()
    ...

It calls LoadEventsEvent() perfectly fine. Now in my RootScreen() im trying to load a the event again.

2. level file RootScreen

...
  BlocBuilder<DifferentBloc, DifferentBlocState>(
                    builder: (context, state) {
                  if (state.navbarItem == FMNavigation.home) {
                   
                    // How to load LoadEventsEvent() here the right way??
                      return BlocProvider(
                   create: (context) => EventsBloc(
                     RepositoryProvider.of<EventRepository>(context),
                     RepositoryProvider.of<SocketRepository>(context),
                  )..add(LoadEventsEvent()),
                   child: HomeScreen(),
               
 
                  } else if (state.navbarItem == FMNavigation.chat) {
...

Now the above code works fine, its loading LoadEventsEvent() like im expecting, but im using BlocProvider for EventsBloc two times now. From the docs it says:

Takes a Create function that is responsible for creating the Bloc or
Cubit and a child which will have access to the instance via
BlocProvider.of(context). It is used as a dependency injection (DI)
widget so that a single instance of a Bloc or Cubit can be provided to
multiple widgets within a subtree.

It seems like it’s not meant to be used multiple times for the same bloc even if it works in my special case. No my second idea was to refactor this code to:

...
BlocBuilder<NavigationCubit, NavigationState>(
                    builder: (context, state) {
                  if (state.navbarItem == FMNavigation.home) {
                    context.read<EventsBloc>().add(LoadEventsEvent());
                    return HomeScreen();
                  } else if (state.navbarItem == FMNavigation.chat) {
...

But reading about context.read() the docs say:

It will not make widget rebuild when the value changes and cannot be
called inside [StatelessWidget.build]/[State.build]. On the other
hand, it can be freely called outside of these methods.

If that is incompatible with your criteria, consider using
Provider.of(context, listen: false). It does the same thing, but
without these added restrictions (but unsafe).

DON’T call [read] inside build if the value is used only for events:

Well how can I get this work?

EDIT:

Is this also good to go?

child: BlocListener<BlocA, BlocAState>(
      listener: (context, state) {
        // TODO: implement listener
        if (state is LiveeventFinishedState) {
          print('LiveeventFinishedState');
          BlocProvider.of<BlocB>(context).add(LoadJoinableEvent());
          BlocProvider.of<BlocC>(context).add(LoadEventsEvent());
        }
      },

Solution

you don’t need to create a new bloc as your RootScreen seems to be a a child node of AppWrapper in the widget tree, Provider will provide data (in this case EventsBloc) to all of its children.

You can access the bloc data by using context.read() and add a new event as below:

  BlocBuilder<DifferentBloc, DifferentBlocState>(
                builder: (context, state) {
              if (state.navbarItem == FMNavigation.home) {
              
              // you can access the bloc just fine
              context.read<EventsBloc>().add(LoadEventsEvent());
              return HomeScreen(),
           

              } else if (state.navbarItem == FMNavigation.chat) {

Answered By – Lộc Trần

Answer Checked By – Pedro (FlutterFixes Volunteer)

Leave a Reply

Your email address will not be published. Required fields are marked *