Back

Jun 15, 2022

Jun 15, 2022

Flutter Navigation Overview and Best Practices

With Flutter, there are two methods to implement navigation; both have advantages and disadvantages, but one might be better suited than the other for your application.

A computer with an open IDE next to a plant.
A computer with an open IDE next to a plant.
A computer with an open IDE next to a plant.

Navigation is one of the most fundamental components of any app and is critical to ensuring that your app is providing a good user experience. With Flutter, there are two methods to implement navigation: the `Navigator` widget and the `Router` widget. Both these approaches have advantages and disadvantages, and one might be better suited than the other for your application.

This article will first explain why Flutter navigation is important, and then show you how to implement navigation in your Flutter apps using the Navigator widget and the Router widget.

What’s Flutter Navigation?

Let’s say that your app looks like this:

The base state of our app.

This home page has a button that leads to a login page. Now, you need to code this button so that it displays the login page upon clicking. This can be accomplished using Flutter navigation.

Through the navigation stack, which is explained fully later in the article, Flutter navigation provides the ability to show different pages to users depending on their actions, for example, clicking a button leads to the display of a different page.

Why Getting to Grips with Flutter Navigation Is Important

Understanding Flutter navigation is important and critical for a good user experience for a few reasons:

  • An app consists of multiple pages presenting different pieces of information. If users can’t navigate to and from those pages, they won’t be able to access all the content, rendering the app useless.

  • When users struggle to navigate through your app, the result is bad user experience. If you don’t understand Flutter navigation properly, you won’t be able to implement a navigation system that works effectively for your app, which ties back to optimizing the user experience.

  • A strong understanding of the navigation system will help you deal with technical issues that come up in developing the app. Copy-pasting code won’t help during troubleshooting.

How to Implement Flutter Navigation

Continuing with the example described above, this section will demonstrate how you can start implementing Flutter navigation.

The home page of the app that’s used an example in this article starts off with the following code:

import 'package:flutter/material.dart';
void main() {
  runApp(const MyApp());
}
class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  }
}
class MyHomePage extends StatelessWidget {
  const MyHomePage({Key? key}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Homepage'),
      ),
      body: Column(
        children: [
          Padding(
            padding: const EdgeInsets.all(16.0),
            child: Center(child: Text('Click below to go to login page', style: TextStyle(fontSize: 23),)),
          ),
          ElevatedButton(onPressed: () {}, child: Text('Login'))
        ],
      ),
    );
  }
}

In this example, you have one main widget called MyApp and another widget called MyHomePage, containing your app bar, text, and button. MyApp references MyHomePage for the content of the page.

With the basic code for the home page in place, you now need to work on your button so that it displays another screen upon clicking. For that, let’s explore the two navigation methods.

The Navigator Widget

As the name suggests, the Navigator widget is the most popular option in Flutter for navigation. The two main functions you need to know for Navigator are Navigator.push() and Navigator.pop().

However, before you dive into using these functions, you need to understand a fundamental concept, the navigation stack.

A diagram explaining the concept of the navigation stack.

You can think of the navigation stack as a pile containing the pages in your app. The page at the top of the bundle will be displayed to the user, while the others remain out of sight.

When you push a page, it goes to the top of the navigation stack, and that’s what the user sees. Using pop, you remove the page at the top of the navigation stack, displaying the page underneath it to the user. So you can either add a page on the stack for navigation between two pages or remove one from the stack.

By understanding the role of the navigation stack, you’ll be able to effectively use the functions like push and pop as a subset of the Navigator widget. Let’s continue with the use case and implementation now.

The Navigator Widget Use Case

You should use the Navigator widget when:

  • You don’t need to preserve the state of the underlying pages on the navigation stack;

  • Nor do you need to store the browsing history of the user; and

  • You just want to pass simple data to other pages.

This widget provides a straightforward and clean way to navigate between pages on a mobile app where the user cannot directly access the page URLs. You’ll be able to add navigation without writing too much code or logic.

Navigator.push()

You can add pages to the call stack with the Navigator.push() function. For example, if you want to go from the home page to the login page, you should use the push() function as follows:

ElevatedButton(
  onPressed: () {
    Navigator.push(
      context,
      MaterialPageRoute(
        builder: (context) => LoginPage(),
      )
    );
  },
  child: Text('Login')
)

For the navigation to work, you only need to edit the ElevatedButton(), because that’s how the user will hop from one page to another.

Annotated screenshot of the button and its code.

Note: For reference, here’s an unannotated version of the same screenshot.

Unannotated screenshot of the button and its code.

In the onPressed() property of the button, you added a Navigator.push() function where we have passed the context and the MaterialPageRoute widget.

The MaterialPageRoute helps create a route object that can be pushed onto the navigation stack. You define LoginPage() as the destination, which is a widget in your login.dart file for the login page.

