When you start develop more screens of your Flutter Apps, the situation just become more complicated. For instance, you will need to manage multiple app screens. And then you need to design how users navigate between them. This is where navigation and routing play an important role. In this tutorial, I will share with you how to handle screen transitions and manage routes in Flutter. Such that your app can be working more dynamic and user-friendly.
How the navigation in Flutter work?
In Flutter, navigation refers to switching between different screens or pages. Just like when you travel, you need a guidance to tell you which direction should you go. A good navigation design can retain the mobile apps users as they won’t be confused. On the other side, routing is how you define and manage these screen transitions. For example, a typical Flutter app starts with a single screen, but you can have multiple screens that users can navigate to using routes.
Types of Navigation
Let’s first talk about Flutter navigation. By default, it provides several ways to handle navigation. The first one should be Push and Pop Navigation. It’s a Stack-based navigation where you push a new screen onto the stack and pop it off after it done.
Another one is called Flutter named routes. It provide a mechanism to define and navigate between two or more screens using simple string identifier. When should you use this approach? Well, usually when you want to manage multiple pages in a more organized way rather than passing around widget references. By three simple steps, you can use named route:
Step 1: You define a set of routes (screens) in your MaterialApp
widget using the routes
property.
Step 2: You give each route a name (a string key).
Step 3: To navigate between these screens, you use the Navigator.pushNamed()
method.
For now, we’ll focus on the basic stack navigation and named routes. These approach will be more appropriate for beginner.
Simple Flutter Navigation Example
Let’s start with a simple app. Assume the apps has two screens, and the user can navigate from the first screen to the second using a button. Pretty simple and straightforward.
Defining Two Screens in Flutter
In your lib/main.dart
, let’s define two screens: HomeScreen and SecondScreen.
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: HomeScreen(),
);
}
}
class HomeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Home Screen'),
),
body: Center(
child: ElevatedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => SecondScreen()),
);
},
child: Text('Go to Second Screen'),
),
),
);
}
}
class SecondScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Second Screen'),
),
body: Center(
child: ElevatedButton(
onPressed: () {
Navigator.pop(context);
},
child: Text('Go Back to Home Screen'),
),
),
);
}
}
Code Explanation:
HomeScreen: This is the first screen that shows a button labeled “Go to Second Screen.” When the user presses the button, it navigates to the SecondScreen.
Navigator.push(): Pushes a new screen (or route) onto the navigation stack.
SecondScreen: A simple screen with a button that uses Navigator.pop()
to return to the previous screen.
You can try it in the apps simulator. When you run and tap “Go to Second Screen,” it pushes the SecondScreen onto the stack. Tapping the “Go Back to Home Screen” button pops the screen off the stack and returns you to the HomeScreen.
Navigating Using Named Routes
While the method we just used is simple, it can get messy if you have multiple screens. This is where named routes come in. Named routes make it easier to navigate between multiple screens in a structured way.
1. Defining Named Routes
Let’s modify our app to use named routes. First, define the routes in the MaterialApp
widget:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
initialRoute: '/',
routes: {
'/': (context) => HomeScreen(),
'/second': (context) => SecondScreen(),
},
);
}
}
class HomeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Home Screen'),
),
body: Center(
child: ElevatedButton(
onPressed: () {
Navigator.pushNamed(context, '/second');
},
child: Text('Go to Second Screen'),
),
),
);
}
}
class SecondScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Second Screen'),
),
body: Center(
child: ElevatedButton(
onPressed: () {
Navigator.pop(context);
},
child: Text('Go Back to Home Screen'),
),
),
);
}
}
2. Understanding Named Routes
- initialRoute: This specifies the first route (screen) the app should load. In our case, it’s
'/'
, which corresponds to the HomeScreen. - routes: A map of route names to their corresponding widgets. Here, we have two routes:
'/'
for HomeScreen and'/second'
for SecondScreen. - Navigator.pushNamed(): Navigates to the route associated with the provided name (
'/second'
in this case).
Using named routes helps maintain a cleaner and more scalable codebase, especially when your app grows beyond two or three screens.
Passing Data Between Screens
Navigating between screens is useful, but often you’ll need to pass data from one screen to another. Flutter makes this easy with both the Navigator.push()
method and named routes.
Let’s say we want to pass a message from the HomeScreen to the SecondScreen.
1. Modifying the SecondScreen to Accept Data
Update SecondScreen to accept a message as an argument:
class SecondScreen extends StatelessWidget {
final String message;
SecondScreen({required this.message});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Second Screen'),
),
body: Center(
child: Text(message),
),
);
}
}
2. Passing Data with Navigator.push()
Now, modify HomeScreen to pass data when navigating to SecondScreen.
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => SecondScreen(message: 'Hello from HomeScreen!'),
),
);
When you press the button on the HomeScreen, it navigates to SecondScreen and displays the passed message.
Passing Data with Named Routes
You can also pass data using named routes by leveraging the arguments
property.
1. Modify Named Route to Accept Arguments
Update SecondScreen to handle data passed via named routes:
class SecondScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
final String message = ModalRoute.of(context)!.settings.arguments as String;
return Scaffold(
appBar: AppBar(
title: Text('Second Screen'),
),
body: Center(
child: Text(message),
),
);
}
}
2. Pass Data via Navigator.pushNamed()
In HomeScreen, pass data when navigating to the named route:
Navigator.pushNamed(
context,
'/second',
arguments: 'Hello from HomeScreen!',
);
Now, the SecondScreen retrieves the message from the arguments property and displays it.
Navigating with Pop Until and Push Replacement
Flutter provides more advanced navigation options like Navigator.popUntil()
and Navigator.pushReplacement()
. These come in handy when you need to manage the navigation stack efficiently.
1. Using popUntil to Return to a Specific Screen
Navigator.popUntil()
pops multiple screens off the stack until a specific condition is met. This is useful when you want to return to the HomeScreen after navigating through multiple screens.
Navigator.popUntil(context, ModalRoute.withName('/'));
This pops all screens off the stack until it returns to the HomeScreen.
2. Using pushReplacement to Replace the Current Screen
Navigator.pushReplacement()
replaces the current screen in the stack with a new one. This is useful if you don’t want the user to go back to the previous screen.
Navigator.pushReplacement(
context,
MaterialPageRoute(builder: (context) => NewScreen()),
);
In this case, NewScreen replaces the current screen, and the user can’t navigate back to it.
Conclusion: Mastering Navigation in Flutter
You’ve now learned the fundamentals of navigation and routing in Flutter. Whether you’re managing simple transitions or passing data between screens, navigation is a crucial aspect of building user-friendly apps. By mastering stack-based navigation, named routes, and advanced techniques like data passing and route management, you’re well on your way to creating apps that provide a smooth, dynamic experience for users.
In the next tutorial, we’ll explore how to integrate Firebase into your Flutter app for backend support. Keep building, and happy coding!