Putting it all together

It is time to check out how to use the Navigator widget in practice. Let's create a basic flow to navigate to a second screen and back. It will look something like this:

The basic way to use a Navigator widget is like any other—by adding it to the widget tree:

class NavigatorDirectlyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Directionality(
child: Navigator(
onGenerateRoute: (RouteSettings settings) {
return MaterialPageRoute(
builder: (BuildContext context) => _screen1(context));
},
),
textDirection: TextDirection.ltr,
);
}
_screen1(BuildContext context) {...} // hidden for brevity
_screen2(BuildContext context) {...} // hidden for brevity
}
The Directionality widget was added here so that we could show Text widgets. Remember, WidgetsApp and variations manage this and more for us.

The Navigator widget contains an onGenerateRoute property, a callback that is responsible for creating a Route widget based on a RouteSettings object passed as an argument.

In the preceding example, you can see that we did not use the settings argument; instead, we returned a default route. The most common approach would check the settings' name property, which works as the identifier of the route. The framework uses the '/' name as the initial route by default and will make an initial call to the callback, passing this as an argument. So, the preceding example uses the _screen1 returned widget as the initial route.

Check the Named routes section later in this chapter for more details and examples of route names.

The result from the onGenerateRoute callback is a Route object. We have used the MaterialPageRoute type here. In its most basic implementation, we should pass an onGenerateRoute callback to it too. It should return a widget to be displayed as the Route. You might be asking: why not use a child property to add the child widget directly? Its creation depends on the context in which it is built, as the Navigator widget may create this Route widget in different contexts.

But if you check the following code, you will see that we can navigate from one screen to another by clicking the corresponding button. We can see this in the _screen1 method, for example:

  Widget _screen1(BuildContext context) {
return Container(
color: Colors.green,
child: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text("Screen 1"),
RaisedButton(
child: Text("Go to Screen 2"),
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(
builder: (BuildContext context) {
return _screen2(context);
},
),
);
},
)
],
),
);
}

Here, you can check that the Navigator widget is accessed by using its Navigator.of static method. You will be familiar with this by now and, as you might be guessing, this is the way we access the corresponding Navigator ancestor from a specific context, and yes, we can have many Navigator widgets in a tree. That is great, as we can have different pieces of independent navigation in subsections of an application.

Back to the example, let's have a look at the RaisedButton widget's onPressed callback, where we push a new Route into the navigation. From here, the value we pass to the push method is similar to the one returned from the onGenerateRoute callback in the previously added Navigator.

To summarize, our top Navigator widget uses the onGenerateRoute callback just to initialize the navigation by providing the initial Route. Later on, screen buttons were added to push a new Route to the navigation, using the push() method from the Navigator widget:

// button on screen 2 to navigate back
onPressed: () {
Navigator.of(context).pop();
},
// _NavigatorDirectlyAppState

The _screen2 widget is almost equal to _screen1; the only difference is that it pops itself from the navigation and goes back to the _screen1 widget.

There is a problem with the preceding example, though. If we press the back button on Android, for example, while on Screen 2, we should go back to Screen 1 as a result, but that is not the case. As we have added the Navigator widget by ourselves, the system is not aware of it: we need to manage it by ourselves as well.

To manage the back button, we need to use WidgetsBindingObserver, which can be used to react to lifecycle messages related to a Flutter application. As you can see in the source codes on GitHub (in the navigation directory), we first converted our app to Stateful and added WidgetsBindingObserver as a mixin to our State class. We also started the observer in initState() with WidgetsBinding.instance.addObserver(this); and stopped the observer with WidgetsBinding.instance.removeObserver(this); on dispose(). With this setup, we can override the didPopRoute() method from WidgetsBindingObserver and manage what happens when the system tells the app to pop a route. The didPopRoute() method is described as follows in the documentation: 

"[It is] called when the system tells the app to pop the current route. For example, on Android, this is called when the user presses the back button."

Inside the didPopRoute() method, we need to pop a Route from our Navigator widget. However, we cannot access Navigator through its static of method, as we do not have the context below this here. We can alternatively add a key to Navigator and access its state here:

// navigation_directly.dart
class _NavigatorDirectlyAppState extends State<NavigatorDirectlyApp> {
final _navigatorKey = GlobalKey<NavigatorState>();

// ... other fields and methods

// part of build method
Navigator(
key: _navigatorKey,
...
)
}

And we can add the didPopRoute() method as well:

@override
Future<bool> didPopRoute() {
return Future.value(_navigatorKey.currentState.pop());
}

Here, we have used the pop() method from the Navigator state to pop the top-most route from the navigation. As this method expects a true return if the observer was managed from the pop route notification, we return it from the Navigator pop values as well, so that when there are no more Routes from it to pop, the default behavior still happens (it quits the app).

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.116.90.141