DEV Community

loading...
Cover image for Flutter BLoC: Right way to Handle navigation

Flutter BLoC: Right way to Handle navigation

pedromassango profile image Pedro Massango Updated on ・2 min read

If you have been using Flutter with BLoc maybe you had the necessity to navigate to a new page inside of your BLoC as we know we don't have a BuildContext to use Navigator.of() function to navigate. Let me show you how...

Note: I'm assuming here that you already know BLoC, if not just follow these link: https://felangel.github.io/bloc/

To navigate in reaction to a BLoC's state we sometimes do something like betwen our widgets:

It works but doesn't seems to be the right way because we need to check the state in the UI and wait until Flutter render the last frame to navigate. And then we think, but we can't navigate inside a BLoC because we don't have a BuildContext there!

Navigate without a BuildContext

Basically we use the navigatorState to navigate without a BuildContext, keep reading to see how it works.

  • Create your NavigatorBloc that receive a global key with the navigator state in the constructor. This bloc should only recieve events to navigate between pages, see the example bellow:
  • Create a global key to store with the navigator state and then pass it to your MaterialApp. Also we should pass this property to NavigatorBloc.

And we're all set, for each navigation you just create a event to navigate for each specific page. Now we can navigate to a page from any BLoC.

Tip: use BlocProvider.of<NavigatorBloc>(context).dispatch(NavigateToHomeEvent()); inside any widget to navigate to another page using the navigator BLoc. That way you will have a centralized navigation class.

Hope you enjoy the post, don't forget to leave an ❤️ and share the article.

Discussion

pic
Editor guide
Collapse
marnore profile image
Marius Noreikis

I only got it working after I changed the code to the following:

child: MaterialApp(
navigatorKey: _navigatorKey,
title: 'My App',
home: HomePage(),
)

Otherwise, the navigatorKey would not contain any state.

Collapse
pedromassango profile image
Pedro Massango Author

Thanks. Now I noticed that I made a typo

Collapse
alexandreroba profile image
Alexandre Roba

Hi, thanks for sharing.

Any idea why ShowDialog when called from the bloc fails (It does not raise an error, There is just no dialog displayed... :()
} else if (event is ShowError) {
showDialog(
context: navigatorKey.currentContext,
builder: (context) {
return RedDialog(
title: 'title',,
message:
'message',,
onNext: () {
Navigator.of(context).pop();
},
nextLabel: 'nest',,
);
});
}

Collapse
pedromassango profile image
Pedro Massango Author

Everything looks good. Are you sure that ShowError event is being fired?

Collapse
alexandreroba profile image
Alexandre Roba

Hi Pedro, I noticed something weird... if in the mapEventToState method you await for the navigator push methods, all following event push will simply be ignored...
@override
Stream mapEventToState(NavigatorAction event) async* {
if(event is NavigatorActionPop){
navigatorKey.currentState.pop();
}else if(event is NavigateToHomeEvent){
AWAIT navigatorKey.currentState.pushNamed('/home'); // This cause all following events to be ignored...
}
}

After some digging the navigatorKey.currentState.pushNamed() call never complete... You can add a then((_)=>print('completed')); this will never be called... We might have an issue somewhere here...

Any idea?

Alex

Collapse
pedromassango profile image
Pedro Massango Author

Maybe it is not good idea to await since it will block all incoming events. I've never tried await there before. Let me investigate...

Collapse
afermin profile image
Alexander Fermin

Hi Pedro, I am new working with Flutter and I am trying to implement you idea but I am getting a not stop looping. I am evaluating in the child widget with BlocBuilder the child state and sending the a event to my navigator bloc but my child widget doesn't stop reevaluating

Collapse
dkobia profile image
David Kobia

@pedromassango - absolutely brilliant.

Collapse
agrapine profile image
Alexandru Agrapine

This looks like a blocky way to navigate... indirectly

:) I think you're missing the redux dependency

Nice job on the article

Collapse
enzobonggio profile image
Enzo Bonggio

Hi, How is it blocky ?

Collapse
pedromassango profile image
Collapse
mpowloka profile image
mpowloka

Hey!

I was just looking for a similar solution. I will just check if it works, but right now I think this is not the best way to create a separate Bloc for Navigation only. Imo, the idea behind BloC is so UI doesn't know what happens after user interaction. If you have a Bloc for navigation then UI still knows what happens. It ends up being just a code extraction way, but the logic is not covered in any way.

Collapse
obrianjmuunga profile image
ObrianJMuunga

Hello, I am trying to use your example, but in navigation_bloc.dart, I am getting NavigationAction is not a type.

Collapse
pedromassango profile image
Pedro Massango Author

Can you show me a piece of your code?

Collapse
obrianjmuunga profile image
ObrianJMuunga

I am using flutter_bloc: 0.21.0

Collapse
aaabramenko profile image
AAAbramenko

We found a way how to navigate without BuildContext, but we need BuildContext to call BlocProvider.of<>(context)... I think I don't understand something, can you explain?

Collapse
pedromassango profile image
Pedro Massango Author

You always need to use the context do dispatch an action. What you can do it to use a unique instance of the NavigatorKey to navigate.

Collapse
furlongmt profile image
Matthew Furlong

Hey Pedro, I'm not sure I follow still. Is there another way you could explain this, possibly with an example?

Collapse
eugenebrusov profile image
Eugene Brusov

This way you can implement navigation and add to Stream any other side-effects (for example to show Snackbar or Dialog) stackoverflow.com/a/59347530/7158449

Collapse
davidag9 profile image
Agyakwalf

My bloc builds multiple screen on navigated view ,is this the reason