Intermediate · Lists, navigation and forms · topic 24 of 50 · 30 min
Navigator basics
Explanation
Screens as a stack
In Flutter, a screen is called a route. The Navigator keeps routes in a stack: opening a screen pushes a route on top; going back pops it off and shows the one below. The system back button and the AppBar's back arrow both pop the top route.
Start: [ Products ]
Tap a product: [ Products, Details(42) ]
Tap "Buy": [ Products, Details(42), Checkout ]
Back: [ Products, Details(42) ]Each route is usually a full screen: a widget with its own Scaffold. Scaffold's AppBar automatically shows a back arrow when there is a route below it to return to, so you rarely need to write a back button yourself.
Push and pop
// From the products screen:
Navigator.push(
context,
MaterialPageRoute(builder: (context) => ProductDetails(productId: 42)),
);
// Inside ProductDetails, to go back:
Navigator.pop(context);Passing data forward
The simplest and safest way to send data to a new screen is through its constructor, like any widget. Pass small things such as an id; the new screen can load the full details itself.
class ProductDetails extends StatelessWidget {
const ProductDetails({super.key, required this.productId});
final int productId;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Product $productId')),
body: Center(
child: FilledButton(
onPressed: () => Navigator.pop(context, 'added'), // return a result
child: const Text('Add to cart'),
),
),
);
}
}Getting a result back
Future<void> openDetails(BuildContext context) async {
final result = await Navigator.push<String>(
context,
MaterialPageRoute(builder: (_) => const ProductDetails(productId: 42)),
);
if (!context.mounted) return; // the screen may be gone after the await
if (result == 'added') {
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(content: Text('Added to cart')));
}
}Replacing and clearing
| Method | Effect | Typical use |
|---|---|---|
| push | Add a screen on top | Open details |
| pop | Remove the top screen, optionally with a result | Back, Cancel, Save and close |
| pushReplacement | Replace the current screen | After login: replace Login with Home |
| pushAndRemoveUntil | Push and remove screens below until a condition | After logout or order success: clear the stack |
Controlling the back button
Sometimes Back should ask first, for example on a half-filled form. Current Flutter versions use the PopScope widget (which replaced the older WillPopScope): set canPop to false while there are unsaved changes, and react in its callback by showing a "Discard changes?" dialog. Check the current API in the docs, as its callback name has changed between versions.
Common mistakes
- Using the context of a screen after an await without checking context.mounted.
- Leaving the login screen in the stack, so Back after login returns to it. Use pushReplacement.
- Passing big objects or global variables instead of constructor arguments or ids.
- Calling Navigator.pop on the first screen, which closes the app on Android.
Need help?
Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.
Try:
Watch the lesson
Navigator basics · 1/6
Screens are routes in a stack managed by the Navigator.
Prefer a video by a teacher? Find free videos on “flutter navigator push pop pass data return result pushreplacement” on YouTube ↗
In real life: Choosing a delivery address
At checkout, a food delivery app pushes an "Select address" screen. When the user taps an address, that screen pops with the chosen address as the result. The checkout screen awaits the push, checks context.mounted, and shows the chosen address. If the user presses back instead, the result is null and nothing changes.
Future<void> pickAddress(BuildContext context) async {
final address = await Navigator.push<String>(
context,
MaterialPageRoute(builder: (_) => const AddressPicker()),
);
if (address != null && context.mounted) {
// update state with the chosen address, for example via setState in a StatefulWidget
}
}
// In AddressPicker: ListTile(title: Text(a), onTap: () => Navigator.pop(context, a))Practice (0/3 done)
The stack is [Home, List, Details]. The user presses Back twice. Which screen is visible?
What does await Navigator.push(...) return if the user presses the back button?
Build a three-screen flow: Login (pushReplacement to Home), Home with a list of items, and Details that receives an item through its constructor and returns "liked" or nothing via pop. Home shows a SnackBar when an item was liked. Check that Back from Home closes the app instead of returning to Login.
Quick quiz
Read more (free)
Related help
Before this, learn
This topic builds on these. If something here feels new, open them first.
Related topics
- Routing with go_routerLists, navigation and forms · same module
- Lists with ListViewLists, navigation and forms · same module
- StacksC Programming (from zero to systems) · Data structures in C
- Navigation ComposeKotlin and Android App Development · Navigation and app architecture
- StacksData Structures and Algorithms (DSA) for interviews · Stacks, queues and linked lists
- Writing and running playbooksLinux and Red Hat (RHCSA to RHCE) · Ansible fundamentals
Questions people ask about this
Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.
What does Navigator.pop(context) do?
Answer: Removes the top screen from the stack
Back to the previous route.
Which is the recommended way to pass a product id to a details screen with Navigator.push?
Answer: The details screen's constructor
Constructor arguments are explicit and type-safe.
Which method replaces the current screen so Back cannot return to it?
Answer: pushReplacement
Used after login.
Which widget controls whether the back button can leave a screen in current Flutter?
Answer: PopScope
PopScope replaced WillPopScope.
What is a widget?Related
Answer: An immutable description of part of the UI
Flutter rebuilds the screen from widgets.
Which MaterialApp constructor works with go_router?Related
What is the time complexity of push and pop?Related
Why pass lambdas like onProductClick instead of the NavController to screens?Related
Answer: Screens stay reusable, previewable and testable
Screens should not depend on navigation details.
From Navigation Compose
Pass the quiz (60% or more) or mark this topic as done when you are ready.
Your progress is saved on this device only.
Next steps
- 2 more topics in “Lists, navigation and forms” before its exam: Routing with go_router, Forms and validation.
- Module 6 exam: Lists, navigation and forms · 13 questions · 25 min, without help. 5 topics of this module still to finish first.
- Then the next module, State management. 50 topics left in the course before the final exam.