Creating complex mobile applications in Flutter often involves managing navigation between multiple screens. In such scenarios, nested navigation in Flutter using Dart becomes essential. This technique is useful for apps with tabs, side drawers, or bottom navigation bars, where each section requires its own navigation stack.

In this guide, you’ll learn how to implement nested navigation in Flutter using Dart programming language inside Android Studio. We’ll build a simple app with a bottom navigation bar, and each tab will maintain its own navigation history.
Step-by-Step Guide with Source Code
1. main.dart
import 'package:flutter/material.dart'; import 'navigation_home.dart'; void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'Nested Navigation Demo', home: NavigationHome(), ); } }
2. navigation_home.dart
import 'package:flutter/material.dart'; import 'screens/home_screen.dart'; import 'screens/profile_screen.dart'; class NavigationHome extends StatefulWidget { @override _NavigationHomeState createState() => _NavigationHomeState(); } class _NavigationHomeState extends State<NavigationHome> { int _selectedIndex = 0; final List<GlobalKey<NavigatorState>> _navigatorKeys = [ GlobalKey<NavigatorState>(), GlobalKey<NavigatorState>(), ]; final List<Widget> _screens = [ Navigator( key: GlobalKey<NavigatorState>(), onGenerateRoute: (routeSettings) { return MaterialPageRoute(builder: (_) => HomeScreen()); }, ), Navigator( key: GlobalKey<NavigatorState>(), onGenerateRoute: (routeSettings) { return MaterialPageRoute(builder: (_) => ProfileScreen()); }, ), ]; @override Widget build(BuildContext context) { return WillPopScope( onWillPop: () async { final isFirstRouteInCurrentTab = !await _navigatorKeys[_selectedIndex].currentState!.maybePop(); return isFirstRouteInCurrentTab; }, child: Scaffold( body: Stack( children: List.generate(_screens.length, (index) { return Offstage( offstage: _selectedIndex != index, child: Navigator( key: _navigatorKeys[index], onGenerateRoute: (settings) { return MaterialPageRoute( builder: (_) => index == 0 ? HomeScreen() : ProfileScreen(), ); }, ), ); }), ), bottomNavigationBar: BottomNavigationBar( currentIndex: _selectedIndex, onTap: (index) => setState(() => _selectedIndex = index), items: const [ BottomNavigationBarItem(icon: Icon(Icons.home), label: 'Home'), BottomNavigationBarItem(icon: Icon(Icons.person), label: 'Profile'), ], ), ), ); } }
3. screens/home_screen.dart
import 'package:flutter/material.dart'; import 'details_screen.dart'; class HomeScreen extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text("Home")), body: Center( child: ElevatedButton( child: Text("Go to Details"), onPressed: () { Navigator.of(context).push( MaterialPageRoute(builder: (_) => DetailsScreen()), ); }, ), ), ); } }
4. screens/profile_screen.dart
import 'package:flutter/material.dart'; class ProfileScreen extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text("Profile")), body: Center(child: Text("Profile Page")), ); } }
5. screens/details_screen.dart
import 'package:flutter/material.dart'; class DetailsScreen extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text("Details")), body: Center(child: Text("This is a detail page")), ); } }
Nested navigation helps in preserving the navigation history of each tab separately, and is very helpful in modern apps like e-commerce, education, or dashboard systems.
For deeper architectural insights, consider checking Flutter’s official navigation documentation on flutter.dev.