Creating Multi-Section Application Navigation in Flutter
Multi-section application navigation is the process of organizing a Flutter application into multiple major sections such as Home, Products, Orders, Notifications, Profile, Settings, or Dashboard. Users can switch between these sections using navigation components such as NavigationBar, NavigationRail, NavigationDrawer, or nested navigation flows.
Flutter provides Navigator and Router for screen navigation. For applications with more advanced routing and deep-linking requirements, Flutter documentation recommends using Router-based navigation or a routing package such as go_router. :contentReference[oaicite:0]{index=0}
1. What Is Multi-Section Application Navigation?
A multi-section application divides an application into several top-level areas. Each section represents a major feature of the application.
Example Application Structure
- Home
- Products
- Orders
- Notifications
- Profile
- Settings
For example, an e-commerce application may have:
Home
Products
Cart
Orders
Profile
The user can move between these sections without having to restart the application.
2. Navigation Architecture
A typical multi-section Flutter application can be organized into two navigation levels:
- Top-level navigation: Moves between major sections such as Home, Products, Orders, and Profile.
- Detail navigation: Opens a specific screen inside a section, such as Product Details or Order Details.
Application
├── Home
│ ├── Featured Products
│ └── Categories
├── Products
│ ├── Product List
│ └── Product Details
├── Orders
│ ├── Order List
│ └── Order Details
└── Profile
├── Personal Information
└── Settings
This structure makes the navigation system easier to understand and maintain.
3. Flutter Navigation Widgets
Flutter provides several Material navigation widgets for different application layouts. The Material widget catalog includes NavigationBar, NavigationDrawer, NavigationRail, and TabBar. :contentReference[oaicite:1]{index=1}
| Widget | Common Use |
|---|
NavigationBar | Bottom navigation for primary sections |
NavigationRail | Side navigation, especially on larger screens |
NavigationDrawer | Drawer-based navigation |
TabBar | Switching between related views or categories |
Navigator | Stack-based navigation between routes |
Router | Declarative and advanced navigation/deep linking |
4. NavigationBar for Multi-Section Applications
NavigationBar is useful when an application has a small number of important top-level destinations. It provides persistent navigation between primary destinations. :contentReference[oaicite:2]{index=2}
Basic 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,
home: const MainScreen(),
);
}
}
class MainScreen extends StatefulWidget {
const MainScreen({super.key});
@override
State<MainScreen> createState() => _MainScreenState();
}
class _MainScreenState extends State<MainScreen> {
int currentIndex = 0;
final List<Widget> screens = const [
HomeScreen(),
ProductsScreen(),
OrdersScreen(),
ProfileScreen(),
];
@override
Widget build(BuildContext context) {
return Scaffold(
body: screens[currentIndex],
bottomNavigationBar: NavigationBar(
selectedIndex: currentIndex,
onDestinationSelected: (index) {
setState(() {
currentIndex = index;
});
},
destinations: const [
NavigationDestination(
icon: Icon(Icons.home_outlined),
selectedIcon: Icon(Icons.home),
label: 'Home',
),
NavigationDestination(
icon: Icon(Icons.shopping_bag_outlined),
selectedIcon: Icon(Icons.shopping_bag),
label: 'Products',
),
NavigationDestination(
icon: Icon(Icons.receipt_long_outlined),
selectedIcon: Icon(Icons.receipt_long),
label: 'Orders',
),
NavigationDestination(
icon: Icon(Icons.person_outline),
selectedIcon: Icon(Icons.person),
label: 'Profile',
),
],
),
);
}
}
5. Creating Individual Sections
Each major section should generally be implemented as its own widget.
class HomeScreen extends StatelessWidget {
const HomeScreen({super.key});
@override
Widget build(BuildContext context) {
return const Scaffold(
appBar: AppBar(
title: Text('Home'),
),
body: Center(
child: Text('Welcome to Home'),
),
);
}
}
class ProductsScreen extends StatelessWidget {
const ProductsScreen({super.key});
@override
Widget build(BuildContext context) {
return const Scaffold(
appBar: AppBar(
title: Text('Products'),
),
body: Center(
child: Text('Product Section'),
),
);
}
}
class OrdersScreen extends StatelessWidget {
const OrdersScreen({super.key});
@override
Widget build(BuildContext context) {
return const Scaffold(
appBar: AppBar(
title: Text('Orders'),
),
body: Center(
child: Text('Orders Section'),
),
);
}
}
class ProfileScreen extends StatelessWidget {
const ProfileScreen({super.key});
@override
Widget build(BuildContext context) {
return const Scaffold(
appBar: AppBar(
title: Text('Profile'),
),
body: Center(
child: Text('Profile Section'),
),
);
}
}
6. Understanding selectedIndex
The selectedIndex property determines which navigation destination is currently selected.
int currentIndex = 0;
NavigationBar(
selectedIndex: currentIndex,
onDestinationSelected: (index) {
setState(() {
currentIndex = index;
});
},
)
For example:
| Index | Section |
|---|
| 0 | Home |
| 1 | Products |
| 2 | Orders |
| 3 | Profile |
7. Why Use a Common Parent Widget?
A common parent widget can control which top-level section is displayed. This keeps the navigation state in one place.
class MainScreen extends StatefulWidget {
const MainScreen({super.key});
@override
State<MainScreen> createState() => _MainScreenState();
}
class _MainScreenState extends State<MainScreen> {
int selectedIndex = 0;
final pages = const [
HomeScreen(),
ProductsScreen(),
OrdersScreen(),
ProfileScreen(),
];
@override
Widget build(BuildContext context) {
return Scaffold(
body: pages[selectedIndex],
bottomNavigationBar: NavigationBar(
selectedIndex: selectedIndex,
onDestinationSelected: (index) {
setState(() {
selectedIndex = index;
});
},
destinations: const [
NavigationDestination(
icon: Icon(Icons.home_outlined),
selectedIcon: Icon(Icons.home),
label: 'Home',
),
NavigationDestination(
icon: Icon(Icons.shopping_bag_outlined),
selectedIcon: Icon(Icons.shopping_bag),
label: 'Products',
),
NavigationDestination(
icon: Icon(Icons.receipt_long_outlined),
selectedIcon: Icon(Icons.receipt_long),
label: 'Orders',
),
NavigationDestination(
icon: Icon(Icons.person_outline),
selectedIcon: Icon(Icons.person),
label: 'Profile',
),
],
),
);
}
}
8. NavigationBar vs Navigator
These two concepts solve different navigation problems.
| NavigationBar | Navigator |
|---|
| Switches between major application sections | Pushes and pops routes/screens |
| Usually remains visible | Usually changes the route stack |
| Example: Home → Orders | Example: Product List → Product Details |
| Good for top-level destinations | Good for hierarchical navigation |
Flutter's Navigator.push() adds a route to the navigation stack, while Navigator.pop() removes the current route and returns to the previous route. :contentReference[oaicite:3]{index=3}
9. Opening Detail Screens Inside a Section
Suppose the Products section displays a list of products. When the user selects a product, the application can open a Product Details screen.
class ProductsScreen extends StatelessWidget {
const ProductsScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Products'),
),
body: ListView(
children: [
ListTile(
title: const Text('Laptop'),
subtitle: const Text('₹60,000'),
trailing: const Icon(Icons.arrow_forward_ios),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const ProductDetailsScreen(
productName: 'Laptop',
price: 60000,
),
),
);
},
),
],
),
);
}
}
Product Details Screen
class ProductDetailsScreen extends StatelessWidget {
final String productName;
final double price;
const ProductDetailsScreen({
super.key,
required this.productName,
required this.price,
});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Product Details'),
),
body: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
productName,
style: const TextStyle(
fontSize: 26,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 10),
Text('Price: ₹$price'),
],
),
),
);
}
}
10. Multi-Level Navigation
A real application can contain both top-level and detail-level navigation.
NavigationBar
|
+-- Home
|
+-- Products
| |
| +-- Product Details
| | |
| | +-- Reviews
| | +-- Checkout
|
+-- Orders
| |
| +-- Order Details
|
+-- Profile
|
+-- Edit Profile
+-- Settings
The bottom navigation controls the major sections, while the Navigator handles screens inside those sections.
11. Preserving Section State with IndexedStack
If every section is rebuilt when the user switches tabs, some temporary UI state may be lost. An IndexedStack can keep multiple section widgets in the widget tree while displaying only the selected one.
class MainScreen extends StatefulWidget {
const MainScreen({super.key});
@override
State<MainScreen> createState() => _MainScreenState();
}
class _MainScreenState extends State<MainScreen> {
int selectedIndex = 0;
final pages = const [
HomeScreen(),
ProductsScreen(),
OrdersScreen(),
ProfileScreen(),
];
@override
Widget build(BuildContext context) {
return Scaffold(
body: IndexedStack(
index: selectedIndex,
children: pages,
),
bottomNavigationBar: NavigationBar(
selectedIndex: selectedIndex,
onDestinationSelected: (index) {
setState(() {
selectedIndex = index;
});
},
destinations: const [
NavigationDestination(
icon: Icon(Icons.home),
label: 'Home',
),
NavigationDestination(
icon: Icon(Icons.shopping_bag),
label: 'Products',
),
NavigationDestination(
icon: Icon(Icons.receipt),
label: 'Orders',
),
NavigationDestination(
icon: Icon(Icons.person),
label: 'Profile',
),
],
),
);
}
}
Advantages of IndexedStack
- Helps preserve the state of section widgets.
- Useful for forms, scroll positions, and tab content.
- Provides smooth switching between sections.
- Keeps multiple section widgets mounted.
12. NavigationRail for Larger Screens
For tablets, desktop applications, and larger layouts, a side navigation pattern can be more suitable. Flutter provides NavigationRail for persistent navigation on the leading edge of larger screens. :contentReference[oaicite:4]{index=4}
class DesktopNavigation extends StatefulWidget {
const DesktopNavigation({super.key});
@override
State<DesktopNavigation> createState() => _DesktopNavigationState();
}
class _DesktopNavigationState extends State<DesktopNavigation> {
int selectedIndex = 0;
final pages = const [
HomeScreen(),
ProductsScreen(),
OrdersScreen(),
ProfileScreen(),
];
@override
Widget build(BuildContext context) {
return Scaffold(
body: Row(
children: [
NavigationRail(
selectedIndex: selectedIndex,
onDestinationSelected: (index) {
setState(() {
selectedIndex = index;
});
},
destinations: const [
NavigationRailDestination(
icon: Icon(Icons.home_outlined),
selectedIcon: Icon(Icons.home),
label: Text('Home'),
),
NavigationRailDestination(
icon: Icon(Icons.shopping_bag_outlined),
selectedIcon: Icon(Icons.shopping_bag),
label: Text('Products'),
),
NavigationRailDestination(
icon: Icon(Icons.receipt_long_outlined),
selectedIcon: Icon(Icons.receipt_long),
label: Text('Orders'),
),
NavigationRailDestination(
icon: Icon(Icons.person_outline),
selectedIcon: Icon(Icons.person),
label: Text('Profile'),
),
],
),
const VerticalDivider(width: 1),
Expanded(
child: pages[selectedIndex],
),
],
),
);
}
}
13. Responsive Multi-Section Navigation
A responsive Flutter application can change its navigation pattern based on available screen width.
LayoutBuilder(
builder: (context, constraints) {
if (constraints.maxWidth < 600) {
return MobileNavigation();
}
return DesktopNavigation();
},
)
A common pattern is:
| Screen Size | Navigation Pattern |
|---|
| Small mobile | NavigationBar |
| Large mobile / tablet | NavigationBar or NavigationRail |
| Desktop | NavigationRail or NavigationDrawer |
Flutter's adaptive navigation examples demonstrate using different navigation patterns depending on screen size, including stack-based navigation on smaller screens and master-detail layouts on larger screens. :contentReference[oaicite:5]{index=5}
14. NavigationDrawer for Multiple Sections
When an application contains many destinations, a drawer can organize navigation options without occupying permanent screen space.
Scaffold(
appBar: AppBar(
title: const Text('My Application'),
),
drawer: NavigationDrawer(
children: [
const Padding(
padding: EdgeInsets.all(16),
child: Text(
'Application Menu',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
),
),
),
NavigationDrawerDestination(
icon: const Icon(Icons.home),
label: const Text('Home'),
),
NavigationDrawerDestination(
icon: const Icon(Icons.shopping_bag),
label: const Text('Products'),
),
NavigationDrawerDestination(
icon: const Icon(Icons.receipt),
label: const Text('Orders'),
),
NavigationDrawerDestination(
icon: const Icon(Icons.settings),
label: const Text('Settings'),
),
],
),
body: const Center(
child: Text('Application Content'),
),
)
15. Passing Data Between Sections
Data can be passed to a detail screen through its constructor.
class Product {
final String name;
final double price;
const Product({
required this.name,
required this.price,
});
}
final product = Product(
name: 'Smartphone',
price: 25000,
);
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => ProductDetailsScreen(
productName: product.name,
price: product.price,
),
),
);
This approach makes the destination screen explicitly receive the data it needs.
16. Returning Data from a Detail Screen
A detail screen can return information to its previous screen using Navigator.pop().
final result = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const SelectionScreen(),
),
);
if (!context.mounted) return;
if (result != null) {
print(result);
}
The destination screen can return the result:
Navigator.pop(context, 'Selected Product');
17. Nested Navigation
Complex applications may require a separate navigation stack inside a particular section. This is called nested navigation.
For example:
Application Navigator
│
├── Home
├── Products
├── Orders
└── Profile
│
├── Profile Overview
├── Edit Profile
├── Addresses
└── Account Settings
A nested Navigator can manage the routes inside the Profile section without putting every profile route into the application's top-level navigation stack. Flutter's official nested-navigation recipe demonstrates this pattern for multi-page flows. :contentReference[oaicite:6]{index=6}
18. Example of a Nested Navigator
class ProfileFlow extends StatefulWidget {
const ProfileFlow({super.key});
@override
State<ProfileFlow> createState() => _ProfileFlowState();
}
class _ProfileFlowState extends State<ProfileFlow> {
final navigatorKey = GlobalKey<NavigatorState>();
@override
Widget build(BuildContext context) {
return Navigator(
key: navigatorKey,
onGenerateRoute: (settings) {
return MaterialPageRoute(
builder: (context) => const ProfileHome(),
);
},
);
}
}
class ProfileHome extends StatelessWidget {
const ProfileHome({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Profile'),
),
body: Center(
child: ElevatedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const EditProfileScreen(),
),
);
},
child: const Text('Edit Profile'),
),
),
);
}
}
class EditProfileScreen extends StatelessWidget {
const EditProfileScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Edit Profile'),
),
body: const Center(
child: Text('Edit Profile'),
),
);
}
}
19. Using go_router for Larger Applications
For applications with complex navigation, multiple navigators, and deep linking requirements, a routing package such as go_router can simplify route configuration. Flutter's navigation documentation specifically identifies go_router as a routing option for advanced requirements. :contentReference[oaicite:7]{index=7}
Installation
flutter pub add go_router
Basic Router Configuration
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
final router = GoRouter(
routes: [
GoRoute(
path: '/',
builder: (context, state) => const HomeScreen(),
),
GoRoute(
path: '/products',
builder: (context, state) => const ProductsScreen(),
),
GoRoute(
path: '/orders',
builder: (context, state) => const OrdersScreen(),
),
GoRoute(
path: '/profile',
builder: (context, state) => const ProfileScreen(),
),
],
);
void main() {
runApp(
MaterialApp.router(
routerConfig: router,
),
);
}
Navigating with go_router
context.go('/products');
For example:
ElevatedButton(
onPressed: () {
context.go('/products');
},
child: const Text('Open Products'),
)
20. Multi-Section Navigation with go_router
A larger application can define a route hierarchy.
/
├── /home
├── /products
│ └── /products/:id
├── /orders
│ └── /orders/:id
└── /profile
├── /profile/edit
└── /profile/settings
For example:
final router = GoRouter(
routes: [
GoRoute(
path: '/',
builder: (context, state) => const MainScreen(),
routes: [
GoRoute(
path: 'products',
builder: (context, state) => const ProductsScreen(),
),
GoRoute(
path: 'orders',
builder: (context, state) => const OrdersScreen(),
),
GoRoute(
path: 'profile',
builder: (context, state) => const ProfileScreen(),
),
],
),
],
);
21. Deep Linking in Multi-Section Applications
Deep linking allows a URL or external link to open a specific location inside an application. Flutter supports deep linking on Android, iOS, and web. :contentReference[oaicite:8]{index=8}
For example:
https://example.com/products
https://example.com/products/101
https://example.com/orders/500
https://example.com/profile
A deep link such as:
/products/101
can take the user directly to Product 101 instead of requiring them to manually open Products first.
22. Route Parameters
Dynamic route parameters can identify a specific resource.
GoRoute(
path: '/products/:id',
builder: (context, state) {
final productId = state.pathParameters['id'];
return ProductDetailsScreen(
productId: productId!,
);
},
)
For example:
/products/101
Here, 101 becomes the product ID.
23. Multi-Section E-Commerce Example
The following example demonstrates a basic e-commerce navigation structure.
import 'package:flutter/material.dart';
void main() {
runApp(const ECommerceApp());
}
class ECommerceApp extends StatelessWidget {
const ECommerceApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(
seedColor: Colors.blue,
),
useMaterial3: true,
),
home: const MainScreen(),
);
}
}
class MainScreen extends StatefulWidget {
const MainScreen({super.key});
@override
State<MainScreen> createState() => _MainScreenState();
}
class _MainScreenState extends State<MainScreen> {
int selectedIndex = 0;
final pages = const [
HomeScreen(),
ProductsScreen(),
CartScreen(),
ProfileScreen(),
];
@override
Widget build(BuildContext context) {
return Scaffold(
body: IndexedStack(
index: selectedIndex,
children: pages,
),
bottomNavigationBar: NavigationBar(
selectedIndex: selectedIndex,
onDestinationSelected: (index) {
setState(() {
selectedIndex = index;
});
},
destinations: const [
NavigationDestination(
icon: Icon(Icons.home_outlined),
selectedIcon: Icon(Icons.home),
label: 'Home',
),
NavigationDestination(
icon: Icon(Icons.shopping_bag_outlined),
selectedIcon: Icon(Icons.shopping_bag),
label: 'Products',
),
NavigationDestination(
icon: Icon(Icons.shopping_cart_outlined),
selectedIcon: Icon(Icons.shopping_cart),
label: 'Cart',
),
NavigationDestination(
icon: Icon(Icons.person_outline),
selectedIcon: Icon(Icons.person),
label: 'Profile',
),
],
),
);
}
}
class HomeScreen extends StatelessWidget {
const HomeScreen({super.key});
@override
Widget build(BuildContext context) {
return const Scaffold(
appBar: AppBar(
title: Text('Home'),
),
body: Center(
child: Text('Welcome to our store'),
),
);
}
}
class ProductsScreen extends StatelessWidget {
const ProductsScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Products'),
),
body: ListView(
children: [
ListTile(
leading: const Icon(Icons.phone_android),
title: const Text('Smartphone'),
subtitle: const Text('₹25,000'),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
const ProductDetailsScreen(),
),
);
},
),
ListTile(
leading: const Icon(Icons.laptop),
title: const Text('Laptop'),
subtitle: const Text('₹60,000'),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
const ProductDetailsScreen(),
),
);
},
),
],
),
);
}
}
class ProductDetailsScreen extends StatelessWidget {
const ProductDetailsScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Product Details'),
),
body: const Center(
child: Text('Product Details'),
),
);
}
}
class CartScreen extends StatelessWidget {
const CartScreen({super.key});
@override
Widget build(BuildContext context) {
return const Scaffold(
appBar: AppBar(
title: Text('Cart'),
),
body: Center(
child: Text('Your Cart'),
),
);
}
}
class ProfileScreen extends StatelessWidget {
const ProfileScreen({super.key});
@override
Widget build(BuildContext context) {
return const Scaffold(
appBar: AppBar(
title: Text('Profile'),
),
body: Center(
child: Text('User Profile'),
),
);
}
}
24. Navigation Flow of the E-Commerce Application
MainScreen
│
├── Home
│
├── Products
│ ├── Smartphone
│ │ └── Product Details
│ └── Laptop
│ └── Product Details
│
├── Cart
│
└── Profile
├── Edit Profile
└── Settings
Here, NavigationBar controls the four primary sections, while Navigator.push() handles product detail screens.
25. Keeping Navigation Code Organized
Large applications should avoid placing all navigation code inside one huge widget.
A possible project structure is:
lib/
├── main.dart
├── navigation/
│ ├── app_navigation.dart
│ └── app_routes.dart
├── screens/
│ ├── home/
│ │ └── home_screen.dart
│ ├── products/
│ │ ├── products_screen.dart
│ │ └── product_details_screen.dart
│ ├── orders/
│ │ ├── orders_screen.dart
│ │ └── order_details_screen.dart
│ └── profile/
│ ├── profile_screen.dart
│ └── settings_screen.dart
├── widgets/
│ └── app_navigation_bar.dart
└── models/
└── product.dart
26. Creating a Reusable NavigationBar Widget
Navigation UI can be extracted into a reusable widget.
class AppNavigationBar extends StatelessWidget {
final int selectedIndex;
final ValueChanged<int> onDestinationSelected;
const AppNavigationBar({
super.key,
required this.selectedIndex,
required this.onDestinationSelected,
});
@override
Widget build(BuildContext context) {
return NavigationBar(
selectedIndex: selectedIndex,
onDestinationSelected: onDestinationSelected,
destinations: const [
NavigationDestination(
icon: Icon(Icons.home_outlined),
selectedIcon: Icon(Icons.home),
label: 'Home',
),
NavigationDestination(
icon: Icon(Icons.search_outlined),
selectedIcon: Icon(Icons.search),
label: 'Search',
),
NavigationDestination(
icon: Icon(Icons.person_outline),
selectedIcon: Icon(Icons.person),
label: 'Profile',
),
],
);
}
}
It can then be used inside the main screen:
bottomNavigationBar: AppNavigationBar(
selectedIndex: selectedIndex,
onDestinationSelected: (index) {
setState(() {
selectedIndex = index;
});
},
),
27. Navigation State Management
As an application grows, navigation state may need to interact with application state.
Examples include:
- Logged-in or logged-out state.
- Selected user account.
- Shopping cart count.
- Unread notification count.
- Selected product.
- Current order.
- Application settings.
For simple applications, local setState() may be sufficient. Larger applications may use state-management solutions such as Provider, Riverpod, Bloc, or another architecture appropriate to the project.
28. Example: Navigation with Cart Count
class MainScreen extends StatefulWidget {
const MainScreen({super.key});
@override
State<MainScreen> createState() => _MainScreenState();
}
class _MainScreenState extends State<MainScreen> {
int selectedIndex = 0;
int cartCount = 2;
@override
Widget build(BuildContext context) {
return Scaffold(
body: const HomeScreen(),
bottomNavigationBar: NavigationBar(
selectedIndex: selectedIndex,
onDestinationSelected: (index) {
setState(() {
selectedIndex = index;
});
},
destinations: [
const NavigationDestination(
icon: Icon(Icons.home_outlined),
selectedIcon: Icon(Icons.home),
label: 'Home',
),
NavigationDestination(
icon: Badge(
label: Text('$cartCount'),
child: const Icon(Icons.shopping_cart_outlined),
),
selectedIcon: Badge(
label: Text('$cartCount'),
child: const Icon(Icons.shopping_cart),
),
label: 'Cart',
),
const NavigationDestination(
icon: Icon(Icons.person_outline),
selectedIcon: Icon(Icons.person),
label: 'Profile',
),
],
),
);
}
}
29. Authentication and Navigation
Many applications have public and authenticated sections.
Application
│
├── Login
├── Register
│
└── Authenticated Area
├── Home
├── Products
├── Orders
└── Profile
After successful login, the application can navigate to the main authenticated section.
Navigator.pushReplacement(
context,
MaterialPageRoute(
builder: (context) => const MainScreen(),
),
);
pushReplacement() is useful when the previous screen should not remain as the next back-navigation destination.
30. Logout Navigation
When a user logs out, an application commonly clears or changes the authenticated navigation state and takes the user to the login screen.
Navigator.pushAndRemoveUntil(
context,
MaterialPageRoute(
builder: (context) => const LoginScreen(),
),
(route) => false,
);
This removes the previous routes from the navigation stack.
31. Avoiding Too Many Top-Level Sections
Top-level navigation should contain the application's major destinations rather than every individual screen.
For example, instead of:
Home
Product 1
Product 2
Product 3
Product 4
Order 1
Order 2
Settings
Edit Profile
Use:
Home
Products
Orders
Profile
Settings
Then navigate to details from inside each section.
32. Navigation and Deep Links
When an application supports deep links, a URL can represent a specific application location.
/products
/products/101
/orders
/orders/500
/profile/settings
This is especially useful for web applications, product sharing, notification links, marketing links, and external navigation. Flutter supports deep linking across Android, iOS, and web. :contentReference[oaicite:9]{index=9}
33. Named Routes
Flutter supports named routes, but the current Flutter navigation documentation states that named routes are not recommended for most new applications. For new applications, Flutter recommends considering go_router or using Navigator with MaterialPageRoute. :contentReference[oaicite:10]{index=10}
A basic named-route example for understanding existing projects is:
MaterialApp(
routes: {
'/': (context) => const HomeScreen(),
'/products': (context) => const ProductsScreen(),
'/orders': (context) => const OrdersScreen(),
'/profile': (context) => const ProfileScreen(),
},
)
Navigation:
Navigator.pushNamed(context, '/products');
34. Common Navigation Mistakes
Mistake 1: Putting Every Screen in Bottom Navigation
Bottom navigation should generally represent major application sections rather than every detail page.
Mistake 2: Losing Tab State
If a section contains forms or scrollable content, repeatedly rebuilding it can cause unwanted state changes. Consider appropriate state preservation techniques such as IndexedStack.
Mistake 3: Huge Navigation Files
Move route definitions and navigation components into separate files as the application grows.
Mistake 4: Mixing Navigation and Business Logic
Keep authentication, database, API, and business logic separate from navigation UI when possible.
Mistake 5: Ignoring Responsive Layouts
A bottom navigation layout designed for a phone may not be appropriate for a desktop screen. Consider adaptive navigation patterns.
Mistake 6: Using the Wrong Navigation Level
Use top-level navigation for major sections and stack navigation for details and temporary flows.
35. Best Practices
- Keep top-level destinations focused on major application sections.
- Use
NavigationBar for bottom-oriented primary navigation.
- Use
NavigationRail or NavigationDrawer when appropriate for larger screens.
- Use
Navigator.push() for hierarchical detail screens.
- Use
Navigator.pop() to return from a pushed route.
- Use
IndexedStack when preserving multiple section states is useful.
- Separate navigation configuration from business logic.
- Use nested navigation for self-contained multi-step flows.
- Consider
go_router for complex routing and deep linking.
- Test navigation on different screen sizes.
- Provide meaningful labels and icons for navigation destinations.
- Keep back navigation predictable.
36. Complete Navigation Flow
User Opens App
|
v
Home Section
|
+----------------+
| |
v v
Products Profile
|
v
Product Details
|
v
Add to Cart
|
v
Cart
|
v
Checkout
|
v
Orders
37. Practical Project Example
Consider building a learning application with the following sections:
Learning App
├── Home
├── Courses
├── My Learning
├── Notifications
└── Profile
Inside Courses:
Courses
├── Flutter
├── Dart
├── Java
└── Web Development
When the user taps Flutter:
Courses
|
+-- Flutter Course
|
+-- Course Details
+-- Lessons
+-- Progress
+-- Certificate
This demonstrates how top-level navigation and hierarchical navigation can work together.
38. Mini Project: Learning App Navigation
import 'package:flutter/material.dart';
void main() {
runApp(const LearningApp());
}
class LearningApp extends StatelessWidget {
const LearningApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: const LearningHome(),
);
}
}
class LearningHome extends StatefulWidget {
const LearningHome({super.key});
@override
State<LearningHome> createState() => _LearningHomeState();
}
class _LearningHomeState extends State<LearningHome> {
int selectedIndex = 0;
final pages = const [
HomePage(),
CoursesPage(),
LearningPage(),
ProfilePage(),
];
@override
Widget build(BuildContext context) {
return Scaffold(
body: IndexedStack(
index: selectedIndex,
children: pages,
),
bottomNavigationBar: NavigationBar(
selectedIndex: selectedIndex,
onDestinationSelected: (index) {
setState(() {
selectedIndex = index;
});
},
destinations: const [
NavigationDestination(
icon: Icon(Icons.home_outlined),
selectedIcon: Icon(Icons.home),
label: 'Home',
),
NavigationDestination(
icon: Icon(Icons.school_outlined),
selectedIcon: Icon(Icons.school),
label: 'Courses',
),
NavigationDestination(
icon: Icon(Icons.play_circle_outline),
selectedIcon: Icon(Icons.play_circle),
label: 'Learning',
),
NavigationDestination(
icon: Icon(Icons.person_outline),
selectedIcon: Icon(Icons.person),
label: 'Profile',
),
],
),
);
}
}
class HomePage extends StatelessWidget {
const HomePage({super.key});
@override
Widget build(BuildContext context) {
return const Scaffold(
appBar: AppBar(
title: Text('Home'),
),
body: Center(
child: Text('Welcome to Learning App'),
),
);
}
}
class CoursesPage extends StatelessWidget {
const CoursesPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Courses'),
),
body: ListView(
children: [
ListTile(
leading: const Icon(Icons.flutter_dash),
title: const Text('Flutter Training'),
subtitle: const Text('Learn Flutter Development'),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const CourseDetailsPage(),
),
);
},
),
const ListTile(
leading: Icon(Icons.code),
title: Text('Dart Programming'),
subtitle: Text('Learn Dart Programming'),
),
],
),
);
}
}
class CourseDetailsPage extends StatelessWidget {
const CourseDetailsPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Flutter Training'),
),
body: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Flutter Training Course',
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 16),
const Text(
'Learn Flutter, Dart, widgets, navigation, state management and application development.',
),
const SizedBox(height: 20),
ElevatedButton(
onPressed: () {
Navigator.pop(context);
},
child: const Text('Back'),
),
],
),
),
);
}
}
class LearningPage extends StatelessWidget {
const LearningPage({super.key});
@override
Widget build(BuildContext context) {
return const Scaffold(
appBar: AppBar(
title: Text('My Learning'),
),
body: Center(
child: Text('Continue Learning'),
),
);
}
}
class ProfilePage extends StatelessWidget {
const ProfilePage({super.key});
@override
Widget build(BuildContext context) {
return const Scaffold(
appBar: AppBar(
title: Text('Profile'),
),
body: Center(
child: Text('User Profile'),
),
);
}
}
39. Learning Outcomes
After completing this topic, you should be able to:
- Understand multi-section application navigation.
- Create top-level navigation using
NavigationBar.
- Manage navigation state with
selectedIndex.
- Create independent screens for application sections.
- Navigate from a section to a detail screen.
- Pass information between screens.
- Return information from a detail screen.
- Preserve section state using
IndexedStack.
- Build responsive navigation using
NavigationRail.
- Understand NavigationDrawer-based navigation.
- Understand nested navigation.
- Understand deep linking.
- Understand the role of
Navigator and Router.
- Use routing packages such as
go_router for complex applications.
- Organize navigation code in a scalable project structure.
40. Interview Questions
Q1. What is multi-section navigation?
Multi-section navigation organizes an application into major areas such as Home, Products, Orders, and Profile and allows users to switch between those areas.
Q2. What is NavigationBar used for?
NavigationBar is used for persistent navigation between primary destinations, commonly at the bottom of a Material application.
Q3. What does selectedIndex do?
It identifies the currently selected navigation destination.
Q4. What does Navigator.push() do?
It adds a new route to the Navigator's route stack.
Q5. What does Navigator.pop() do?
It removes the current route and returns to the previous route.
Q6. Why use IndexedStack?
It allows multiple child sections to remain mounted while displaying one selected child, which can help preserve section state.
Q7. What is nested navigation?
Nested navigation means using a separate Navigator for a subsection or self-contained flow inside a larger application.
Q8. What is deep linking?
Deep linking allows an external URL or URI to open a specific location inside an application.
Q9. When can NavigationRail be useful?
It is useful for persistent side-oriented navigation, particularly in tablet and desktop layouts.
Q10. What is go_router?
go_router is a Flutter-maintained routing package that provides a declarative API for navigation and supports complex routing scenarios such as nested routes and deep links. :contentReference[oaicite:11]{index=11}
41. Practice Exercises
- Create a Flutter application with Home, Search, Cart, and Profile sections.
- Use
NavigationBar to switch between all sections.
- Use
IndexedStack to preserve section state.
- Create a Product List screen inside the Products section.
- Navigate from Product List to Product Details.
- Pass product name and price to Product Details.
- Return a selected value from Product Details.
- Create a Profile section with a nested Settings screen.
- Implement a responsive NavigationBar/NavigationRail layout.
- Convert the application to use
go_router.
- Add a dynamic product route such as
/products/:id.
- Test a deep link that opens a specific product.
42. Quick Revision
| Concept | Purpose |
|---|
| NavigationBar | Primary bottom navigation |
| NavigationRail | Side navigation for larger layouts |
| NavigationDrawer | Drawer-based application navigation |
| Navigator | Stack-based route navigation |
| Navigator.push() | Adds a route |
| Navigator.pop() | Removes the current route |
| IndexedStack | Helps preserve multiple section widgets |
| Nested Navigator | Manages navigation inside a subsection |
| Router | Declarative and advanced navigation |
| go_router | Routing package for complex navigation |
| Deep Linking | Opens a specific application location from a URL/URI |
43. Key Takeaways
- Multi-section navigation divides an application into clear top-level destinations.
NavigationBar is useful for bottom-oriented primary navigation.
NavigationRail and NavigationDrawer provide alternative navigation layouts.
Navigator handles route-stack navigation between screens.
NavigationBar and Navigator can be used together.
IndexedStack can help preserve the state of multiple sections.
- Nested navigators are useful for self-contained navigation flows.
- Responsive applications can change navigation patterns based on available screen size.
- Deep linking allows users to open specific application locations directly.
- For advanced routing requirements, Flutter supports Router-based navigation and routing packages such as
go_router. :contentReference[oaicite:12]{index=12}
44. Useful Resources
Official Flutter Navigation Documentation: Flutter Navigation and Routing
Flutter Navigation Cookbook: Flutter Navigation Cookbook
Flutter Deep Linking: Flutter Deep Linking Documentation
Flutter Material Components: Flutter Material Widgets
JustAcademy Flutter Training: JustAcademy Flutter Training Course
Register for Course Demo: Register for Flutter Course Demo