In this article, we will look at the basics of routing and navigation in Flutter. We will learn how to setup routing in our app so that we can navigate via navigation methods like:
- push
- pushNamed
- pop
- popUntil
Introduction
Navigating in Flutter is done with the help of the Navigator class that manages a stack of Route objects. The Navigator class provides different methods like Navigator.push(), Navigator.pop(), Navigator.pushNamed() etc. for managing the stack.
Setup
Let’s start by creating a new Flutter project.
flutter create routing
The command above creates a new project called “routing“.
Now let’s start coding by updating the main.dart file so that it looks like this:
//lib/main.dart
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Routing & Navigation"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'Hi, ready to route?',
),
],
),
),
);
}
}
We have created a basic setup for our app. If you run the app right now, you should see the screen below:

Now let’s create a folder called “routes” inside the “lib” folder. This folder will contain the screens that we will route to.
Add a file called “route_A.dart” in this folder with following file content:
//lib/routes/route_A.dart
import 'package:flutter/material.dart';
class RouteA extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text("Welcome to Route A"),
],
),
));
}
}
The Navigator class provides many methods to navigate between the different routes in an application. Also, there can be multiple ways to perform the same navigation.

For example, we can use either the push method or the pushNamed method to navigate to RouteA from the home page.
The choice depends upon how we have setup the routing in our application.
push Method
push<T extends Object>(BuildContext context, Route<T> route) → Future<T>
pushNamed Method
pushNamed<T extends Object>(BuildContext context, String routeName, { Object arguments }) → Future<T>
As we can see from the definitions of the these two methods, while pushNamed method takes the BuildContext and a route name as parameters, the push method takes BuildContext with the Route.
Setting up routes for pushNamed method
In order to use the pushNamed method, we should first create a map of widgets that can be navigated to as a String and WidgetBuilder pair.
The MaterialApp has a “routes” property for just this purpose.
We can add routes like this:
//lib/main.dart
return MaterialApp(
title: 'Flutter Demo',
routes: {
'routeA':(context) => RouteA()
},
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
To test this route, let’s add a button in the Homepage and navigate with pushNamed method.
//lib/main.dart
class MyHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Routing & Navigation"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'Hi, ready to route?',
),
RaisedButton(
child: Text("Let's go through route A"),
onPressed: () {
Navigator.pushNamed(context, "routeA");
},
),
],
),
),
);
}
}
When you run and press the button, it should navigate to RouteA.
Let’s add another route RouteB which will be identical to RouteA.
//lib/routes/route_B.dart
import 'package:flutter/material.dart';
class RouteB extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text("Welcome to Route B"),
],
),
));
}
}
This time we will navigate to this screen via push method.
As mentioned above, we need to pass an instance of Route class for this. We will make use of MaterialPageRoute which is an implementation of Route that replaces the entire screen with a platform-specific adaptive transition.
So let’s another button for this purpose with the Navigator.push method in the Homepage.
//lib/main.dart
RaisedButton(
child: Text("Let's go through route B"),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => RouteB()),
);
},
)
Our home page should appear as below and both of our routes should be functioning as expected.

Now, let’s try navigating backwards.
Similar to navigating ahead, for navigating back to a certain page is also facilitated by the Navigator methods.
For simply going back one screen, we can use the pop method.
So, we can have something like a back button on our RouteA that can take us back to Homepage as follows:
//lib/routes/route_A.dart
RaisedButton(
child: Text("Back to home"),
onPressed: () {
Navigator.pop(context);
},
),
Another scenario is when we want to navigate back multiple screens. Say the user reached RouteB via RouteA and he wants to get back to the Homepage. In this case, we can simply use the method popUntil so that all intermediate routes are removed from the Stack and the named view is presented.
To see this in action, let’s make few changes in our code.
Let’s first add a new route map as:
//lib/main.dart
routes: {
'routeA': (context) => RouteA(),
'/': (context) => MyHomePage()
},
The ‘/‘ key route is a special kind of map to the route that should specifically be used to identify the default route only. Also, since we are using the default route map, we can not use the home property as only one of these identifiers can be used.
So make sure to remove this property value as below:
//lib/main.dart
return MaterialApp(
title: 'Flutter Demo',
routes: {
'routeA': (context) => RouteA(),
'/': (context) => MyHomePage()
},
theme: ThemeData(
primarySwatch: Colors.blue,
),
);
This will basically navigate any route named as “/” to MyHomePage.
Next, add a button in RouteA that will navigate to RouteB.
//lib/routes/route_A.dart
RaisedButton(
child: Text("Let's go through route B from A"),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => RouteB()),
);
},
)
And finally another button in RouteB that will pop back to Home page.
//lib/routes/route_B.dart
RaisedButton(
child: Text("Go back all the way to home"),
onPressed: () {
Navigator.popUntil(context, ModalRoute.withName('/'));
},
)
Here we are using RoutePredicate returned from the ModelRoute.withName to pop until the specified route.
You should be able to successfully test these backward navigation now.
Handle Fallback Routes
Sometimes our app might get a request to a route that does not exist. It can happen if somehow a route was deleted but it’s navigation wasn’t updated. In this scenario, Flutter will throw an exception saying the specified route was not found.
To handle such scenarios, we can make use of another property of the MaterialApp called “onUnknownRoute“.
//lib/main.dart
return MaterialApp(
title: 'Flutter Demo',
onUnknownRoute: (RouteSettings setting) {
return new MaterialPageRoute(
builder: (context) => NotFoundPage()
);
},
....
..
Here, we are asking the framework to redirect all unknown routes to the “NotFoundPage“.
Conclusion
In this post we learned how to setup routing and perform common navigation in Flutter. More advanced topics like passing parameters, and hero animations are covered on the following posts.
You must be logged in to post a comment.