In This Article

Routing and Navigation in Flutter App: Routing Made Easy

Routing and navigating are the two most common terms in mobile applications.

In Flutter, routing refers to switching between the pages or screens within the application.  It is crucial to the performance as it directly influences the user experience. This article will briefly guide Flutter navigation and routing and how to use them in Flutter.

What is Routing in Flutter?

Routing is navigating between different pages within an application. Just as you need a map to navigate a city, you need a routing system to guide users through your app.

Routing is managed through the Navigator class, part of the WidgetsApp class, typically provided by either the MaterialApp or CupertinoApp widget.

The Navigator manages routes using a stack (Last In, First Out – LIFO). This means the new route is pushed to the stack when you navigate a new page. When you want to return, the current route is popped off the stack to reveal the previous one.

Why Do We Use the Route in Flutter?

We use routes in Flutter to navigate between different screens or pages in an application. It’s essential for providing a logical flow and maintaining the flutter setstate when moving from one screen to another. It enables users to return to their previous screens and pass data between screens.

Flutter Navigator

The Navigator is a Flutter widget that controls managing a stack of Route objects and facilitates screen switching. You may think of it as a data structure stack where you can push new routes onto it and pop existing ones off of it.

To navigate to a new screen, you can use the Navigator.push() method. This method pushes a new route onto the stack and transitions to it.

Navigator.push(
  context,
  MaterialPageRoute(builder: (context) => SecondScreen()),
);

How To Do Routing in Flutter

There are mainly two main methods of routing in Flutter: Basic (or Stack) Routing and Named Routing. Let’s delve into both types in more detail:

Stack Routing (basic)

Stack routing, or basic routing as it’s sometimes known, is the most straightforward approach to navigation in Flutter. This involves using Navigator’s push and pop methods to add or remove routes (screens) from the stack.

Push: When you want to navigate from one screen to another, you “push” a route to the Navigator’s stack. This new route becomes active, and its associated screen is displayed. Here’s an example:

Navigator. push(
  context,
  MaterialPageRoute(builder: (context) => NewScreen()),
);

In this example, MaterialPageRoute is a route that uses a platform-adaptive transition. It’s common in Android and iOS applications.

Pop: you have to “pop” the current route from the Navigator’s stack in case you want to return to the previous screen and dismiss the current one.  Here’s how you can do it:

Navigator.pop(context);

When you call ‘pop,’ the topmost item, i.e., the stack’s current route, gets removed, and the previous becomes active again.

Named Routing

It is another form of navigation that makes it easier to manage routes in larger applications. Named routing allows you to refer to your routes using predefined string identifiers (or ‘names’) rather than directly dealing with the routes themselves.

In named routing, navigation becomes a matter of calling ‘Navigator.pushNamed( )’, passing the name of the route you want to navigate.

For example, to navigate to the details screen, you’d do the following:

Navigator.pushNamed(context, '/details');

And to go back to the previous screen, you need to pop the current route, just like with basic routing:

Navigator.pop(context);

Passing Arguments to Named Routes

In a Flutter application, sometimes you want to share data from one screen to another. This is often referred to as “passing arguments” to a route.

Here is what you can do:

  • Define your routes

First, you need to tell Flutter about your “routes.” Each route needs a name. We do this in the ‘MaterialApp’ widget. Here, we have two routes(or screens): a HomeScreen and a ‘DetailScreen.’

MaterialApp(
  initialRoute: '/',
  routes: {
    '/': (context) => HomeScreen(),
    '/details': (context) => DetailScreen(),
  },
);
  • Navigate and Pass Arguments

If you want to go to the details route using the named route, send some data there, i.e., ‘Hello from HomeScreen!’. You can do this:

Navigator.pushNamed(
  context, 
  '/details',
  arguments: 'Hello from HomeScreen!',
);
  • Receive the Arguments

In the DetailScreen, you can accept and use what you brought or sent from the previous screen:

class DetailScreen extends StatelessWidget {
 const DetailScreen({super.key});

 @override
 Widget build(BuildContext context) {
   // Accept what you brought
   final String message = ModalRoute.of(context)!.settings.arguments as String;

   // Use it in your room (or screen)
   return Scaffold(
     appBar: AppBar(title: const Text('Detail Screen')),
     body: Center(child: Text(message)),
   );
 }
}

So, that’s it! You’re now able to pass arguments to named routes in Flutter. You’ve learned to bring something when navigating from one screen to another. Enjoy!

Which Routing is Best in Flutter

