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(
title: 'Nested Navigation Demo',
home: const NestedNavigationScreen(),
);
}
}
class NestedNavigationScreen extends StatefulWidget {
const NestedNavigationScreen({super.key});
@override
State<NestedNavigationScreen> createState() => _NestedNavigationScreenState();
}
class _NestedNavigationScreenState extends State<NestedNavigationScreen> {
int _currentIndex = 0;
final GlobalKey<NavigatorState> _homeNavigatorKey = GlobalKey<NavigatorState>();
final GlobalKey<NavigatorState> _settingsNavigatorKey = GlobalKey<NavigatorState>();
@override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () async {
final isFirstRouteInCurrentTab = await _currentIndex == 0
? !_homeNavigatorKey.currentState!.canPop()
: !_settingsNavigatorKey.currentState!.canPop();
if (isFirstRouteInCurrentTab) {
// If user is on first route of current tab, allow app to close
return true;
} else {
// Pop the current tab's navigator
if (_currentIndex == 0) {
_homeNavigatorKey.currentState!.pop();
} else {
_settingsNavigatorKey.currentState!.pop();
}
return false;
}
},
child: Scaffold(
body: Stack(
children: [
Offstage(
offstage: _currentIndex != 0,
child: Navigator(
key: _homeNavigatorKey,
onGenerateRoute: (routeSettings) {
return MaterialPageRoute(
builder: (context) => const HomePage(),
);
},
),
),
Offstage(
offstage: _currentIndex != 1,
child: Navigator(
key: _settingsNavigatorKey,
onGenerateRoute: (routeSettings) {
return MaterialPageRoute(
builder: (context) => const SettingsPage(),
);
},
),
),
],
),
bottomNavigationBar: BottomNavigationBar(
currentIndex: _currentIndex,
onTap: (index) {
setState(() {
_currentIndex = index;
});
},
items: const [
BottomNavigationBarItem(icon: Icon(Icons.home), label: 'Home'),
BottomNavigationBarItem(icon: Icon(Icons.settings), label: 'Settings'),
],
),
),
);
}
}
class HomePage extends StatelessWidget {
const HomePage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Home Page')),
body: Center(
child: ElevatedButton(
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(builder: (context) => const DetailsPage()),
);
},
child: const Text('Go to Details'),
),
),
);
}
}
class DetailsPage extends StatelessWidget {
const DetailsPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Details Page')),
body: const Center(child: Text('Details content here')),
);
}
}
class SettingsPage extends StatelessWidget {
const SettingsPage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Settings Page')),
body: Center(
child: ElevatedButton(
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(builder: (context) => const ProfilePage()),
);
},
child: const Text('Go to Profile'),
),
),
);
}
}
class ProfilePage extends StatelessWidget {
const ProfilePage({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Profile Page')),
body: const Center(child: Text('Profile content here')),
);
}
}
This Flutter app uses a BottomNavigationBar with two tabs: Home and Settings.
Each tab has its own Navigator with a unique GlobalKey. This allows each tab to keep its own navigation stack separately.
The Stack widget with Offstage widgets shows only the active tab's navigator, hiding the other but preserving its state.
Pressing buttons on each tab pushes new pages onto that tab's navigator stack.
The WillPopScope intercepts the back button to pop pages from the current tab's stack before exiting the app.
This setup mimics real apps where each tab has independent navigation history.