Summary: in this tutorial, you’ll learn about flutter navigation to navigate from one screen to another and back.
Introduction to the Flutter navigation
Typically, apps contain multiple screens for displaying different information. For example, an E-commerce app may have a screen that displays a list of products. When the user taps a product, a new screen displays the product details.
Flutter navigation manages the screens as a stack and shows the screen that is on top of the stack.
Stack
A stack is a data structure that serves as a collection of items with two main operations:
- Push – add an item to the stack.
- Pop – remove the most recently added item from the stack.
The order in which an item is added to or removed from the stack is known as last in, first out, or LIFO
in short.
A real-life example of a stack database structure is a pile of books or a deck of cards.
Navigator
Flutter navigation works like a stack of screens. In Flutter, screens and pages are often called routes, which are widgets.
Flutter uses the Navigator object to navigate from one route to another. The Navigator object is like a stack of routes, which has two main methods:
push()
– navigate to a new route by adding a route to the stack.pop()
– remove the current route from the stack.
Flutter navigation example
The following example shows how to use the Navigator object to navigate to a new route and back:
import 'package:flutter/material.dart';
void main() {
runApp(
const MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Navigation',
home: HomeScreen(),
)
);
}
class HomeScreen extends StatelessWidget {
const HomeScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Home screen'),
),
body: Center(
child: ElevatedButton(
child: const Text('Go to the detail screen'),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => const DetailScreen()),
);
},
),
),
);
}
}
class DetailScreen extends StatelessWidget {
const DetailScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Detail screen'),
),
body: Center(
child: ElevatedButton(
onPressed: () {
Navigator.pop(context);
},
child: const Text('Go back!'),
),
),
);
}
}
Code language: Dart (dart)
How it works.
First, create two screens: HomeScreen
and DetailScreen
.
Second, the HomeScreen
has a button. When the button is pressed, call the push()
method of the Navigator object to navigate to the new route.
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => const SecondScreen()),
);
}
Code language: Dart (dart)
The push()
method accepts the first argument as the current BuildContext
and the second argument as a MaterialPageRoute
object.
The MaterialPageRoute
object specifies the new route to navigate to.
Third, the DetailScreen
also has a button. When the button is pressed, go back to the HomeScreen
by calling the pop()
method of the Navigator
object:
Navigator.pop(context);
Code language: Dart (dart)
The pop()
method accepts a context of the current BuildContext
.
Sending data to the new route
The following example illustrates how to send a string from the HomeScreen
to DetailScreen
:
import 'package:flutter/material.dart';
void main() {
runApp(const MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Navigation',
home: HomeScreen(),
));
}
class HomeScreen extends StatelessWidget {
const HomeScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Home screen'),
),
body: Center(
child: ElevatedButton(
child: const Text('Go to the detail screen'),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const DetailScreen(
message: 'This is a message from the home screen'),
),
);
},
),
),
);
}
}
class DetailScreen extends StatelessWidget {
final String message;
const DetailScreen({super.key, required this.message});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Detail screen'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(message),
ElevatedButton(
onPressed: () {
Navigator.pop(context);
},
child: const Text('Go back!'),
),
],
),
),
);
}
}
Code language: Dart (dart)
How it works.
First, add the message
property to the DetailScreen
and a constructor that accepts a string.
Second, add a Text
widget to display the message property.
Third, when calling the push()
method of the Navigator
object, pass a string to the DetailScreen
constructor.
Sending data back to the previous route
The following example illustrates how to pass data from the DetailScreen
back to the HomeScreen
:
import 'package:flutter/material.dart';
void main() {
runApp(const MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Flutter Navigation',
home: HomeScreen(),
));
}
class HomeScreen extends StatelessWidget {
const HomeScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Home screen'),
),
body: Center(
child: ElevatedButton(
child: const Text('Go to the detail screen'),
onPressed: () async {
var message = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const DetailScreen(
message: 'This is a message from the home screen'
),
),
);
print(message);
},
),
),
);
}
}
class DetailScreen extends StatelessWidget {
final String message;
const DetailScreen({super.key, required this.message});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Detail screen'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(message),
ElevatedButton(
onPressed: () {
Navigator.pop(context, 'This is a message from the Detail screen');
},
child: const Text('Go back!'),
),
],
),
),
);
}
}
Code language: Dart (dart)
First, pass a string to the second argument of the pop()
method of the Navigator object from the DetailScreen
:
onPressed: () {
Navigator.pop(
context,
'This is a message from the Detail screen'
);
}
Code language: Dart (dart)
Second, change the onPressed
function to an async
function because we’ll use the await
keyword inside it.
The push()
method returns a Future
object therefore we use the await
keyword to wait for the data future to be ready before getting the return value.
For the sake of demonstration, we print out the returned string.
onPressed: () async {
var message = await Navigator.push(
context,
MaterialPageRoute(builder: (context) => const DetailScreen(message: 'This is a message from the home screen')),
);
print(message);
}
Code language: Dart (dart)
Summary
- Use
Navigator
object to navigate from one route to another. - Use
push()
method of the Navigator object to navigate to a new route. - Use
pop()
method of the Navigator object to go back to the previous route.