What is the purpose of the Navigator class in Flutter?
#navigation#navigator#routing#screens
Answer
Overview
The
text
NavigatorCore Purpose
Navigator serves three main purposes:
- Manages Screen Stack - Maintains a stack of Route objects representing screens
- Provides Navigation Methods - Offers APIs like ,text
push(),textpop()to control navigationtextpushReplacement() - Handles Transitions - Manages platform-specific animations when moving between screens
How Navigator Works
Navigator uses a stack data structure (LIFO - Last In, First Out):
text┌─────────────────┐ │ Details Screen │ ← Top of stack (current screen) ├─────────────────┤ │ List Screen │ ├─────────────────┤ │ Home Screen │ ← Bottom of stack (root screen) └─────────────────┘
Basic Navigation Methods
1. Push - Add Screen to Stack
dart// Add a new screen on top of the current one Navigator.push( context, MaterialPageRoute(builder: (context) => DetailScreen()), );
2. Pop - Remove Current Screen
dart// Remove the current screen and return to previous one Navigator.pop(context); // Pop with a result value Navigator.pop(context, 'Selected Value');
3. PushNamed - Navigate Using Route Name
dart// Navigate using predefined route names Navigator.pushNamed(context, '/details'); // With arguments Navigator.pushNamed( context, '/details', arguments: {'id': 123, 'title': 'Product'}, );
Advanced Navigation Methods
1. PushReplacement - Replace Current Screen
dart// Replace current screen without adding to stack Navigator.pushReplacement( context, MaterialPageRoute(builder: (context) => HomeScreen()), ); // Example: After login, replace login screen with home ElevatedButton( onPressed: () async { final success = await login(); if (success) { Navigator.pushReplacement( context, MaterialPageRoute(builder: (context) => HomeScreen()), ); } }, child: Text('Login'), )
2. PushAndRemoveUntil - Clear Stack and Push
dart// Clear all previous routes and add new one Navigator.pushAndRemoveUntil( context, MaterialPageRoute(builder: (context) => HomeScreen()), (route) => false, // Remove all previous routes ); // Keep routes until a condition is met Navigator.pushAndRemoveUntil( context, MaterialPageRoute(builder: (context) => ProfileScreen()), ModalRoute.withName('/home'), // Keep routes up to /home );
3. PopUntil - Pop Multiple Screens
dart// Pop until reaching a specific route Navigator.popUntil(context, ModalRoute.withName('/home')); // Pop until condition is met Navigator.popUntil(context, (route) => route.isFirst);
4. CanPop - Check if Pop is Possible
dart// Check if there are routes to pop if (Navigator.canPop(context)) { Navigator.pop(context); } else { // Already at root, maybe show exit dialog showExitDialog(); }
5. MaybePop - Pop Only if Possible
dart// Safely pop without checking canPop await Navigator.maybePop(context); // With result await Navigator.maybePop(context, 'result');
Complete Navigation Example
dartimport 'package:flutter/material.dart'; void main() => runApp(MyApp()); class MyApp extends StatelessWidget { Widget build(BuildContext context) { return MaterialApp( title: 'Navigator Demo', initialRoute: '/', routes: { '/': (context) => HomeScreen(), '/list': (context) => ListScreen(), '/details': (context) => DetailScreen(), }, ); } } class HomeScreen extends StatelessWidget { Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Home')), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ ElevatedButton( onPressed: () { // Push to list screen Navigator.push( context, MaterialPageRoute(builder: (context) => ListScreen()), ); }, child: Text('Go to List (Push)'), ), SizedBox(height: 10), ElevatedButton( onPressed: () { // Push using named route Navigator.pushNamed(context, '/list'); }, child: Text('Go to List (Named)'), ), ], ), ), ); } } class ListScreen extends StatelessWidget { Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('List')), body: ListView.builder( itemCount: 10, itemBuilder: (context, index) { return ListTile( title: Text('Item $index'), onTap: () async { // Navigate to details and wait for result final result = await Navigator.push( context, MaterialPageRoute( builder: (context) => DetailScreen(id: index), ), ); if (result != null) { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text('Result: $result')), ); } }, ); }, ), ); } } class DetailScreen extends StatelessWidget { final int id; const DetailScreen({Key? key, this.id = 0}) : super(key: key); Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Details #$id')), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text('Detail Screen for Item $id'), SizedBox(height: 20), ElevatedButton( onPressed: () { // Pop with result Navigator.pop(context, 'Liked Item $id'); }, child: Text('Like and Go Back'), ), SizedBox(height: 10), ElevatedButton( onPressed: () { // Pop to home screen Navigator.popUntil(context, ModalRoute.withName('/')); }, child: Text('Back to Home'), ), SizedBox(height: 10), ElevatedButton( onPressed: () { // Replace with another screen Navigator.pushReplacement( context, MaterialPageRoute( builder: (context) => DetailScreen(id: id + 1), ), ); }, child: Text('Next Detail'), ), ], ), ), ); } }
Navigator with Custom Transitions
dartNavigator.push( context, PageRouteBuilder( pageBuilder: (context, animation, secondaryAnimation) => DetailScreen(), transitionsBuilder: (context, animation, secondaryAnimation, child) { const begin = Offset(1.0, 0.0); const end = Offset.zero; const curve = Curves.easeInOut; var tween = Tween(begin: begin, end: end).chain( CurveTween(curve: curve), ); return SlideTransition( position: animation.drive(tween), child: child, ); }, ), );
Navigator API Overview
| Method | Purpose | Use Case |
|---|---|---|
text | Add screen to stack | View details, open form |
text | Remove current screen | Go back, close dialog |
text | Navigate by route name | Centralized routing |
text | Replace current screen | After login/logout |
text | Clear stack and navigate | Logout, complete flow |
text | Pop multiple screens | Back to specific screen |
text | Check if pop possible | Prevent exit errors |
text | Pop if possible | Safe back navigation |
text | Pop and push in one call | Quick replacement |
Two Navigation Styles
Imperative API (Navigator 1.0)
dart// Direct method calls Navigator.push(context, route); Navigator.pop(context);
Declarative API (Navigator 2.0)
dart// Define pages based on app state Navigator( pages: [ MaterialPage(child: HomeScreen()), if (showDetails) MaterialPage(child: DetailScreen()), ], onPopPage: (route, result) => route.didPop(result), );
Best Practices
- Use named routes for apps with many screens
- Always check before popping to avoid errorstext
canPop() - Use after authentication to prevent going backtext
pushReplacement() - Return data with when collecting user inputtext
pop() - Use for logout flowstext
pushAndRemoveUntil() - Implement custom transitions for better UX
- Handle back button properly on Android with text
PopScope
Common Use Cases
Login Flow
dart// After successful login, don't allow back to login screen Navigator.pushAndRemoveUntil( context, MaterialPageRoute(builder: (context) => HomeScreen()), (route) => false, );
Logout Flow
dart// Clear all screens and go to login Navigator.pushAndRemoveUntil( context, MaterialPageRoute(builder: (context) => LoginScreen()), (route) => false, );
Multi-step Form
dart// Step 1 → Step 2 → Step 3 → Success // On success, go back to home Navigator.pushAndRemoveUntil( context, MaterialPageRoute(builder: (context) => HomeScreen()), ModalRoute.withName('/'), );