Navigator class
A widget that manages a set of child widgets with a stack discipline.
Many apps have a navigator near the top of their widget hierarchy in order to display their logical history using an Overlay with the most recently visited pages visually on top of the older pages. Using this pattern lets the navigator visually transition from one page to another by moving the widgets around in the overlay. Similarly, the navigator can be used to show a dialog by positioning the dialog widget above the current page.
Using the Navigator
Mobile apps typically reveal their contents via full-screen elements called "screens" or "pages". In Flutter these elements are called routes and they're managed by a Navigator widget. The navigator manages a stack of Route objects and provides methods for managing the stack, like Navigator.push and Navigator.pop.
Displaying a full-screen route
Although you can create a navigator directly, it's most common to use the navigator created by a WidgetsApp or a MaterialApp widget. You can refer to that navigator with Navigator.of.
A MaterialApp is the simplest way to set things up. The MaterialApp's home becomes the route at the bottom of the Navigator's stack. It is what you see when the app is launched.
void main() {
runApp(MaterialApp(home: MyAppHome()));
}
To push a new route on the stack you can create an instance of MaterialPageRoute with a builder function that creates whatever you want to appear on the screen. For example:
Navigator.push(context, MaterialPageRoute<void>(
builder: (BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('My Page')),
body: Center(
child: FlatButton(
child: Text('POP'),
onPressed: () {
Navigator.pop(context);
},
),
),
);
},
));
The route defines its widget with a builder function instead of a child widget because it will be built and rebuilt in different contexts depending on when it's pushed and popped.
As you can see, the new route can be popped, revealing the app's home page, with the Navigator's pop method:
Navigator.pop(context);
It usually isn't necessary to provide a widget that pops the Navigator in a route with a Scaffold because the Scaffold automatically adds a 'back' button to its AppBar. Pressing the back button causes Navigator.pop to be called. On Android, pressing the system back button does the same thing.
Using named navigator routes
Mobile apps often manage a large number of routes and it's often easiest to refer to them by name. Route names, by convention, use a path-like structure (for example, '/a/b/c'). The app's home page route is named '/' by default.
The MaterialApp can be created
with a Map<String, WidgetBuilder>
which maps from a route's name to
a builder function that will create it. The MaterialApp uses this
map to create a value for its navigator's onGenerateRoute callback.
void main() {
runApp(MaterialApp(
home: MyAppHome(), // becomes the route named '/'
routes: <String, WidgetBuilder> {
'/a': (BuildContext context) => MyPage(title: 'page A'),
'/b': (BuildContext context) => MyPage(title: 'page B'),
'/c': (BuildContext context) => MyPage(title: 'page C'),
},
));
}
To show a route by name:
Navigator.pushNamed(context, '/b');
Routes can return a value
When a route is pushed to ask the user for a value, the value can be returned via the pop method's result parameter.
Methods that push a route return a Future. The Future resolves when the
route is popped and the Future's value is the pop method's result
parameter.
For example if we wanted to ask the user to press 'OK' to confirm an
operation we could await
the result of Navigator.push:
bool value = await Navigator.push(context, MaterialPageRoute<bool>(
builder: (BuildContext context) {
return Center(
child: GestureDetector(
child: Text('OK'),
onTap: () { Navigator.pop(context, true); }
),
);
}
));
If the user presses 'OK' then value will be true. If the user backs out of the route, for example by pressing the Scaffold's back button, the value will be null.
When a route is used to return a value, the route's type parameter must
match the type of pop's result. That's why we've used
MaterialPageRoute<bool>
instead of MaterialPageRoute<void>
or just
MaterialPageRoute
. (If you prefer to not specify the types, though, that's
fine too.)
Popup routes
Routes don't have to obscure the entire screen. PopupRoutes cover the screen with a ModalRoute.barrierColor that can be only partially opaque to allow the current screen to show through. Popup routes are "modal" because they block input to the widgets below.
There are functions which create and show popup routes. For example: showDialog, showMenu, and showModalBottomSheet. These functions return their pushed route's Future as described above. Callers can await the returned value to take an action when the route is popped, or to discover the route's value.
There are also widgets which create popup routes, like PopupMenuButton and DropdownButton. These widgets create internal subclasses of PopupRoute and use the Navigator's push and pop methods to show and dismiss them.
Custom routes
You can create your own subclass of one of the widget library route classes like PopupRoute, ModalRoute, or PageRoute, to control the animated transition employed to show the route, the color and behavior of the route's modal barrier, and other aspects of the route.
The PageRouteBuilder class makes it possible to define a custom route
in terms of callbacks. Here's an example that rotates and fades its child
when the route appears or disappears. This route does not obscure the entire
screen because it specifies opaque: false
, just as a popup route does.
Navigator.push(context, PageRouteBuilder(
opaque: false,
pageBuilder: (BuildContext context, _, __) {
return Center(child: Text('My PageRoute'));
},
transitionsBuilder: (___, Animation<double> animation, ____, Widget child) {
return FadeTransition(
opacity: animation,
child: RotationTransition(
turns: Tween<double>(begin: 0.5, end: 1.0).animate(animation),
child: child,
),
);
}
));
The page route is built in two parts, the "page" and the
"transitions". The page becomes a descendant of the child passed to
the buildTransitions
method. Typically the page is only built once,
because it doesn't depend on its animation parameters (elided with _
and __
in this example). The transition is built on every frame
for its duration.
Nesting Navigators
An app can use more than one Navigator. Nesting one Navigator below another Navigator can be used to create an "inner journey" such as tabbed navigation, user registration, store checkout, or other independent journeys that represent a subsection of your overall application.
Real World Example
It is standard practice for iOS apps to use tabbed navigation where each tab maintains its own navigation history. Therefore, each tab has its own Navigator, creating a kind of "parallel navigation."
In addition to the parallel navigation of the tabs, it is still possible to launch full-screen pages that completely cover the tabs. For example: an on-boarding flow, or an alert dialog. Therefore, there must exist a "root" Navigator that sits above the tab navigation. As a result, each of the tab's Navigators are actually nested Navigators sitting below a single root Navigator.
The nested Navigators for tabbed navigation sit in WidgetApp
and
CupertinoTabView, so you don't need to worry about nested Navigators
in this situation, but it's a real world example where nested Navigators
are used.
Sample Code
The following example demonstrates how a nested Navigator can be used to present a standalone user registration journey.
Even though this example uses two Navigators to demonstrate nested Navigators, a similar result is possible using only a single Navigator.
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
// ...some parameters omitted...
// MaterialApp contains our top-level Navigator
initialRoute: '/',
routes: {
'/': (BuildContext context) => HomePage(),
'/signup': (BuildContext context) => SignUpPage(),
},
);
}
}
class SignUpPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
// SignUpPage builds its own Navigator which ends up being a nested
// Navigator in our app.
return Navigator(
initialRoute: 'signup/personal_info',
onGenerateRoute: (RouteSettings settings) {
WidgetBuilder builder;
switch (settings.name) {
case 'signup/personal_info':
// Assume CollectPersonalInfoPage collects personal info and then
// navigates to 'signup/choose_credentials'.
builder = (BuildContext _) => CollectPersonalInfoPage();
break;
case 'signup/choose_credentials':
// Assume ChooseCredentialsPage collects new credentials and then
// invokes 'onSignupComplete()'.
builder = (BuildContext _) => ChooseCredentialsPage(
onSignupComplete: () {
// Referencing Navigator.of(context) from here refers to the
// top level Navigator because SignUpPage is above the
// nested Navigator that it created. Therefore, this pop()
// will pop the entire "sign up" journey and return to the
// "/" route, AKA HomePage.
Navigator.of(context).pop();
},
);
break;
default:
throw Exception('Invalid route: ${settings.name}');
}
return MaterialPageRoute(builder: builder, settings: settings);
},
);
}
}
Navigator.of operates on the nearest ancestor Navigator from the given
BuildContext. Be sure to provide a BuildContext below the intended
Navigator, especially in large build
methods where nested Navigators
are created. The Builder widget can be used to access a BuildContext at
a desired location in the widget subtree.
- Inheritance
Constructors
-
Creates a widget that maintains a stack-based history of child widgets. [...]
const
Properties
- initialRoute → String
-
The name of the first route to show. [...]
final
-
observers
→ List<
NavigatorObserver> -
A list of observers for this navigator.
final
- onGenerateRoute → RouteFactory
-
Called to generate a route for a given RouteSettings.
final
- onUnknownRoute → RouteFactory
-
Called when onGenerateRoute fails to generate a route. [...]
final
- hashCode → int
-
The hash code for this object. [...]
read-only, inherited
- key → Key
-
Controls how one widget replaces another widget in the tree. [...]
final, inherited
- runtimeType → Type
-
A representation of the runtime type of the object.
read-only, inherited
Methods
-
createState(
) → NavigatorState -
Creates the mutable state for this widget at a given location in the tree. [...]
override
-
createElement(
) → StatefulElement -
Creates a StatefulElement to manage this widget's location in the tree. [...]
inherited
-
debugDescribeChildren(
) → List< DiagnosticsNode> -
Returns a list of DiagnosticsNode objects describing this node's
children. [...]
@protected, inherited
-
debugFillProperties(
DiagnosticPropertiesBuilder properties) → void -
Add additional properties associated with the node. [...]
inherited
-
noSuchMethod(
Invocation invocation) → dynamic -
Invoked when a non-existent method or property is accessed. [...]
inherited
-
toDiagnosticsNode(
{String name, DiagnosticsTreeStyle style }) → DiagnosticsNode -
Returns a debug representation of the object that is used by debugging
tools and by toStringDeep. [...]
inherited
-
toString(
{DiagnosticLevel minLevel: DiagnosticLevel.debug }) → String -
Returns a string representation of this object.
inherited
-
toStringDeep(
{String prefixLineOne: '', String prefixOtherLines, DiagnosticLevel minLevel: DiagnosticLevel.debug }) → String -
Returns a string representation of this node and its descendants. [...]
inherited
-
toStringShallow(
{String joiner: ', ', DiagnosticLevel minLevel: DiagnosticLevel.debug }) → String -
Returns a one-line detailed description of the object. [...]
inherited
-
toStringShort(
) → String -
A short, textual description of this widget.
inherited
Operators
-
operator ==(
dynamic other) → bool -
The equality operator. [...]
inherited
Static Methods
-
canPop(
BuildContext context) → bool - Whether the navigator that most tightly encloses the given context can be popped. [...]
-
maybePop<
T extends Object>( BuildContext context, [ T result ]) → Future< bool> -
Returns the value of the current route's Route.willPop method for the
navigator that most tightly encloses the given context. [...]
@optionalTypeArgs
-
of(
BuildContext context, { bool nullOk: false }) → NavigatorState - The state from the closest instance of this class that encloses the given context. [...]
-
pop<
T extends Object>( BuildContext context, [ T result ]) → bool -
Pop the top-most route off the navigator that most tightly encloses the
given context. [...]
@optionalTypeArgs
-
popAndPushNamed<
T extends Object, TO extends Object>( BuildContext context, String routeName, { TO result }) → Future< T> -
Pop the current route off the navigator that most tightly encloses the
given context and push a named route in its place. [...]
@optionalTypeArgs
-
popUntil(
BuildContext context, RoutePredicate predicate) → void - Calls pop repeatedly on the navigator that most tightly encloses the given context until the predicate returns true. [...]
-
push<
T extends Object>( BuildContext context, Route< T> route) → Future< T> -
Push the given route onto the navigator that most tightly encloses the
given context. [...]
@optionalTypeArgs
-
pushAndRemoveUntil<
T extends Object>( BuildContext context, Route< T> newRoute, RoutePredicate predicate) → Future< T> -
Push the given route onto the navigator that most tightly encloses the
given context, and then remove all the previous routes until the
predicate
returns true. [...]@optionalTypeArgs -
pushNamed<
T extends Object>( BuildContext context, String routeName) → Future< T> -
Push a named route onto the navigator that most tightly encloses the given
context. [...]
@optionalTypeArgs
-
pushNamedAndRemoveUntil<
T extends Object>( BuildContext context, String newRouteName, RoutePredicate predicate) → Future< T> -
Push the route with the given name onto the navigator that most tightly
encloses the given context, and then remove all the previous routes until
the
predicate
returns true. [...]@optionalTypeArgs -
pushReplacement<
T extends Object, TO extends Object>( BuildContext context, Route< T> newRoute, { TO result }) → Future< T> -
Replace the current route of the navigator that most tightly encloses the
given context by pushing the given route and then disposing the previous
route once the new route has finished animating in. [...]
@optionalTypeArgs
-
pushReplacementNamed<
T extends Object, TO extends Object>( BuildContext context, String routeName, { TO result }) → Future< T> -
Replace the current route of the navigator that most tightly encloses the
given context by pushing the route named
routeName
and then disposing the previous route once the new route has finished animating in. [...]@optionalTypeArgs -
removeRoute(
BuildContext context, Route route) → void -
Immediately remove
route
from the navigator that most tightly encloses the given context, and Route.dispose it. [...] -
removeRouteBelow(
BuildContext context, Route anchorRoute) → void -
Immediately remove a route from the navigator that most tightly encloses
the given context, and Route.dispose it. The route to be replaced is the
one below the given
anchorRoute
. [...] -
replace<
T extends Object>( BuildContext context, { Route oldRoute, Route< T> newRoute }) → void -
Replaces a route on the navigator that most tightly encloses the given
context with a new route. [...]
@optionalTypeArgs
-
replaceRouteBelow<
T extends Object>( BuildContext context, { Route anchorRoute, Route< T> newRoute }) → void -
Replaces a route on the navigator that most tightly encloses the given
context with a new route. The route to be replaced is the one below the
given
anchorRoute
. [...]@optionalTypeArgs
Constants
- defaultRouteName → const String
-
The default name for the initialRoute. [...]
'/'