Flutter Bloc State won't be yielded after change from the Page pushed on top

Issue

The AddWorkoutEvent is dispatched correctly from the PageCreateWorkout with a DbMWorkout.

This will be inserted in the correct table of the DB.
The PageCreateWorkout will be notified with the WorkoutAddedState to go to PageWorkoutDetail with the given workoutId. The routes are stacked on the PageSelectWorkout, in which the WorkoutBloc is mainly used, to show all workouts. In there the PageSelectWorkout shall be refreshed with the new WorkoutLoadedState with the newly given workoutList. (The WorkoutList contains the added Workout, which I checked in the logger; but the State won’t be yielded. Note that I am extending equatable to WorkoutStates.)

else if (event is AddWorkoutEvent) {
      logger.i("AddWorkoutEvent | workout: ${event.workout}");
      yield WorkoutLoadingState();
      try {
        DbMWorkout workout = await RepositoryWorkout.repo.add(event.workout);
        yield WorkoutAddedState(id: workout.id);
        List<DbMWorkout> workoutList = await RepositoryWorkout.repo.getAll();
        
        logger.i("AddWorkoutEvent | workoutListLength: ${workoutList.length}");

        yield WorkoutLoadedState(workoutList: workoutList); // <-- this state
      } catch (e) {
        yield WorkoutErrorState(message: e.toString());
      }
    } 

The PageSelectWorkout is the initialPage of a Navigator in an indexedStack:

IndexedStack(
                sizing: StackFit.expand,
                index: _currentIndex,
                children: <Widget>[
                  Navigator(
                      key: _pageOverview,
                      initialRoute: PageOverview.routeName,
                      onGenerateRoute: (route) =>
                          RouteGenerator.generateRoute(route)),
                  Navigator(
                      key: _pageSelectWorkoutNew,
                      initialRoute: PageSelectWorkout.routeName,
                      onGenerateRoute: (route) =>
                          RouteGenerator.generateRoute(route)),
                  Navigator(
                      key: _pageLog,
                      initialRoute: PageLog.routeName,
                      onGenerateRoute: (route) =>
                          RouteGenerator.generateRoute(route)),
                  Navigator(
                      key: _pageSettings,
                      initialRoute: PageSettings.routeName,
                      onGenerateRoute: (route) =>
                          RouteGenerator.generateRoute(route))
                ]),

The named Route to the SelectWorkout is wrapped with the correct Bloc in a BlocProvider:

case PageSelectWorkout.routeName:
    return CupertinoPageRoute(
        settings: settings,
        builder: (context) {
          return BlocProvider(
              create: (context) => WorkoutBloc(),
              child: PageSelectWorkout());
        });

Note: In other Events like DeleteWorkoutEvent , which is happening without navigating to another Page, the updated State gets yielded correctly.

Solution

I found the answer after checking out some GitHub Issues and it’s rather simple:
Since I want to access the same Bloc on 2 different Pages I can not just wrap a new Instance of the Bloc to each of the Page.

Instead I should wrap the BlocProvider with the WorkoutBloc higher in the WidgetTree Hierarchy for example in the main.dart

Before:

case PageSelectWorkout.routeName:
    return CupertinoPageRoute(
        settings: settings,
        builder: (context) => BlocProvider(
            create: (context) => WorkoutBloc(), // <-- Instance of WorkoutBloc
            child: PageSelectWorkout()));

  case PageCreateWorkout.routeName:
    return CupertinoPageRoute(
        settings: settings,
        builder: (context) => BlocProvider(
              create: (context) => WorkoutBloc(), // <-- Instance of WorkoutBloc
              child: PageCreateWorkout(
                workoutIndex: arguments["workoutIndex"],
                workoutPosition: arguments["workoutPosition"],
              ),
            ));

After:

case PageSelectWorkout.routeName:
    return CupertinoPageRoute(
        settings: settings, builder: (context) => PageSelectWorkout());

  case PageCreateWorkout.routeName:
    return CupertinoPageRoute(
      settings: settings,
      builder: (context) => PageCreateWorkout(
        workoutIndex: arguments["workoutIndex"],
        workoutPosition: arguments["workoutPosition"],
      ),
    );

and higher in the Inheritance/WidgetTree Hierarchy e.g. main.dart :

return MaterialApp(
                  debugShowCheckedModeBanner: false,
                  title: Strings.appTitle,
                  theme: AppTheme.darkTheme,
                  home: MultiBlocProvider(providers: [
                    BlocProvider<WorkoutBloc>(
                      create: (context) => WorkoutBloc(), // <-- put the Instance higher
                    ),
                    BlocProvider<NavigationBloc>(
                      create: (context) => NavigationBloc(),
                    ),
                  ], child: BottomNavigationController()));

Answered By – DJ2695

Answer Checked By – Marie Seifert (FlutterFixes Admin)

Leave a Reply

Your email address will not be published.