Tutorial

Introduction to Navigation in Flutter

Published on September 26, 2019
Default avatar

By Joshua Hall

Introduction to Navigation in Flutter

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.

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.

Project File Setup

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

Naming Routes

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.

main.dart
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 {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Navigation Demo',
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  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.

welcome_screen.dart
import 'package:flutter/material.dart';
import '../navbar.dart';

class WelcomeScreen extends StatelessWidget {
  static const String id = 'welcome_screen';

  @override
  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.

main.dart
class _MyHomePageState extends State<MyHomePage> {
  @override
  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()
    });
  }
}

Push and Pop

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.

navbar.dart
import 'package:flutter/material.dart';
import './screens/account_screen.dart';
import './screens/balance_screen.dart';
import './screens/transfer_screen.dart';

class Navbar extends StatelessWidget {
  @override
  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)),
        ],
      ),
    );
  }
}

Flutter Navigation demo

Conclusion

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.

Learn more about us


About the authors
Default avatar
Joshua Hall

author

Still looking for an answer?

Ask a questionSearch for more help

Was this helpful?
 
Leave a comment


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!

Try DigitalOcean for free

Click below to sign up and get $200 of credit to try our products over 60 days!

Sign up

Join the Tech Talk
Success! Thank you! Please check your email for further details.

Please complete your information!

Get our biweekly newsletter

Sign up for Infrastructure as a Newsletter.

Hollie's Hub for Good

Working on improving health and education, reducing inequality, and spurring economic growth? We'd like to help.

Become a contributor

Get paid to write technical tutorials and select a tech-focused charity to receive a matching donation.

Welcome to the developer cloud

DigitalOcean makes it simple to launch in the cloud and scale up as you grow — whether you're running one virtual machine or ten thousand.

Learn more
DigitalOcean Cloud Control Panel