0

I have created the Cubit SurveysCubit in HomePage from BlocProvider component, now, I want to access it from a new page pushed in its child body.

All works fine until the pushed page is reached here the following error is shown telling me that the SurveysBloc created on the previous page is not found :

BlocProvider.of() called with a context that does not contain a SurveysCubit.

This is the home page :

class HomePage extends StatelessWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return BlocProvider(
  create: (_) =>
      SurveysCubit(repo: RepositoryProvider.of<SurveyRepository>(_)),
  child: Builder(builder: (newContext) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            ElevatedButton(
              onPressed: () => Navigator.push(
                newContext,
                MaterialPageRoute<void>(
                  builder: (BuildContext newContext) => Surveys(),
                ),
              ),
              child: const Text("Surveys"),
            ),
          ],
        ),
      ),
    );
  }),
);
  }
}

Surveys, the pushed page :

class Surveys extends StatelessWidget {
  List<Survey> surveys = [];
  Surveys({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    surveys = BlocProvider.of<SurveysCubit>(context).getSurveys; //this call fails
    return Scaffold(
      body: ListView.builder(
        itemCount: surveys.length,
        itemBuilder: (context, i) => Column(
          children: [
            ShowSurvey(survey: surveys[i]),
            SizedBox(
              height: 20,
            ),
          ],
        ),
      ),
    );
  }
}

I know I could avoid Builder widget, but I wanted to force a new context creation. I think I provided the right context, so it should work, I don't understand what's happening.

Yauheni Pakala
  • 868
  • 9
  • 16
Finley Adams
  • 773
  • 1
  • 8
  • 20

2 Answers2

2

you want to share your cubit state between 2 pages you have 2 option to achieve that but first let me explain what BlocProvider.of or Navigator.of basically the "of" word, ClassA.of means that lets search in my ancestors in context tree about an object of type ClassA. so when you are typing Navigator.of you are getting the Navigator object from node above you in the tree, same as BlocProvider.of. now back to your question you are providing a bloc in page HomePage, then trying to access it from Surveys page, now this Surveys page can not access it, because the bloc you are asking to get is not providing in an ancestor node of Surveys page. to solve this you can pass the bloc instance as parameter to surveys page and wrap the hole page with BlocProvider.value

class Surveys extends StatelessWidget {
  final cubit;
  Surveys({Key? key, required this.cubit}) : super(key: key);

  @override
  Widget build(BuildContext context) {
   return BlocProvider.value(
          value: cubit,
          child:....
          );
  }
}

or wrap the page directly when you are pushing it (I do not recommend this because every time you want to use this page you have to remember to wrap it with bloc provider)

Navigator.push(
                newContext,
                MaterialPageRoute<void>(
                  builder: (BuildContext newContext) => BlocProvider.value(
                  value: BlocProvider.of<SurveysCubit>(newContext);
                  child: Surveys()
                  ),
                ),
              ),

or last Option is when you want to access you bloc in every page not only this 2 pages just wrap your MaterialApp in the main function with bloc provider. now when ever you call BlocProvider.of(context) you will get it.

mjd
  • 96
  • 2
0

First of, if your HomePage is using SurveysCubit I'd recommend wraping it completely rather than declaring BlocProvider on top of build method. This will minimalize chance that you will run into issues like this one.

But if you wonna stay with your implementation, I have few tips.

  • Remove Builder from tree; it is an inline alternative to defining StatelessWidget. It's redundant here.
  • Even if you will pass the right context when pushing Surveys page, errors might occur because RepositoryProvider is using new throwaway context _ that clearly doesnt have repo in it, or it will but then, your implementation is missleading to people reading it.
class HomePage extends StatelessWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return BlocProvider(
      create: (newContext) => SurveysCubit(
        repo: RepositoryProvider.of<SurveyRepository>(newContext),
      ),
      child: Scaffold(
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              ElevatedButton(
                onPressed: () => Navigator.push(
                  newContext,
                  MaterialPageRoute<Surveys>(
                    builder: (_) => Surveys(),
                  ),
                ),
                child: const Text("Surveys"),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

If this doesnt help, you might need to wrap MaterialPageRoute with BlocProvicer.value().

Secondly, I advice using power of bloc by using BlocBuilder inside your Survey page rather than reading cubit value once. You lose app responsiveness to state changes by fething data once. Best case scenario you want to react to changes, not reload page to get changes etc.

Consider this:

class Surveys extends StatelessWidget {
  Surveys({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: BlocBuilder<SurveysCubit, SurveysState>(
        builder: (context, state) {
          return ListView.builder(
            itemCount: state.surveys.length,
            itemBuilder: (context, i) => Column(
              children: [
                ShowSurvey(survey: state.surveys[i]),
                const SizedBox(height: 20),
              ],
            ),
          );
        },
      ),
    );
  }
}

Please tell if it worked.

Stahp
  • 972
  • 1
  • 9
  • 16
  • The "newContext" after Navigator.push isn't defined, it's in a different scope from the above one. By the way, no it didn't solve – Finley Adams May 15 '22 at 02:53