Welcome, Flutter enthusiasts!
Flutter has quickly become a go-to solution for creating beautiful and efficient mobile applications. A critical aspect often overlooked during the app development is navigation—specifically, Flutter routing.
Let's dive into the nuts and bolts of routing in a Flutter application to ensure users enjoy seamless navigation between screens.
For those born in a pre-Flutter universe, routing might seem like a sophisticated term borrowed from your networking knowledge. However, within the Flutter realm, routing refers to how an application transitions between screens or pages, following the principles of navigation and routing. Routes are an integral part of any application for structuring pages.
Flutter utilizes a stack for managing routes, where a new route is pushed onto it while it navigates to a new screen, and the current route is popped off when navigating back to the previous route. This navigator 2.0 technique creates a historical sequence of visited pages that can be navigated in reverse, capturing the essence of chronological navigation.
1// Flutter routing example 2Navigator.push( 3 context, 4 MaterialPageRoute(builder: (context) => SecondScreen()), 5);
For instance, when the user taps the action in the lines of code above, a new route representing the second screen gets piled on top of the first route.
Navigating between different screens is like a journey. In Flutter, each screen is a new journey stop, defined as a route. Hence, while navigating to a new screen, you're pushing a new route on top of the navigation stack, and during a return journey, you're popping off the current route.
This concept is encapsulated by the term "PageRoute", where each route is an independent "Widget". Navigation in Flutter is handled using the Navigator widget. Each screen in the Flutter app is a new PageRoute and is put on the stack maintained by the Navigator widget. Like a stack, the first route is also put in the first, home page or first screen. Then as the user navigates around your app, new routes are put on top of the stack while the current route is always the top-most element.
The easiest way to visualize this is to think of your app as a stack of paper, where each paper represents a new route. As we navigate with the router widget through the app, we keep piling new sheets or new screens on top. When we need to return, we remove the topmost page, revealing the one below it.
Let's look at an example using MaterialPageRoute class and how it maps with Flutter routing graph:
1Navigator.push( 2 context, 3 MaterialPageRoute(builder: (context) => SecondScreen()), 4);
In this code snippet, we push the second screen onto the navigation stack with MaterialPageRoute. Here, SecondScreen() is a new widget where we want to navigate. After navigating the SecondScreen(), the stack looks like this - [FirstScreen(), SecondScreen()].
Flutter provides a simple and effective way to manage routes through the Flutter router, or Router API.
The Router API is the newer, more flexible API announced with Navigator 2.0. You can control your app's navigation stack with its declarative routing package. This significantly improved over the traditional Navigator, where Flutter controlled the navigation stack.
When using the Router API, you define a Router Widget in the widget tree. This widget consumes the RouteInformationProvider and passes the route information to the RouterDelegate. The RouterDelegate then takes the route information and pushes new pages onto the stack.
You have two screens, FirstScreen() and SecondScreen(). To navigate from FirstScreen to SecondScreen, an example could look like:
1// Defining routes 2void main() { 3 runApp(MaterialApp( 4 initialRoute: '/', 5 routes: <String, WidgetBuilder>{ 6 '/': (BuildContext context) => FirstScreen(), 7 '/second': (BuildContext context) => SecondScreen(), 8 }, 9 )); 10}
In this example, the initialRoute specifies the default route when the application launches. The routes property defines available named routes and the widgets to construct when navigating to those routes.
To navigate to SecondScreen() from FirstScreen():
1Navigator.pushNamed(context, '/second');
With the Router API, you can manage navigation and routing in your Flutter project in a much cleaner, more organized way.
When applications start growing, managing individual routes can become nearly impossible. The Flutter solution to this problem is named routes. A named route is a route associated with a string value. This is especially handy when passing information from one screen to another.
Let's say there are two screens in your Flutter project, the FirstScreen and SecondScreen. Navigating between these screens using Named Routes would look something like this:
1void main() { 2 runApp(MaterialApp( 3 initialRoute: '/', 4 routes: { 5 '/': (BuildContext context) => FirstScreen(), 6 '/second': (BuildContext context) => SecondScreen(), 7 }, 8 )); 9}
Here, the routes property defines the mappings from named routes to WidgetBuilder. When navigating, you can now use the Navigator.pushNamed() function with the named route as a string:
1Navigator.pushNamed(context, '/second');
This method makes pushing a new route onto the stack convenient when you have multiple routes in your application. What's more, named routes can have arguments passed to them! This helps avoid complications and code duplication in larger apps.
Also, as your application grows and begins to consist of a large number of pages, handling each route individually can get cumbersome. This is where named routes become our lifesavers!
Suppose you want to link particular parts of your app to a web URL or launch your app from a link in an SMS or email. In that case, you need to consider deep links.
Deep linking is using hyperlinks that take users directly to specific content within an application. In Flutter, these deep links are used as URIs that allow users to navigate to a particular location (the deep link navigates to a location).
Flutter's Navigator 2.0, combined with the Router API, provides a robust solution to handle deep links in a straightforward declarative manner. With it, handling deep links becomes just as easy as defining a new route. You can process the deep link to extract the required parameters and navigate the user to the correct screen.
For example, to handle deep links, you would follow this process:
Specify a URI in the address bar of your browser.
Router gets the updated URL, and the RouterDelegate parses the URL.
The parsed URI decides the new screen where the deep link navigates.
Keep in mind that correctly handling deep links is critical to creating user-friendly mobile applications. It also becomes pivotal for mobile marketing, as you can track campaign performance by attributing a unique deep link to each campaign.
When our application grows with an increasing number of routes, catching complex deep linking scenarios, and maintaining code readability can become quite challenging. But here's the good news: we have the GoRouter package to make these things more manageable!
The GoRouter is an advanced, yet simple declarative routing package for Flutter. It extends the functionality of the new Router API and provides additional features like path parameters, query parameters, and seamlessly handling sub routes. This gives it superior handling power for complex deep-linking situations compared to the basic Router API.
The routing package integrates with Flutter's Router API and Navigator 2.0 to provide a concise and coherent routing solution. It builds on declarative navigation and named routes to offer a simple yet effective routing solution.
In saying this, let's illustrate a simplistic approach to using GoRouter in your Flutter project:
1final router = GoRouter( 2 // specify initial route or home page 3 initialGoRoute: '/', 4 // the route paths 5 routes: [ 6 GoRoute( 7 path: '/', 8 builder: (context, state) => HomePage()), 9 GoRoute( 10 path: '/second', 11 builder: (context, state) => SecondPage()), 12 ], 13);
In this example, we have set up the GoRouter routes for '/second'. To navigate to /second, we would still utilize the same method as before:
1router.go('/second');
Or, using GoRouter's magic for deep linking:
1router.go('/second?id=42');
In this example, the second route will access the ID through the state's parameters by calling state.params['id'].
Managing routes is quite crucial in a Flutter application. Here are some best practices that you should adhere to for efficient Flutter Routes management:
Use Named Routes: Whenever possible, use named routes. It makes your code more readable and easy to manage.
Avoid Deep Nesting: If your app has a deep nesting hierarchy of routes, it's better to flatten it as much as possible. This will simplify your routing logic and make your app more performant.
Use Navigator 2.0: The Flutter team has significantly improved the navigation system with Navigator 2.0. It offers more flexibility and better handles complex navigation cases.
GoRouter vs. Handled Schema: GoRouter is a powerful tool for managing complex deep linking situations in your Flutter apps. It integrates with Navigator 2.0, providing advanced routing techniques that extend the system's functionality.
Work with Query Parameters: Query parameters can be utilized for handling complex routing scenarios in the Flutter application.
Test Your Routes: Always test your navigation patterns, including deep linking behavior. This ensures that any links associated with your application take the user to the correct location.
Following the above best practices, flawlessly handling routes on multiple screens across different platforms gives the user a seamless journey through your application.
We explored the Flutter Routing systems, from basic concepts to advanced techniques and from navigating between two screens to handling complex deep linking scenarios. Along the way, we encountered some best practices that will help us create intuitive and user-friendly Flutter applications.
Yet, understanding Flutter's routing is a continuous learning process since Flutter is frequently updated with new features and improvements. So always keep an eye on the official Flutter API Documentation.
Tired of manually designing screens, coding on weekends, and technical debt? Let DhiWise handle it for you!
You can build an e-commerce store, healthcare app, portfolio, blogging website, social media or admin panel right away. Use our library of 40+ pre-built free templates to create your first application using DhiWise.