One of the most fundamental aspects of a mobile app is for the user to be able to move between different pages. Luckily for us, Flutter makes creating routes and moving between screens incredibly easy, especially when compared to many front-end solutions.
For our example, we’re just going to have 4 screens, our main.dart
file, and break the navbar into its own file.
* screens 📂
* account_screen.dart
* balance_screen.dart
* transfer_screen.dart
* welcome_screen.dart
* main.dart
* navbar.dart
While you would want to break each route into its own file in most cases, we’ll put them in our main.dart
for now.
In our MaterialApp
we can set the routes
map, which is a list of key/value pairs. Each item in this map links a string value to a callback function that returns the page we want rendered. The point of this is to speed up development by letting us toss around something like 'welcome_screen'
whenever we need a new page, instead of the full (context) => WelcomeScreen()
.
To set our home page we can either use the MaterialApp
’s home
property or the initialRoute
property. They effectively do the same thing but home
takes the class itself, like WelcomeScreen()
, and initialRoute
takes the key from our routes
map. You can’t use both since that confuses the compiler.
import 'package:flutter/material.dart';
import './screens/welcome_screen.dart';
import './screens/account_screen.dart';
import './screens/balance_screen.dart';
import './screens/transfer_screen.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
Widget build(BuildContext context) {
return MaterialApp(
title: 'Navigation Demo',
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
Widget build(BuildContext context) {
return MaterialApp(
home: WelcomeScreen(),
routes: {
'welcome_screen': (context) => WelcomeScreen(),
'account_screen': (context) => AccountScreen(),
'balance_screen': (context) => BalanceScreen(),
'transfer_screen': (context) => TransferScreen()
});
}
}
That works fine, but you may end up typing each of these routes often and just using strings will make it hard to debug when you make the slightest typo. Instead it would make our code a bit less fragile to store each key in a static id variable in each class and just access that id. This will also give us the benefit of VSCode’s IntelliSense and help figuring out why a page may be unavailable.
Each screen in our example is the same, beside the id and the text widget. We’re also setting out bottom navbar to a widget that we’ll create later.
import 'package:flutter/material.dart';
import '../navbar.dart';
class WelcomeScreen extends StatelessWidget {
static const String id = 'welcome_screen';
Widget build(BuildContext context) {
return Scaffold(
body: Center(
bottomNavigationBar: Navbar(),
child: Text('Welcome'),
),
);
}
}
Now we can replace our string with each screen’s id. Notice that we’re accessing it without actually calling the class itself.
class _MyHomePageState extends State<MyHomePage> {
Widget build(BuildContext context) {
return MaterialApp(initialRoute: WelcomeScreen.id, routes: {
WelcomeScreen.id: (context) => WelcomeScreen(),
AccountScreen.id: (context) => AccountScreen(),
BalanceScreen.id: (context) => BalanceScreen(),
TransferScreen.id: (context) => TransferScreen()
});
}
}
Unlike with front-end web development, mobile routing is based on ‘stacking’ screens on top of each other. When we navigate from the welcome screen to the account screen, we’re not really changing pages but adding our account screen onto our stack, thus covering the previous page. To go back to the welcome screen, we would just need to destroy, or pop
off, the uppermost layer revealing the already rendered page beneath it.
There are quite a few different methods on Navigator
to do this, which you can fully explore here. The main two we need are pushNamed
to add to our stack and pop
to remove the latest layer. pop
just needs our build’s context
and push
methods needs the context
and the page’s key we’ve setup in our routes.
Any method appended with Named
is for when we’ve set up our routes in the MaterialApp
, otherwise you could pass in the callback itself instead of our keys.
import 'package:flutter/material.dart';
import './screens/account_screen.dart';
import './screens/balance_screen.dart';
import './screens/transfer_screen.dart';
class Navbar extends StatelessWidget {
Widget build(BuildContext context) {
return Container(
color: Colors.red,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
FlatButton(
onPressed: () => Navigator.pop(context),
child: Icon(Icons.arrow_left, color: Colors.white, size: 40)),
FlatButton(
onPressed: () => Navigator.pushNamed(context, BalanceScreen.id),
child: Icon(Icons.account_balance, color: Colors.white)),
FlatButton(
onPressed: () => Navigator.pushNamed(context, TransferScreen.id),
child: Icon(Icons.sync, color: Colors.white)),
FlatButton(
onPressed: () => Navigator.pushNamed(context, AccountScreen.id),
child: Icon(Icons.account_circle, color: Colors.white)),
],
),
);
}
}
Yet again when it comes to routing and navigation Flutter really shines in efficiency and ease of use. Hopefully this short tutorial was helpful in understanding this new technology.
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
While we believe that this content benefits our community, we have not yet thoroughly reviewed it. If you have any suggestions for improvements, please let us know by clicking the “report an issue“ button at the bottom of the tutorial.
This textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!