3

I'm using Flutter Bloc for a project.

This is the structure:

      MultiBlocProvider(
          providers: [
            BlocProvider<BlocA>(
              create: (context) {
                return BlocA()
                  ..add(FetchEvent());
              },
            ),
            BlocProvider<BlocC>(
              create: (context) {
                return BlocC()..add(FetchEvent());
              },
            ),
          ],
          child: IndexedStack(
            index: _pageNum,
            children: [
              ChildA(),
              ChildB(),
            ],
          ),
        )

Inside ChildB I have a Navigator.push() to a ChildC, where BlocC is being used (BlocConsumer), but I'm getting an error,

Flutter could not find the correct Provider above BlocConsumer<BlocC> Widget

Edit There is a button in ChildB that navigates to ChildC on pressed. Like,

TextButton( // this code exists inside a scaffold widget
   child: Text("Page C"),
   onPressed: () {
      Navigator.push(context, CupertinoPageRoute(builder: (context) => ChildC()));
   }
), 

This is the bloc consumer in ChildC

// Child C
 Scaffold(
      body: BlocConsumer<BlocC, CState>(
        builder: (context, state) {
          if(state is CSuccessState) {
            return _body();
          } else if (state is CLoadingState || state is CInitState) {
            return Center(child: CircularProgressIndicator());
          } else return Center(child: Text("Something went wrong"));
        },
        listener: (context, state) {},

      ),
    );

Edit 2

I found this answer, apparently, this is an issue when using Navigator.push. Still, if anyone knows how to solve it in my case, do let me know

Nithin Sai
  • 840
  • 1
  • 10
  • 23

2 Answers2

6

Provider is scoped. It means only the subtree of your provider (your child widgets) can access it. When you push a new page, it pushed it to the MaterialApp router (default root router of your app), and your widget tree looks like:

MaterialApp
 |- Provider
 |   |- TextButton (with onPressed: () => Navigator.push())
 |- ChildC

As you can see, your ChildC is not below the Provider(s).


1. Move your providers above your material app

A way to solve it is to move your providers above your MaterialApp:

Provider
 |- MaterialApp
 |   |- TextButton (with onPressed: () => Navigator.push())
 |   |- ChildC

2. Use nested routers

You can push your page to a nested router that is below your Provider:

MaterialApp
 |- Provider
 |   |- Router
 |   |  |- TextButton (with onPressed: () => Navigator.push())
 |   |  |- ChildC

3. Provide again your BlockC above ChildC :

TextButton( // this code exists inside a scaffold widget
   child: Text("Page C"),
   onPressed: () {
      final blockCValue = context.read<BlockC>();
      Navigator.push(
        context,
        CupertinoPageRoute(builder: (context) => Provider<BlockC>.value(
          value: blockCValue,
          child: ChildC(),
        ),
      );
   },
), 
Valentin Vignal
  • 6,151
  • 2
  • 33
  • 73
  • How can you send your provider to the other screen without doing it global ? – Daniel Roldán Jan 26 '23 at 15:35
  • I'm not sure I understand your question. Could you try to specify? I believe this is what my point `3.` illustrates – Valentin Vignal Jan 26 '23 at 15:44
  • For example, you have ScreenA and ScreenB, i want to use in both the same Provider Bloc, but instead of creating it globally, i create it in the ScreenA, so, i push to the ScreenB, how can re-provide it now in the ScreenB ? Cause i have a problem here :https://stackoverflow.com/questions/75247028/unhandled-exception-bad-state-cannot-add-new-events-after-calling-close-betw?noredirect=1#comment132782939_75247028 – Daniel Roldán Jan 26 '23 at 15:47
1

The Navigator's behaviour is expected.

You should replace Navigator.push(context, CupertinoPageRoute(builder: (context) => ChildC())); with

Navigator.push(context, CupertinoPageRoute(builder: (ctx) => BlocProvider.value(value: context.read<BlocC>()), child: ChildC());

This will work.

Rahul
  • 4,699
  • 5
  • 26
  • 38