As you can see, this process is relatively straightforward. Now, let’s look at how you navigate back to other pages on the navigation stack.

Navigator.pop()

This simple function is used to go back to the previous route on the stack. On your login page, you add a simple button using the following code:

import 'package:flutter/material.dart';
class LoginPage extends StatelessWidget {
  const LoginPage({ Key? key }) : super(key: key);
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Login Page"),
      ),
      body: Column(children: [
        Text('Hello World!'),
        ElevatedButton(onPressed: () {}, child: Text('Go Back'))
      ],),
    );
  }
}

You can add the functionality to go back to the home page upon clicking the button with the following modifications to the button code:

ElevatedButton(
  onPressed: () {
    Navigator.pop(context);
  },
  child: Text('Go Back')
)

A short line inside the onPressed() function is all that’s needed to add this functionality.

The Router Widget

Flutter Navigator 2.0 introduced the Router widget to take a more declarative route approach. Its main aim is to help sync the browser URL to the app pages.

The Router Widget Use Cases

The Router widget is what will likely come in handy if you’re using Flutter for web app development.

At first, the widget can be challenging to master because it has many essential nested functions that you need to understand, and it requires many more lines of code even for simple navigation compared to the Navigator widget.

However, if your app has any type of content and you want to save the history of every single interaction, then you should use the Router widget. For example, if you’re building a blog and you want to store the URLs of the user’s visited blog posts from their history. Or, let’s say you showcase videos on your site and you want to store the URLs of videos that users have watched on your website from their history.

How Does It Work?

As we mentioned, the Router widget is complicated. In fact, despite Flutter trying to make routing more straightforward with this widget, it’s probably more complex than it needs to be.

The widget contains several subsections, which can be explained as follows:

  • Router: A new widget that dispatches the opening and closing of pages in your Flutter app. It wraps around the pages attribute in the Navigator widget to automatically modify it upon the app’s state changes.

  • Route name provider: This delegate assists the router in actually understanding which routes to show according to the operating system’s request.

  • Route name parser: Parses the string from routeNameProvider into a user-specified data type.

  • Router delegate: This is the main component of the Router widget. The routerDelegate determines how to rebuild the navigation stack to display the pages.

  • Back button dispatch: Tells the app to rebuild the Router to go back after the system back button has been pressed.

In more simple terms , this widget handles navigation in the following way:

  • A user interacts with the app;

  • The interaction modifies the state of the app;

  • The app state notifies the Router widget; and

  • The Router widget rebuilds the Navigator to show the new route.

The implementation of many of the functions is left to the developer. You can read more about Flutter’s complex navigation and routing system.

Navigation Best Practices

Now that you know about implementing navigation, it’s time to learn about some coding standards and best practices.

Named vs. Anonymous Routes

Navigator.push() and Navigator.pop() are used for anonymous routes, where you define the widget you want to redirect the user to.

Anonymous routes push and remove elements from the top of the stack, and the state of the underlying objects is left untouched.

Navigator.pushNamed() is used for named routes where you define a routes map containing the URL and the widget linked to it.

Third-Party Libraries

A principal rule of software development is to build upon existing solutions rather than writing everything from scratch.

To minimize repetition and work, you can choose from the thousands of Flutter third-party libraries that are available.

Also, most libraries are pre-tested and verified, so you can be confident that the solution you’ll be using will be one of the best ones. Apart from saving time, third-party libraries are also a huge cost saver.

Minimize Complexity

The end user doesn’t care about what technologies and methods you’ve used to build the app. All the user wants is a functional experience. As a developer, it’s your goal to implement the simplest solutions possible instead of more complex ones for marginal improvements. In short, you should use an elaborate system only if the resulting improvement is huge.

For Flutter navigation, using the Router widget is only recommended if your intended use perfectly matches its targeted use cases. Otherwise, for most purposes, the Navigator widget will get the job done satisfactorily without introducing a ton of unnecessary complexity.

Conclusion

This article introduced two different methods of implementing navigation in Flutter. You can either use the old, imperative, but more accessible approach of the Navigator, or use Navigator 2.0’s new, but more complex procedure, like the Router.

It mostly boils down to your particular use case and preference, and ultimately, ensuring that your chosen method provides a great experience for your users is what matters the most!

Naman Bansal.
Naman Bansal.

Written by

Written by

Naman Bansal

Naman Bansal

SHARE

SHARE

Title

Title

our newsletter

Sign up for The Pieces Post

Check out our monthly newsletter for curated tips & tricks, product updates, industry insights and more.

our newsletter

Sign up for The Pieces Post

Check out our monthly newsletter for curated tips & tricks, product updates, industry insights and more.

our newsletter

Sign up for The Pieces Post

Check out our monthly newsletter for curated tips & tricks, product updates, industry insights and more.