Flutter Navigation Basics
Navigation is an essential part of Flutter application development. It allows users to move from one screen to another, open detail pages, return to previous screens, pass data between screens, and create multi-screen application flows. In Flutter, screens and pages are represented as routes, and the Navigator manages these routes using a navigation stack.
Flutter provides the Navigator API for straightforward navigation. For applications with advanced routing and deep-linking requirements, Flutter also supports the Router API and routing packages such as go_router.
1. What is Navigation in Flutter?
Navigation means moving from one screen or page of an application to another. For example, a shopping application may have a Home Screen, Product Details Screen, Cart Screen, and Checkout Screen.
In Flutter, a screen is commonly represented by a widget, and navigation between screens is handled through routes.
Example Application Flow
Home Screen
|
v
Product List
|
v
Product Details
|
v
Cart
|
v
Checkout
Flutter's Navigator maintains these routes in a stack. A new route can be pushed onto the stack, and the current route can be removed with pop().
2. What is a Route?
A route represents a screen or page that can be displayed by the application.
For example:
HomeScreen
DetailsScreen
ProfileScreen
SettingsScreen
Each of these screens can act as a route when displayed through the Navigator.
Important Concept
In Flutter, a route can be created from a widget using classes such as MaterialPageRoute or CupertinoPageRoute.
3. What is Navigator?
The Navigator widget manages a stack of routes. When a new screen is opened, Flutter generally pushes a new route onto the stack. When the user goes back, the current route is popped from the stack.
Navigation Stack Example
Initial:
[Home]
After opening Details:
[Home, Details]
After opening Profile:
[Home, Details, Profile]
After pressing Back:
[Home, Details]
After pressing Back again:
[Home]
Common Navigator Methods
| Method | Purpose |
Navigator.push() | Adds a new route to the navigation stack. |
Navigator.pop() | Removes the current route and returns to the previous route. |
Navigator.pushReplacement() | Replaces the current route with another route. |
Navigator.pushAndRemoveUntil() | Adds a route and removes previous routes according to a condition. |
Navigator.popUntil() | Removes routes until a specified condition is satisfied. |
4. Basic Navigation with Navigator.push()
The most common way to open another screen is Navigator.push().
Syntax
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const SecondScreen(),
),
);
The context identifies the location in the widget tree from which navigation is performed. MaterialPageRoute creates a Material-style route, and the builder returns the destination screen.
5. Complete Example: Navigate from One Screen to Another
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Navigation Demo',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue),
useMaterial3: true,
),
home: const 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(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const DetailsScreen(),
),
);
},
child: const Text('Open Details'),
),
),
);
}
}
class DetailsScreen extends StatelessWidget {
const DetailsScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Details Screen'),
),
body: const Center(
child: Text(
'Welcome to the Details Screen',
style: TextStyle(fontSize: 20),
),
),
);
}
}
How This Example Works
- The application starts with
HomeScreen.
- The user taps the
Open Details button.
Navigator.push() adds DetailsScreen to the navigation stack.
- The Details Screen appears.
- The AppBar back button can be used to return to the Home Screen.
6. Returning to the Previous Screen with Navigator.pop()
Use Navigator.pop() when you want to remove the current route and return to the previous screen.
Syntax
Navigator.pop(context);
Example
ElevatedButton(
onPressed: () {
Navigator.pop(context);
},
child: const Text('Go Back'),
)
If the navigation stack is:
[HomeScreen, DetailsScreen]
After calling Navigator.pop(context), the stack becomes:
[HomeScreen]
7. Complete Push and Pop Example
import 'package:flutter/material.dart';
void main() {
runApp(const MaterialApp(
debugShowCheckedModeBanner: false,
home: FirstScreen(),
));
}
class FirstScreen extends StatelessWidget {
const FirstScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('First Screen'),
),
body: Center(
child: ElevatedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const SecondScreen(),
),
);
},
child: const Text('Go to Second Screen'),
),
),
);
}
}
class SecondScreen extends StatelessWidget {
const SecondScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Second Screen'),
),
body: Center(
child: ElevatedButton(
onPressed: () {
Navigator.pop(context);
},
child: const Text('Back to First Screen'),
),
),
);
}
}
8. Navigation Flow
FirstScreen
|
| Navigator.push()
v
SecondScreen
|
| Navigator.pop()
v
FirstScreen
This is the basic navigation pattern used in many Flutter applications.
9. Navigation Using CupertinoPageRoute
Flutter also provides CupertinoPageRoute for navigation patterns associated with Cupertino-style interfaces.
Example
Navigator.push(
context,
CupertinoPageRoute(
builder: (context) => const DetailsScreen(),
),
);
You need to import the Cupertino library:
import 'package:flutter/cupertino.dart';
For Material applications, MaterialPageRoute is commonly used. For Cupertino-style applications, CupertinoPageRoute can be used.
10. Passing Data to Another Screen
Navigation often requires sending information from one screen to another. For example, when a user selects a product, the Product Details screen needs to know which product was selected.
Example Model
class Product {
final String name;
final double price;
const Product({
required this.name,
required this.price,
});
}
Details Screen
class ProductDetailsScreen extends StatelessWidget {
final Product product;
const ProductDetailsScreen({
super.key,
required this.product,
});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(product.name),
),
body: Center(
child: Text(
'Price: ₹${product.price}',
style: const TextStyle(fontSize: 20),
),
),
);
}
}
Navigate and Pass the Product
final product = Product(
name: 'Flutter Course',
price: 4999,
);
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ProductDetailsScreen(
product: product,
),
),
);
This approach passes data directly through the destination widget's constructor.
11. Returning Data from a Screen
A screen can also return a value when it is popped. This is useful when a user makes a selection on another screen.
Return Data
Navigator.pop(context, 'Selected Item');
Receive Data
final result = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const SelectionScreen(),
),
);
print(result);
Complete Example
class HomeScreen extends StatelessWidget {
const HomeScreen({super.key});
Future openSelection(BuildContext context) async {
final result = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const SelectionScreen(),
),
);
if (result != null) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Selected: $result'),
),
);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Home'),
),
body: Center(
child: ElevatedButton(
onPressed: () => openSelection(context),
child: const Text('Select Option'),
),
),
);
}
}
class SelectionScreen extends StatelessWidget {
const SelectionScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Select'),
),
body: Center(
child: ElevatedButton(
onPressed: () {
Navigator.pop(context, 'Flutter');
},
child: const Text('Select Flutter'),
),
),
);
}
}
12. Navigator.pushReplacement()
pushReplacement() replaces the current route with another route. This is useful when the user should not normally return to the previous screen using the Back action.
Example
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) => const HomeScreen(),
),
);
Common Use Case
A typical use case is moving from a login screen to the home screen after successful authentication.
Login Screen
|
| Successful Login
v
Home Screen
The login screen can be replaced so that pressing Back does not simply return the user to the login screen.
13. Navigator.pushAndRemoveUntil()
pushAndRemoveUntil() pushes a new route and removes previous routes according to a condition.
Example
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(
builder: (context) => const HomeScreen(),
),
(route) => false,
);
The condition (route) => false removes all previous routes from the stack.
Common Use Case
This can be useful after completing a login or onboarding flow when the user should start a new navigation history from the Home Screen.
14. Navigator.popUntil()
popUntil() removes routes from the stack until a specified condition is satisfied.
Example
Navigator.popUntil(
context,
(route) => route.isFirst,
);
This removes routes until the first route in the navigation stack is reached.
Example Navigation Stack
[Home, Products, Details, Checkout]
After popUntil(route.isFirst):
[Home]
15. Named Routes
Flutter supports named routes using route names such as /, /home, and /profile.
Basic Configuration
MaterialApp(
initialRoute: '/',
routes: {
'/': (context) => const HomeScreen(),
'/profile': (context) => const ProfileScreen(),
},
)
Navigate Using a Named Route
Navigator.pushNamed(
context,
'/profile',
);
Go Back
Navigator.pop(context);
Although named routes are supported, current Flutter documentation does not recommend them for most applications. For simple navigation, using Navigator with MaterialPageRoute is an option. Applications with more advanced routing and deep-linking requirements can use a routing solution such as go_router.
16. Passing Arguments with Named Routes
Named routes can receive arguments through Navigator.pushNamed().
Example
Navigator.pushNamed(
context,
'/profile',
arguments: 'Manish',
);
The destination screen can read the arguments through the current route.
final name = ModalRoute.of(context)!.settings.arguments as String;
Complete Example
class ProfileScreen extends StatelessWidget {
const ProfileScreen({super.key});
@override
Widget build(BuildContext context) {
final name = ModalRoute.of(context)!.settings.arguments as String;
return Scaffold(
appBar: AppBar(
title: const Text('Profile'),
),
body: Center(
child: Text(
'Welcome, $name',
style: const TextStyle(fontSize: 22),
),
),
);
}
}
17. Navigation with go_router
For applications that need structured routing, deep links, browser URL synchronization, or more advanced navigation behavior, a routing package such as go_router can be used.
Add go_router
flutter pub add go_router
Basic Example
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
void main() {
runApp(
MaterialApp.router(
routerConfig: router,
),
);
}
final router = GoRouter(
routes: [
GoRoute(
path: '/',
builder: (context, state) => const HomeScreen(),
),
GoRoute(
path: '/details',
builder: (context, state) => const DetailsScreen(),
),
],
);
class HomeScreen extends StatelessWidget {
const HomeScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Home'),
),
body: Center(
child: ElevatedButton(
onPressed: () {
context.go('/details');
},
child: const Text('Open Details'),
),
),
);
}
}
class DetailsScreen extends StatelessWidget {
const DetailsScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Details'),
),
body: Center(
child: ElevatedButton(
onPressed: () {
context.pop();
},
child: const Text('Go Back'),
),
),
);
}
}
18. What is Deep Linking?
A deep link is a URL that opens an application at a specific location instead of simply opening the application home screen.
Example
https://example.com/products/25
This type of link could open a product details screen for product ID 25.
Flutter supports deep linking on Android, iOS, and the web. Applications with advanced deep-linking requirements can use the Router API or a routing package such as go_router.
19. Navigator vs go_router
| Feature | Navigator | go_router |
| Basic screen navigation | Yes | Yes |
push() and pop() | Yes | Provides routing methods and navigation APIs |
| Simple applications | Suitable | Can also be used |
| Structured routing | Requires additional architecture | Supported |
| Deep linking | Possible with appropriate routing setup | Supported through routing configuration |
| Web URL synchronization | Requires Router-style navigation | Supported |
| Nested/advanced navigation | Possible | Provides routing features for structured navigation |
20. Practical Example: Login to Home Navigation
Consider an application with Login and Home screens.
Login Screen
|
| Login Successful
v
Home Screen
Code
class LoginScreen extends StatelessWidget {
const LoginScreen({super.key});
void login(BuildContext context) {
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) => const HomeScreen(),
),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Login'),
),
body: Center(
child: ElevatedButton(
onPressed: () => login(context),
child: const Text('Login'),
),
),
);
}
}
class HomeScreen extends StatelessWidget {
const HomeScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Home'),
),
body: const Center(
child: Text('Welcome to the Home Screen'),
),
);
}
}
21. Practical Example: Product List to Product Details
class Product {
final String name;
final double price;
const Product({
required this.name,
required this.price,
});
}
class ProductListScreen extends StatelessWidget {
const ProductListScreen({super.key});
final List products = const [
Product(name: 'Flutter Course', price: 4999),
Product(name: 'Dart Course', price: 3999),
Product(name: 'Mobile App Course', price: 5999),
];
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Products'),
),
body: ListView.builder(
itemCount: products.length,
itemBuilder: (context, index) {
final product = products[index];
return ListTile(
title: Text(product.name),
subtitle: Text('₹${product.price}'),
trailing: const Icon(Icons.arrow_forward_ios),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
ProductDetailsScreen(product: product),
),
);
},
);
},
),
);
}
}
class ProductDetailsScreen extends StatelessWidget {
final Product product;
const ProductDetailsScreen({
super.key,
required this.product,
});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(product.name),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
product.name,
style: const TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 10),
Text(
'Price: ₹${product.price}',
style: const TextStyle(fontSize: 20),
),
],
),
),
);
}
}
22. Navigation with Bottom Navigation
Many applications have multiple main sections such as Home, Search, Notifications, and Profile. A bottom navigation interface can be used to switch between these sections.
Example
class MainScreen extends StatefulWidget {
const MainScreen({super.key});
@override
State createState() => _MainScreenState();
}
class _MainScreenState extends State {
int selectedIndex = 0;
final List screens = const [
Center(child: Text('Home')),
Center(child: Text('Search')),
Center(child: Text('Profile')),
];
@override
Widget build(BuildContext context) {
return Scaffold(
body: screens[selectedIndex],
bottomNavigationBar: NavigationBar(
selectedIndex: selectedIndex,
onDestinationSelected: (index) {
setState(() {
selectedIndex = index;
});
},
destinations: const [
NavigationDestination(
icon: Icon(Icons.home),
label: 'Home',
),
NavigationDestination(
icon: Icon(Icons.search),
label: 'Search',
),
NavigationDestination(
icon: Icon(Icons.person),
label: 'Profile',
),
],
),
);
}
}
This pattern changes the displayed content based on the selected navigation destination. More complex applications may use separate navigation stacks for different sections.
23. Common Navigation Mistakes
Mistake 1: Forgetting MaterialPageRoute
Navigator.push(
context,
const SecondScreen(),
);
The above is incorrect because Navigator.push() expects a Route, not directly a widget.
Correct Version
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const SecondScreen(),
),
);
Mistake 2: Calling Navigator.pop() Without Checking Navigation Context
Make sure the current context belongs to a route that can be popped before performing more complex pop operations.
Mistake 3: Using Too Many Routes Without a Clear Structure
Large applications should organize navigation carefully. A routing solution can help keep complex navigation structures maintainable.
Mistake 4: Using Named Routes Automatically for Every Application
Named routes remain available, but current Flutter documentation says they are not recommended for most applications. Consider direct Navigator usage or a routing package according to the application's requirements.
24. Navigation Best Practices
- Use
Navigator.push() for straightforward screen-to-screen navigation.
- Use
Navigator.pop() to return to the previous screen.
- Use
pushReplacement() when the previous route should be replaced.
- Use
pushAndRemoveUntil() when you need to clear part or all of the navigation history.
- Pass required data explicitly through constructors when practical.
- Use typed return values when returning data from a screen.
- For advanced routing and deep linking, consider
go_router or another suitable routing approach.
- Keep navigation logic organized instead of scattering complex route logic throughout the application.
- Test navigation on different screen sizes and platforms when the application supports multiple platforms.
25. Quick Reference
| Task | Code |
| Open a new screen | Navigator.push() |
| Return to previous screen | Navigator.pop() |
| Replace current screen | Navigator.pushReplacement() |
| Clear previous routes | Navigator.pushAndRemoveUntil() |
| Go back multiple screens | Navigator.popUntil() |
| Use a named route | Navigator.pushNamed() |
| Pass data through constructor | Screen(data: value) |
| Return data | Navigator.pop(context, result) |
26. Interview Questions
Q1. What is navigation in Flutter?
Navigation is the process of moving between different screens or routes in a Flutter application.
Q2. What is Navigator?
Navigator is a Flutter widget that manages a stack of routes and provides methods for moving between them.
Q3. What does Navigator.push() do?
Navigator.push() adds a new route to the navigation stack and displays it.
Q4. What does Navigator.pop() do?
Navigator.pop() removes the current route from the navigation stack and returns to the previous route.
Q5. What is MaterialPageRoute?
MaterialPageRoute creates a Material-style route for displaying a destination widget with an appropriate transition.
Q6. What is pushReplacement()?
pushReplacement() replaces the current route with a new route.
Q7. How can data be passed between screens?
Data can be passed through the destination widget's constructor or through route arguments when using named-route based navigation.
Q8. How can data be returned from another screen?
A value can be supplied to Navigator.pop(), and the calling screen can receive that value from the Future returned by Navigator.push().
Q9. What are named routes?
Named routes identify routes using string paths such as /profile and can be navigated to with Navigator.pushNamed().
Q10. Is named-route navigation recommended for most new Flutter applications?
Current Flutter documentation says named routes are not recommended for most applications. Direct Navigator usage or a routing package such as go_router can be considered based on application requirements.
27. Practice Exercises
- Create a Home Screen and navigate to a Profile Screen.
- Add a Back button using
Navigator.pop().
- Create a Product List Screen and Product Details Screen.
- Pass product name and price to the Details Screen.
- Create a Login Screen and replace it with a Home Screen after login.
- Create a selection screen that returns a selected value to the previous screen.
- Create three screens and experiment with
push(), pop(), and pushReplacement().
- Create a simple application using
go_router with Home and Details routes.
28. Mini Project: Multi-Screen Flutter App
Create an application with the following screens:
Home
|
+-- Products
| |
| +-- Product Details
|
+-- Profile
|
+-- Settings
Recommended implementation:
- Use
Navigator.push() for basic screen transitions.
- Use constructors to pass product information.
- Use
Navigator.pop() to return to previous screens.
- Use
pushReplacement() for flows such as Login to Home.
- Experiment with
pushAndRemoveUntil() for clearing navigation history.
29. Key Takeaways
- Flutter screens are commonly represented as routes.
Navigator manages a stack of routes.
Navigator.push() opens a new route.
Navigator.pop() returns to the previous route.
MaterialPageRoute can be used for Material-style route transitions.
- Data can be passed directly to another screen through constructors.
- Data can be returned with
Navigator.pop(context, result).
pushReplacement() replaces the current route.
pushAndRemoveUntil() can remove previous navigation history.
- Named routes are supported, but current Flutter documentation does not recommend them for most applications.
- Advanced navigation and deep-linking requirements can be handled with Router-based approaches or packages such as
go_router.
30. Official Flutter Documentation
31. JustAcademy Flutter Training Resources
For additional Flutter learning and course information, visit the following resources: