Search code examples
flutterdartbloc

How to set correctly the BlocProvider between two different routes (screens)?


I am using Bloc for my application, however I was doing something wrong and that is, providing all BlocProvider creates in the MaterialApp and I would not like to follow that bad practice.

Let's suppose that when I navigate to ScreenA, we create the Bloc as follows :

      case PageNames.screenA:
        return PageTransition( // Some class that navigates
          duration: const Duration(milliseconds: 400),
          child: BlocProvider<ScreenABloc>(
            create: (context) => ScreenABloc(),
            child: const ScreenAPage(),
          ),
      );

Now inside ScreenA, I will do a navigation to ScreenB, and everything is fine, however inside ScreenB at the bottom of my widget tree I want to access the ScreenABloc again, but I can't assign a BlocProvider.value because I get :

ProviderNotFoundException (Error: Could not find the correct Provider<ScreenABloc> above this Welcome Widget

return BlocProvider.value(
      value: BlocProvider.of<ScreenABloc>(context),
      child: child ...
);

So I am not sure how to get the supplier that has already been created, or if I should re-create it or what to do in those cases.


Solution

  • Using all BlocProviders in the starting of the file is not always considered bad practice. as according to official docs

    By default, BlocProvider will create the bloc lazily, meaning create will get executed when the bloc is looked up via BlocProvider.of(context).


    • So now, What are the use cases to use providers globally?

      When you want to access the providers almost everywhere in the app.

    • When to use providers passing through routes ?

      When only some screens needs access to the bloc you can pass value using BlocProvider.value


    According to the official docs of BlocProvider in flutter_bloc,follow these steps to pass BlocProvider to the child screen.

    Creation of BlocA()

    BlocProvider(
      lazy: false,
      create: (BuildContext context) => BlocA(),
      child: ChildA(),
    );
    

    Passing the value of BlocA to ScreenA()

    Make sure this is called from inside the ChildA()

    BlocProvider.value(
      value: BlocProvider.of<BlocA>(context),
      child: ScreenA(),
    );
    

    Now either from ChildA or ScreenA retrieve value as:

    // with extensions
    context.read<BlocA>();
    
    // without extensions
    BlocProvider.of<BlocA>(context);