Bloc: Navigator.push made me loose my bloc after Navigator.pop

Created on 4 Sep 2019  路  4Comments  路  Source: felangel/bloc

From one widget, w_1 I use a BlocListener to listen on the bloc b_1's state s_1. If s_1 fulfills my requirements, I Navigate.push to a widget w_2 that uses the same bloc b_1. Now, done with w_2 i Navigate.pop and return to w_1, where I find that b_1 does not respond to dispatch events.

My setup is as follows:

  @override
  Widget build(BuildContext context) {
    return BlocProvider<ActiveWorkoutBloc>(
      builder: (BuildContext context) => awb,
      child: BlocListener(
          bloc: awb,
          listener: (context, state) {
            // do stuff here based on BlocA's state
            if (state is Counting && state.counting == 0) {
              Navigator.push(context, MaterialPageRoute(builder: (context) {
                return BlocProvider<ActiveWorkoutBloc>(
                    builder: (context) => awb, child: buildPage());
              }));
            }
          },
          child: buildPage()),
    );
  }

How do I know that the state does not respond?
In my bloc, I wrote a print statement to print everytime it receives a dispatch. This will print while in w_1, when in w_2, but no longer when returning to w_1.

Is this a bug, or a fault of mine?

question

Most helpful comment

Helped alot! Thanks Felix.

All 4 comments

I solved the issue by changing the build to a widget (NoseArea) like this, instead of a method (buildPage):

  @override
  Widget build(BuildContext context) {
    return BlocProvider<ActiveWorkoutBloc>(
      builder: (BuildContext context) => awb,
      child: BlocListener(
          bloc: awb,
          listener: (context, state) {
            // do stuff here based on BlocA's state
            if (state is Counting && state.counting == 0) {
              Navigator.push(context, MaterialPageRoute(builder: (context) {
                return BlocProvider<ActiveWorkoutBloc>(
                    builder: (context) => awb, child: NoseAreaCard());
              }));
            }
          },
          child: NoseAreaCard()),
    );
  }

However, I dont understand that

if (state is Counting && state.counting == 0)

Only gets triggered once. Does anyone know what might cause this?

Hi @joakiti 馃憢
Thanks for opening an issue!

Regarding your question, you should keep in mind that BlocProvider will automatically dispose the bloc by default. I would recommend refactoring your example like so:

  @override
  Widget build(BuildContext context) {
    return BlocProvider<ActiveWorkoutBloc>(
      builder: (BuildContext context) => ActiveWorkoutBloc(),
      child: BlocListener<ActiveWorkoutBloc, ActiveWorkoutState>(
          listener: (context, state) {
            // do stuff here based on BlocA's state
            if (state is Counting && state.counting == 0) {
              Navigator.push(context, MaterialPageRoute(builder: (_) {
                return BlocProvider<ActiveWorkoutBloc>.value(
                    value: BlocProvider.of<ActiveWorkoutBloc>(context), child: buildPage());
              }));
            }
          },
          child: buildPage()),
    );
  }

This way the top level BlocProvider is responsible for creating and disposing the ActiveWorkoutBloc and the lower level BlocProvider just exposes the bloc to the new route using BlocProvider.value which doesn't automatically dispose the bloc.

Hope that helps 馃憤

Helped alot! Thanks Felix.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

timtraversy picture timtraversy  路  3Comments

komapeb picture komapeb  路  3Comments

rsnider19 picture rsnider19  路  3Comments

MahdiPishguy picture MahdiPishguy  路  3Comments

frankrod picture frankrod  路  3Comments