skip to Main Content

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.

3

Answers


  1. You can create a method of creating BlocProvider in the screen itself, then you can use that method for navigating and creating providers for you.

    Here’s an example:

    class MyWidget extends StatelessWidget {
      const MyWidget({super.key});
    
       static Widget create() {
        return MultiBlocProvider(
          providers: [
            BlocProvider<SignInBloc>(
              create: (BuildContext context) => SignInBloc(),
            ),
          ],
          child: const SignInScreen(),
        );
      }
    
      @override
      Widget build(BuildContext context) {
        return Container();
      }
    }
    
    Login or Signup to reply.
  2. you should use BlocProvider.value when you push a new route:

    /// declare the bloc
    final _screenABloc = ScreenABloc();
    

    then use it in both ScreenA and ScreenB

    case PageNames.screenA:
      return PageTransition( // Some class that navigates
        duration: const Duration(milliseconds: 400),
        child: BlocProvider.value(
          value: _screenABloc,
          child: const ScreenAPage(),
        ),
      );
    
    case PageNames.screenB:
      return PageTransition( // Some class that navigates
        duration: const Duration(milliseconds: 400),
        child: BlocProvider.value(
          value: _screenABloc,
          child: const ScreenBPage(),
        ),
      );
    
    Login or Signup to reply.
  3. 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);
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search