You must have the right flutter router package in place to get the maximum out of flutter. Following are some top routing packages at your service:

  • Auto-route: This package provides a strongly typed routing setup, which helps avoid errors and improves code readability. It also supports route guards, path parameters, and much more.
  • Go-router:  Go-Router is a declarative routing package for Flutter that simplifies screen navigation using URLs. It allows you to handle deep links and manage various navigation scenarios in your app. It provides a convenient and straightforward API for navigating different screens based on URLs.
  • Shelf-router: Shelf is a modular, middleware-based web server framework for Dart programming language. It allows you to define routes for your Shelf application in a way similar to how you would do it in an Express application for Node.js.
  • flutter_modular: A modular and easy-to-use package for routing. It also provides dependency injection and is widely used for large applications.

Best Flutter Routing Package: Flutter Developer’s Choice

As per the developer’s community, go_router’  is the most efficient package with advanced routing capabilities. It supports both Material and Cupertino apps.

go_router  can display multiple screens (sub-routes) for the destination.   It also provides redirection support which means you can re-direct the user to different URLs according to requirements.

Flutter Routing Best Practices

You can adhere to several recommended practices regarding routing in Flutter to create an app that is well-organized and easy to maintain. Several crucial factors are listed below:

  • Route generator: Implement a function for creating routes based on their names in the route generator. Each identified route’s relevant Widget should be created and returned by this function. You can write your own custom logic for handling erroneous or unknown routes.
  • Use the navigator class in Flutter: Flutter provides the Flutter Navigator class to manage routing and navigation within your app. It allows you to push new routes onto the stack, pop routes off it, and handle transitions between screens.
  • State management and routing: Consider integrating a futter setstate management solution like Provider, Riverpod, or Flutter Bloc with your routing. This helps separate your UI from the business logic and keeps your codebase organized and scalable.

Flutter Nested Routing

It is a powerful feature in Flutter that enables it to handle multiple independent navigators, each with its distinctive navigation stack. It is constructive when you want to keep the state of various screens while carrying navigation within them.

Here is how you can set up nesting routing in a flutter:

In this example, MyHomePage contains a BottomNavigationBar with two items. Each item has its own Navigator, defined in _buildOffstageNavigator. When a tab is selected in the BottomNavigationBar, _onItemSelected is called, and the _currentIndex variable is updated, causing a rebuild of the MyHomePage widget. Then, in _buildOffstageNavigator, the Offstage widget makes the current tab’s Navigator visible and all others invisible.

import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
 const MyApp({super.key});

 @override
 Widget build(BuildContext context) {
   return const MaterialApp(home: MyHomePage());
 }
}

class MyHomePage extends StatefulWidget {
 const MyHomePage({super.key});

 @override
 State createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
 int _currentIndex = 0;
 final _navigatorKeys = [
   GlobalKey<NavigatorState>(),
   GlobalKey<NavigatorState>(),
 ];

 @override
 Widget build(BuildContext context) {
   return Scaffold(
     body: Stack(
       children: [
         _buildOffstageNavigator(0),
         _buildOffstageNavigator(1),
       ],
     ),
     bottomNavigationBar: BottomNavigationBar(
       currentIndex: _currentIndex,
       items: const [
         BottomNavigationBarItem(icon: Icon(Icons.home), label: 'Home'),
         BottomNavigationBarItem(icon: Icon(Icons.person), label: 'Profile'),
       ],
       onTap: _onItemSelected,
     ),
   );
 }

 Widget _buildOffstageNavigator(int index) {
   return Offstage(
     offstage: _currentIndex != index,
     child: Navigator(
       key: _navigatorKeys[index],
       onGenerateRoute: (routeSettings) {
         return MaterialPageRoute<void>(
           builder: (_) {
             return _currentIndex == 0
                 ? const HomeScreen()
                 : const ProfileScreen();
           },
         );
       },
     ),
   );
 }

 void _onItemSelected(int index) {
   _currentIndex = index;
   setState(() {});
 }
}

class HomeScreen extends StatelessWidget {
 const HomeScreen({Key? key}) : super(key: key);

 @override
 Widget build(BuildContext context) {
   return const Center(child: Text('Home Screen'));
 }
}

class ProfileScreen extends StatelessWidget {
 const ProfileScreen({Key? key}) : super(key: key);

 @override
 Widget build(BuildContext context) {
   return const Center(child: Text('Profile Screen'));
 }
}

 

Here is a visual representation of the above-mentioned code:

Conclusion

In Flutter, routing and navigation are the foundation of any application, orchestrating the user journey across several displays. They improve usability by enabling smooth transitions but also support maintaining an organized and understandable user interface, enhancing the entire user experience.

Picture of Bashir Ahmad

Bashir Ahmad

When not savoring tortillas, Bashir captivates readers with helpful and engaging prose, driven by his passion for Flutter and a dedication to providing value.

Share on:

Leave a Comment

Your email address will not be published. Required fields are marked *