Named Routes in Flutter
Named Routes in Flutter provide a way to navigate between screens using unique string names instead of directly creating a route with a screen widget every time. For example, instead of repeatedly writing a complete MaterialPageRoute, you can define a route such as /details and navigate to it using Navigator.pushNamed().
Named routes are configured through the routes property of MaterialApp or CupertinoApp. Flutter's current documentation notes that named routes are not recommended for most new applications, especially when advanced deep-linking or routing requirements exist. For such applications, Navigator with explicit routes or a routing package such as go_router may be more suitable.
1. What Are Named Routes?
A named route is a route identified by a unique string. Each route name is mapped to a screen widget in the application's routing configuration.
For example:
'/': HomeScreen
'/login': LoginScreen
'/profile': ProfileScreen
'/settings': SettingsScreen
'/details': DetailsScreen
Instead of directly creating a route every time, you can navigate by using the route name:
Navigator.pushNamed(context, '/details');
Flutter looks up the route name and builds the screen associated with that name.
2. Why Use Named Routes?
Named routes can make navigation code easier to read when several parts of an application need to navigate to the same screen.
Without Named Routes
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const ProfileScreen(),
),
);
With Named Routes
Navigator.pushNamed(context, '/profile');
With named routes, the route configuration is maintained in one place, while individual screens only need to use the route name.
3. Important Properties for Named Routes
| Property or Method | Purpose |
|---|
routes | Defines a map of route names and screen builders. |
initialRoute | Specifies the first named route displayed when the application starts. |
home | Defines the initial screen directly. |
Navigator.pushNamed() | Opens a screen using its route name. |
Navigator.pop() | Closes the current route and returns to the previous screen. |
Navigator.pushNamedAndRemoveUntil() | Opens a named route and removes previous routes according to a condition. |
onGenerateRoute | Creates routes dynamically when navigation requires custom logic. |
onUnknownRoute | Handles route names that cannot be resolved. |
4. Defining Named Routes in MaterialApp
Named routes are defined using the routes property of MaterialApp. The property accepts a map where each key is a route name and each value is a widget builder.
Basic Syntax
MaterialApp(
initialRoute: '/',
routes: {
'/': (context) => const HomeScreen(),
'/details': (context) => const DetailsScreen(),
},
);
In this example:
'/' represents the Home Screen.
'/details' represents the Details Screen.
initialRoute: '/' tells Flutter to start with the Home Screen.
- Each route value is a function that returns a widget.
5. Route Naming Conventions
Flutter route names commonly use a path-like format beginning with a forward slash.
| Route Name | Example Screen |
|---|
/ | Home Screen |
/login | Login Screen |
/register | Registration Screen |
/profile | Profile Screen |
/products | Product List Screen |
/product-details | Product Details Screen |
/settings | Settings Screen |
Use meaningful and consistent route names so that the purpose of each route is easy to understand.
6. Creating a Basic Named Routes Application
The following application defines two named routes: Home Screen and Second Screen.
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: 'Named Routes Demo',
initialRoute: '/',
routes: {
'/': (context) => const HomeScreen(),
'/second': (context) => const SecondScreen(),
},
);
}
}
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.pushNamed(context, '/second');
},
child: const Text('Open 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('Go Back'),
),
),
);
}
}
How the Example Works
- The application starts with the route
/.
- The route
/ displays HomeScreen.
- The user taps the Open Second Screen button.
Navigator.pushNamed(context, '/second') opens the second screen.
- The route
/second displays SecondScreen.
- The user taps Go Back.
Navigator.pop(context) removes the current route.
- The user returns to
HomeScreen.
7. Understanding the routes Property
The routes property contains a map of route names and widget builders.
routes: {
'/': (context) => const HomeScreen(),
'/profile': (context) => const ProfileScreen(),
'/settings': (context) => const SettingsScreen(),
}
The map can be understood as follows:
| Key | Value |
|---|
'/' | Builds HomeScreen |
'/profile' | Builds ProfileScreen |
'/settings' | Builds SettingsScreen |
When a named route is requested, Flutter searches the route map for the matching name.
8. Using initialRoute
The initialRoute property determines which named route is displayed when the application starts.
MaterialApp(
initialRoute: '/login',
routes: {
'/login': (context) => const LoginScreen(),
'/home': (context) => const HomeScreen(),
},
);
In this example, the application starts with LoginScreen because initialRoute is set to /login.
Important Note
When using initialRoute, do not also define a home property for the same application configuration. Use either a direct home screen or an initial named route.
9. Difference Between home and initialRoute
home | initialRoute |
|---|
| Accepts a widget directly. | Accepts a route name. |
| Useful for simple applications. | Useful when starting with a named route. |
Example: home: const HomeScreen() | Example: initialRoute: '/login' |
Automatically represents the default / route. | Must refer to a route configured in the routing system. |
10. Navigating with Navigator.pushNamed()
The Navigator.pushNamed() method opens a route using its string name.
Syntax
Navigator.pushNamed(
context,
'/profile',
);
Example
ElevatedButton(
onPressed: () {
Navigator.pushNamed(context, '/profile');
},
child: const Text('Open Profile'),
)
Flutter finds the route named /profile in the configured route map and displays the associated screen.
11. Closing a Named Route with Navigator.pop()
Named routes are still managed by the Navigator stack. Therefore, the current named route can be closed using Navigator.pop().
ElevatedButton(
onPressed: () {
Navigator.pop(context);
},
child: const Text('Go Back'),
)
The route name does not need to be provided when going back. The Navigator removes the current route and reveals the previous route.
12. Named Routes Navigation Flow
Home Screen
|
| Navigator.pushNamed(context, '/profile')
↓
Profile Screen
|
| Navigator.pushNamed(context, '/settings')
↓
Settings Screen
|
| Navigator.pop(context)
↓
Profile Screen
|
| Navigator.pop(context)
↓
Home Screen
13. Centralizing Route Names
Writing route names directly in many files can lead to spelling mistakes. A common practice is to store route names as constants in a separate class.
Route Constants
class AppRoutes {
static const String home = '/';
static const String login = '/login';
static const String profile = '/profile';
static const String settings = '/settings';
}
Using Route Constants
MaterialApp(
initialRoute: AppRoutes.home,
routes: {
AppRoutes.home: (context) => const HomeScreen(),
AppRoutes.login: (context) => const LoginScreen(),
AppRoutes.profile: (context) => const ProfileScreen(),
AppRoutes.settings: (context) => const SettingsScreen(),
},
);
Navigating with Route Constants
Navigator.pushNamed(
context,
AppRoutes.profile,
);
This approach helps maintain consistent route names throughout the application.
14. Complete Example Using Centralized Route Names
import 'package:flutter/material.dart';
class AppRoutes {
static const String home = '/';
static const String profile = '/profile';
static const String settings = '/settings';
}
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Route Constants Demo',
initialRoute: AppRoutes.home,
routes: {
AppRoutes.home: (context) => const HomeScreen(),
AppRoutes.profile: (context) => const ProfileScreen(),
AppRoutes.settings: (context) => const SettingsScreen(),
},
);
}
}
class HomeScreen extends StatelessWidget {
const HomeScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Home'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () {
Navigator.pushNamed(
context,
AppRoutes.profile,
);
},
child: const Text('Open Profile'),
),
const SizedBox(height: 12),
ElevatedButton(
onPressed: () {
Navigator.pushNamed(
context,
AppRoutes.settings,
);
},
child: const Text('Open Settings'),
),
],
),
),
);
}
}
class ProfileScreen extends StatelessWidget {
const ProfileScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Profile'),
),
body: Center(
child: ElevatedButton(
onPressed: () {
Navigator.pop(context);
},
child: const Text('Back to Home'),
),
),
);
}
}
class SettingsScreen extends StatelessWidget {
const SettingsScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Settings'),
),
body: Center(
child: ElevatedButton(
onPressed: () {
Navigator.pop(context);
},
child: const Text('Back to Home'),
),
),
);
}
}
15. Passing Arguments to Named Routes
Sometimes a screen needs additional data. For example, a Product Details screen may need a product ID, product name, or price.
Flutter allows arguments to be passed through the arguments parameter of Navigator.pushNamed().
Basic Syntax
Navigator.pushNamed(
context,
'/details',
arguments: 'Flutter Course',
);
Multiple values can be passed using a map:
Navigator.pushNamed(
context,
'/details',
arguments: {
'id': 101,
'name': 'Flutter Course',
'price': 4999,
},
);
16. Receiving Arguments with ModalRoute
The destination screen can read the arguments using ModalRoute.of(context).
Complete Example
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,
initialRoute: '/',
routes: {
'/': (context) => const HomeScreen(),
'/details': (context) => const DetailsScreen(),
},
);
}
}
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.pushNamed(
context,
'/details',
arguments: {
'id': 101,
'name': 'Flutter Course',
},
);
},
child: const Text('Open Details'),
),
),
);
}
}
class DetailsScreen extends StatelessWidget {
const DetailsScreen({super.key});
@override
Widget build(BuildContext context) {
final arguments = ModalRoute.of(context)!.settings.arguments
as Map<String, dynamic>;
final int productId = arguments['id'] as int;
final String productName = arguments['name'] as String;
return Scaffold(
appBar: AppBar(
title: const Text('Details Screen'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Product ID: $productId'),
Text('Product Name: $productName'),
const SizedBox(height: 20),
ElevatedButton(
onPressed: () {
Navigator.pop(context);
},
child: const Text('Go Back'),
),
],
),
),
);
}
}
Important Points
arguments can contain a string, number, map, list, or custom object.
- The receiving screen must know the expected data type.
- Type casting should be performed carefully.
- For complex data, a custom argument class can improve readability and type safety.
17. Passing a Custom Object as Arguments
Instead of passing a map, you can create a custom Dart class to represent route arguments.
Creating an Argument Class
class ProductArguments {
final int id;
final String name;
const ProductArguments({
required this.id,
required this.name,
});
}
Passing the Object
Navigator.pushNamed(
context,
'/details',
arguments: const ProductArguments(
id: 101,
name: 'Flutter Course',
),
);
Receiving the Object
final arguments = ModalRoute.of(context)!.settings.arguments
as ProductArguments;
final int productId = arguments.id;
final String productName = arguments.name;
Custom argument classes are useful when a route requires multiple related values.
18. Using onGenerateRoute
The onGenerateRoute property allows you to create routes dynamically. It receives a RouteSettings object containing the requested route name and optional arguments.
Basic Syntax
MaterialApp(
onGenerateRoute: (settings) {
if (settings.name == '/details') {
return MaterialPageRoute(
builder: (context) => const DetailsScreen(),
settings: settings,
);
}
return MaterialPageRoute(
builder: (context) => const NotFoundScreen(),
);
},
);
This approach is useful when route creation requires conditions, validation, custom arguments, or dynamic logic.
19. Complete onGenerateRoute Example
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: 'Generated Routes Demo',
initialRoute: '/',
onGenerateRoute: (settings) {
switch (settings.name) {
case '/':
return MaterialPageRoute(
builder: (context) => const HomeScreen(),
settings: settings,
);
case '/profile':
return MaterialPageRoute(
builder: (context) => const ProfileScreen(),
settings: settings,
);
case '/settings':
return MaterialPageRoute(
builder: (context) => const SettingsScreen(),
settings: settings,
);
default:
return MaterialPageRoute(
builder: (context) => const NotFoundScreen(),
settings: settings,
);
}
},
);
}
}
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: () {
Navigator.pushNamed(context, '/profile');
},
child: const Text('Open Profile'),
),
),
);
}
}
class ProfileScreen extends StatelessWidget {
const ProfileScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Profile'),
),
body: Center(
child: ElevatedButton(
onPressed: () {
Navigator.pop(context);
},
child: const Text('Go Back'),
),
),
);
}
}
class SettingsScreen extends StatelessWidget {
const SettingsScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Settings'),
),
body: Center(
child: ElevatedButton(
onPressed: () {
Navigator.pop(context);
},
child: const Text('Go Back'),
),
),
);
}
}
class NotFoundScreen extends StatelessWidget {
const NotFoundScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Page Not Found'),
),
body: const Center(
child: Text('The requested route does not exist.'),
),
);
}
}
20. Handling Unknown Routes
If a user attempts to navigate to a route that is not defined, the application should provide a suitable fallback screen.
A fallback can be configured using onUnknownRoute.
MaterialApp(
routes: {
'/': (context) => const HomeScreen(),
'/profile': (context) => const ProfileScreen(),
},
onUnknownRoute: (settings) {
return MaterialPageRoute(
builder: (context) => const NotFoundScreen(),
);
},
);
This allows the application to display a custom error screen instead of failing silently when a route cannot be found.
21. Named Route Navigation with a Drawer
A Navigation Drawer often contains links to different sections of an application. Named routes can be used to navigate from each drawer item.
Drawer(
child: ListView(
children: [
const DrawerHeader(
child: Text('Application Menu'),
),
ListTile(
leading: const Icon(Icons.home),
title: const Text('Home'),
onTap: () {
Navigator.pushNamed(context, '/');
},
),
ListTile(
leading: const Icon(Icons.person),
title: const Text('Profile'),
onTap: () {
Navigator.pushNamed(context, '/profile');
},
),
ListTile(
leading: const Icon(Icons.settings),
title: const Text('Settings'),
onTap: () {
Navigator.pushNamed(context, '/settings');
},
),
],
),
)
When navigating from a drawer, you may also want to close the drawer before or after navigation, depending on the layout and navigation structure.
22. Named Routes and Login Flow
Named routes can be used to organize a simple login flow.
Login Screen
|
| Navigator.pushReplacementNamed(context, '/home')
↓
Home Screen
Example
ElevatedButton(
onPressed: () {
Navigator.pushReplacementNamed(
context,
'/home',
);
},
child: const Text('Login'),
)
pushReplacementNamed() replaces the current route with the named route so that the user does not return to the previous route through normal back navigation.
23. Removing Routes with pushNamedAndRemoveUntil()
pushNamedAndRemoveUntil() pushes a named route and removes previous routes according to a predicate.
Example
Navigator.pushNamedAndRemoveUntil(
context,
'/home',
(route) => false,
);
This example navigates to /home and removes the previous routes from the stack.
This pattern may be used after logout, registration completion, or finishing a checkout flow where earlier screens should not remain accessible through back navigation.
24. Named Routes vs Direct Navigator.push()
| Feature | Named Routes | Direct Route Navigation |
|---|
| Navigation method | Navigator.pushNamed() | Navigator.push() |
| Screen reference | Uses a string route name | Uses a route object and destination widget |
| Route configuration | Defined centrally | Usually created at the navigation point |
| Readability | Short navigation statements | More explicit route construction |
| Arguments | Passed through arguments or route generation | Often passed through a constructor |
| Dynamic routing | May require onGenerateRoute or another routing solution | Can directly construct a destination with required data |
| Current Flutter guidance | Not recommended for most new applications | Suitable for simple navigation using Navigator and explicit routes |
25. Advantages of Named Routes
- Route names can make navigation code short and readable.
- Routes can be configured in one central location.
- The same route can be accessed from different parts of the application.
- Named routes can reduce repeated route-building code.
- They can be useful in small applications with a fixed set of screens.
- They support passing arguments through the Navigator's route settings.
26. Limitations of Named Routes
- Route names are strings, so spelling mistakes may cause navigation errors.
- Arguments passed through
arguments may require type casting.
- Complex dynamic routes may require additional route-generation logic.
- Basic named routes do not automatically provide a complete solution for advanced deep linking.
- Large applications may require more structured routing solutions.
- Flutter's current documentation does not recommend named routes for most applications.
27. Common Mistakes
Mistake 1: Using an Undefined Route Name
Navigator.pushNamed(context, '/unknown');
If /unknown is not configured and no suitable fallback is provided, navigation may fail or invoke route-generation fallback logic.
Mistake 2: Spelling Mistakes in Route Names
routes: {
'/profile': (context) => const ProfileScreen(),
}
Navigator.pushNamed(context, '/profle');
The route names do not match. Use centralized constants to reduce this problem.
Mistake 3: Defining home and initialRoute Together
MaterialApp(
home: const HomeScreen(),
initialRoute: '/login',
);
Choose an appropriate starting-screen configuration instead of defining conflicting initial navigation settings.
Mistake 4: Incorrect Argument Type
final id = ModalRoute.of(context)!.settings.arguments as int;
If the caller passes a string or map instead of an integer, a runtime type error may occur. Ensure that the sender and receiver agree on the argument type.
28. Best Practices
- Use meaningful route names such as
/profile, /settings, and /product-details.
- Keep route names in a centralized class or constants file.
- Use consistent naming conventions throughout the project.
- Validate route arguments before using them.
- Use custom argument classes when multiple values must be passed together.
- Provide an unknown-route fallback when appropriate.
- Use
pushReplacementNamed() when the current route should not remain in the navigation stack.
- Use
pushNamedAndRemoveUntil() when previous routes need to be removed.
- For simple navigation, consider using
Navigator.push() with MaterialPageRoute and constructor-based data passing.
- For advanced navigation, deep linking, and complex route requirements, consider Router-based navigation or a routing package such as
go_router.
29. When Should You Use Named Routes?
Named routes may be useful when:
- The application has a small number of fixed screens.
- Several widgets navigate to the same destination.
- You want route names to be managed in one location.
- The navigation flow is simple and does not require complex URL handling.
For new applications with advanced navigation requirements, evaluate Navigator with explicit routes, Router-based navigation, or a routing package such as go_router.
30. Interview Questions
Q1. What are named routes in Flutter?
Named routes are routes identified by unique string names. They allow navigation between screens using route names instead of creating a route object at every navigation point.
Q2. Where are named routes defined?
Named routes are commonly defined in the routes property of MaterialApp or CupertinoApp.
Q3. What is the use of Navigator.pushNamed()?
Navigator.pushNamed() opens a route by using its configured route name.
Q4. How do you close a named route?
Use Navigator.pop(context) to remove the current route and return to the previous screen.
Q5. What is the purpose of initialRoute?
initialRoute specifies the named route displayed when the application starts.
Q6. How do you pass arguments to a named route?
Pass data through the arguments parameter of Navigator.pushNamed().
Q7. How do you receive named route arguments?
Arguments can be accessed using ModalRoute.of(context)!.settings.arguments or through an onGenerateRoute callback.
Q8. What is onGenerateRoute?
onGenerateRoute is a callback used to create routes dynamically based on route settings.
Q9. What is the difference between pushNamed() and pushReplacementNamed()?
pushNamed() adds a new named route to the stack, while pushReplacementNamed() replaces the current route with a named route.
Q10. Are named routes recommended for every Flutter application?
No. Flutter's current documentation does not recommend named routes for most applications. The appropriate navigation approach depends on the application's requirements, including deep linking, URL synchronization, and routing complexity.
31. Practice Exercises
- Create a Flutter application with
/ and /details named routes.
- Use
Navigator.pushNamed() to open the Details Screen.
- Use
Navigator.pop() to return to the Home Screen.
- Create named routes for Home, Profile, and Settings screens.
- Store all route names in a separate
AppRoutes class.
- Pass a product name through the
arguments parameter.
- Receive the product name using
ModalRoute.of(context).
- Create a custom argument class and pass it to a named route.
- Implement
onGenerateRoute for three screens.
- Create a fallback screen using
onUnknownRoute.
- Build a login flow using
pushReplacementNamed().
- Build a logout flow using
pushNamedAndRemoveUntil().
32. Quick Revision
| Concept | Code | Purpose |
|---|
| Define routes | routes: {} | Map route names to screen builders |
| Set initial route | initialRoute: '/' | Choose the starting named route |
| Open named route | Navigator.pushNamed(context, '/profile') | Navigate to a named screen |
| Close route | Navigator.pop(context) | Return to the previous screen |
| Pass arguments | arguments: data | Send data to a named route |
| Read arguments | ModalRoute.of(context) | Access route arguments |
| Generate routes | onGenerateRoute | Create routes dynamically |
| Handle unknown routes | onUnknownRoute | Display a fallback route |
| Replace route | pushReplacementNamed() | Replace the current route |
| Clear route history | pushNamedAndRemoveUntil() | Push a route and remove previous routes |
33. Key Takeaways
- Named routes identify screens using string names.
- Named routes are configured using the
routes property.
initialRoute determines the starting named route.
Navigator.pushNamed() opens a named route.
Navigator.pop() closes the current route.
- Arguments can be passed using the
arguments parameter.
- Arguments can be received through
ModalRoute.of(context) or onGenerateRoute.
- Route constants help prevent spelling mistakes.
onGenerateRoute supports custom route-generation logic.
onUnknownRoute can provide a fallback screen.
pushReplacementNamed() replaces the current route.
pushNamedAndRemoveUntil() can remove previous routes.
- Named routes are not recommended for most new applications according to current Flutter documentation.
- For advanced navigation and deep-linking requirements, consider Router-based navigation or a routing package such as
go_router.
34. Official Flutter Documentation
35. Flutter Training Resources
For more information about Flutter development and training, visit the following resources: