Search code examples
flutterdartflutter-providerflutter-bloc

Flutter could not find the correct Provider<Bloc> after navigating to different route


I'm using bloc to handle the state of a signup form. But I get the error: Error: Could not find the correct Provider<CreateDidBloc> above this BlocBuilder<CreateDidBloc, CreateDidState> Widget.
As you can see in the error I do use the bloc CreateDidBloc and the state CreateDidState and get them through a BlocBuilder.
The BlocBuilder is positioned in my "signup" page, which in my case is called Creation. The error occurs when I'm navigating from my introduction page to the creation page.
Below is an extract of my MaterialApp in which I wrap the creation page with a RepositoryProvider and BlocProvider so that the creation page has access to the Bloc and repository. But for some reason does the creation page not find the BlocProvider for the CreateDidBloc after navigating to the creation page.

initialRoute: "/introduction",
routes: {
  "/introduction": (context) => Introduction(),
  "/create": (context) => RepositoryProvider(
        create: (context) => CreateDidRepository(),
        child: BlocProvider(
          create: (BuildContext context) => CreateDidBloc(
            repo: context.read<CreateDidRepository>(),
          ),
          child: Creation(),
        ),
      ),
},

This is how I navigate to the Creation screen:

Navigator.push(context,
    PageTransition(type: PageTransitionType.fade, child: Creation()));

Creation (signup) page:

@override
Widget build(BuildContext context) {
  return SafeArea(
      child: Scaffold(
          body: Column(
    children: [
      Expanded(
        child: PageView(
          physics: const NeverScrollableScrollPhysics(),
          controller: _pageController,
          onPageChanged: (index) {
            setState(() => currentStep = index);
          },
          children: [
            Step1(),
            Step2(),
            Step3(),
          ],
        ),
      ),
      Row(
        mainAxisAlignment: MainAxisAlignment.spaceEvenly,
        children: [
          BlocBuilder<CreateDidBloc, CreateDidState>(
            builder: (context, state) {
              return ElevatedButton(
                  onPressed: () {
                    next();
                  },
                  child: Text(L.of(context).next));
            },
          ),
          BlocBuilder<CreateDidBloc, CreateDidState>(
            builder: (context, state) {
              return OutlinedButton(
                  onPressed:
                      state.formStatus is FormSubmitting ? null : cancel,
                  child: Text(L.of(context).back));
            },
          )
        ],
      ),
      const SizedBox(
        height: 16,
      )
    ],
  )));
}

This is the stack trace:

When the exception was thrown, this was the stack
#0      Provider._inheritedElementOf
#1      Provider.of
#2      ReadContext.read
#3      _BlocBuilderBaseState.initState
#4      StatefulElement._firstBuild

Solution

  • I found the reason for why my BlocBuilder couldn't find the CreateDidBloc after navigating. That error occurs because navigating in Flutter pushes your page right below the MaterialApp in the widget tree. That means that my newly navigated Page is above the BlocProvider which is specified in the widget tree below the MaterialApp.
    To fix this I just wrapped my MaterialApp with a MultiBlocProvider and the RepositoryProvider so that the Bloc is provided globally no matter in what route I am.
    This is the important extract in which I can define the blocs globally:

    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return RepositoryProvider(
            create: (context) => CreateDidRepository(),
            child: MultiBlocProvider(
                providers: [
                  BlocProvider<LanguageBloc>(
                      create: (context) =>
                          LanguageBloc(LanguagePreference.getLanguage())),
                  BlocProvider<CreateDidBloc>(
                      create: (context) =>
                          CreateDidBloc(repo: context.read<CreateDidRepository>()))
                ],
                child: BlocBuilder<LanguageBloc, LanguageState>(
                  builder: (context, state) {
                    return MaterialApp(...)