Bloc: Keeping BLoC state and navigator state synchronised

Created on 28 Oct 2019  路  5Comments  路  Source: felangel/bloc

Felix, first of all, thanks so much for writing bloc! It's a great package with really superb documentation.

I have a question about BLoC state maintenance and navigation. It can be illustrated most easily using the navigation example in the documentation. This uses a BlocListener to push a new named route in response to a BLoC state change. So you have a starting page "Page A" that's displayed in state StateA and a "Page B" that's displayed in state StateB, with the /pageB route getting pushed to the navigator by a BlocListener when the state changes to StateB.

The thing that I don't understand is what happens when that "Page B" is popped by the navigator, either in response to pressing the "Pop" button that's displayed on the page, or because the user presses the Android back button. The page gets popped and so we go back to viewing "Page A", but the BLoC state is still StateB. (You can see this clearly by adding the usual SimpleBlocDelegate for debugging.) I had been thinking that the BLoC state and what's displayed to the user should change in lockstep (that's what happens if you use the if (state is State1) { return ... } if (state is State2) { return ... } pattern in your build method).

This doesn't seem right to me, but maybe I'm missing something. I'm having a hard time seeing how to use a BLoC that spans multiple screens and works consistently with the navigator and the back button. Do you need to do something like set up a NavigatorObserver to keep track of pops and "put the BLoC state back"? Or am I just totally misunderstanding how to manage this?

question

Most helpful comment

Thanks everyone! That does make things clearer.

All 5 comments

You would usually have a bloc/page. If you need state from your page A's bloc in your page B, you can use BlocProvider<BlocA>.value(...) on top of your page B route for example and then you can retrieve bloc A this way and use it in your page B. The cool thing about this approach is that bloc A won't get disposed when popping page B. Let me know if this is what you're looking for.

Thanks @RollyPeres ! I think I need to read back through the tutorials again, because I definitely missed the idea that there should be a 1-to-1 correspondence between blocs and pages. I had been thinking that you might have blocs at three different levels: blocs for individual pages (the "normal" case, for form validation and similar), global blocs (for authentication, settings, themes, and so on), but also something at an intermediate level for managing multi-page flows (things like wizards, or in my particular case, a multi-step login process). Maybe I was just getting confused, because I can imagine how what you're suggesting would work for my use case.

I believe the Bloc should be created for big enough functionality part, not 1 per page. At least with this implementation of the BLoC pattern, it makes no sense.

Hi @ian-ross 馃憢
Thanks for opening an issue!

As @RollyPeres and @tenhobi have mentioned, you should provide a bloc at the closest common ancestor for all widgets that need access. If you need the same bloc in multiple routes, I would recommend providing the bloc at the closest common ancestor using BlocProvider(builder: (context) => MyBloc(), child: MyChild()) and then wrapping each route you push with BlocProvider.value(value: BlocProvider.of(context), child: MyPage()). This ensures that each route will have access to the same bloc instance and that popping routes will not result in prematurely closing the bloc.

Hope that helps 馃憤

Thanks everyone! That does make things clearer.

Was this page helpful?
0 / 5 - 0 ratings