I want to implement the following navigation:
GoRouter
├─ StatefulShellRoute-1(root of the app)
│ ├─ GoRoute(/tab-1)
│ ├─ GoRoute(/tab-2)
│ ├─ GoRoute(/tab-3)
│ ├─ GoRoute(/tab-4)
│ ├─ GoRoute(/tab-5)
├─ StatefulShellRoute-2(a menu can be opened from anywhere in the app)
│ ├─ GoRoute(/menu/tab-1)
│ ├─ GoRoute(/menu/tab-2)
│ ├─ GoRoute(/menu/tab-3)
Navigation seems fine at first but I have a problem where if I push /menu/tab-1 from /tab-n, then I navigate with goBranch to /menu/tab-2, I cannot pop back to /tab-n as the navigator complains there is nothing to pop.
Full code:
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
void main() {
runApp(const MainApp());
}
abstract class Routes {
static const tab1 = "/tab-1";
static const tab2 = "/tab-2";
static const tab3 = "/tab-3";
static const tab4 = "/tab-4";
static const tab5 = "/tab-5";
static const test = "/test";
static const menuTab1 = "/menu/tab-1";
static const menuTab2 = "/menu/tab-2";
static const menuTab3 = "/menu/tab-3";
}
abstract class AppRouter {
static final parentNavigatorKey = GlobalKey<NavigatorState>();
static final tab1NavigatorKey = GlobalKey<NavigatorState>();
static final tab2NavigatorKey = GlobalKey<NavigatorState>();
static final tab3NavigatorKey = GlobalKey<NavigatorState>();
static final tab4NavigatorKey = GlobalKey<NavigatorState>();
static final tab5NavigatorKey = GlobalKey<NavigatorState>();
static final menuTab1NavigatorKey = GlobalKey<NavigatorState>();
static final menuTab2NavigatorKey = GlobalKey<NavigatorState>();
static final menuTab3NavigatorKey = GlobalKey<NavigatorState>();
static Page getPage({
required Widget child,
required GoRouterState state,
}) {
return MaterialPage(
key: state.pageKey,
child: child,
);
}
static GoRouter router = GoRouter(
navigatorKey: parentNavigatorKey,
initialLocation: Routes.tab1,
routes: [
StatefulShellRoute.indexedStack(
parentNavigatorKey: parentNavigatorKey,
pageBuilder: (
BuildContext context,
GoRouterState state,
StatefulNavigationShell navigationShell,
) {
return getPage(
child: RootNavigation(
navigationShell: navigationShell,
),
state: state,
);
},
branches: [
StatefulShellBranch(
navigatorKey: tab1NavigatorKey,
routes: [
GoRoute(
path: Routes.tab1,
pageBuilder: (context, GoRouterState state) {
return getPage(
child: const Center(
child: Text("Tab 1 content"),
),
state: state,
);
},
),
],
),
StatefulShellBranch(
navigatorKey: tab2NavigatorKey,
routes: [
GoRoute(
path: Routes.tab2,
pageBuilder: (context, state) {
return getPage(
child: const Center(
child: Text("Tab 2 content"),
),
state: state,
);
},
),
],
),
StatefulShellBranch(
navigatorKey: tab3NavigatorKey,
routes: [
GoRoute(
path: Routes.tab3,
pageBuilder: (context, state) {
return getPage(
child: const Center(
child: Text("Tab 3 content"),
),
state: state,
);
},
),
],
),
StatefulShellBranch(
navigatorKey: tab4NavigatorKey,
routes: [
GoRoute(
path: Routes.tab4,
pageBuilder: (context, state) {
return getPage(
child: const Center(
child: Text("Tab 4 content"),
),
state: state,
);
},
),
],
),
StatefulShellBranch(
navigatorKey: tab5NavigatorKey,
routes: [
GoRoute(
path: Routes.tab5,
pageBuilder: (context, state) {
return getPage(
child: const Center(
child: Text("Tab 5 content"),
),
state: state,
);
},
),
],
),
],
),
StatefulShellRoute.indexedStack(
parentNavigatorKey: parentNavigatorKey,
pageBuilder: (
BuildContext context,
GoRouterState state,
StatefulNavigationShell navigationShell,
) {
return getPage(
child: Menu(
shell: navigationShell,
),
state: state,
);
},
branches: [
StatefulShellBranch(
navigatorKey: menuTab1NavigatorKey,
routes: [
GoRoute(
path: Routes.menuTab1,
pageBuilder: (context, GoRouterState state) {
return getPage(
child: Center(
child: const Text("Menu tab 1"),
),
state: state,
);
},
),
],
),
StatefulShellBranch(
navigatorKey: menuTab2NavigatorKey,
routes: [
GoRoute(
path: Routes.menuTab2,
pageBuilder: (context, state) {
return getPage(
child: Center(
child: const Text("Menu tab 2"),
),
state: state,
);
},
),
],
),
StatefulShellBranch(
navigatorKey: menuTab3NavigatorKey,
routes: [
GoRoute(
path: Routes.menuTab3,
pageBuilder: (context, state) {
return getPage(
child: Center(
child: const Text("Menu tab 3"),
),
state: state,
);
},
),
],
),
],
),
GoRoute(
parentNavigatorKey: parentNavigatorKey,
path: Routes.test,
builder: (context, state) {
return Test();
},
),
],
);
}
class Test extends StatelessWidget {
const Test({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Hello"),
),
body: Center(
child: Text("TEST!"),
),
);
}
}
class MainApp extends StatelessWidget {
const MainApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp.router(
routerConfig: AppRouter.router,
debugShowCheckedModeBanner: false,
);
}
}
class RootNavigation extends StatelessWidget {
const RootNavigation({
required this.navigationShell,
});
final StatefulNavigationShell navigationShell;
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
navigationShell,
Container(
// color: Colors.lightGreen,
child: Column(
children: [
Text(
"URL Based Navigation",
style: Theme.of(context).textTheme.headlineLarge,
),
ElevatedButton(
onPressed: () {
context.go(Routes.tab1);
},
child: Text("Navigate to: Tab 1"),
),
ElevatedButton(
onPressed: () {
context.go(Routes.tab2);
},
child: Text("Navigate to: Tab 2"),
),
ElevatedButton(
onPressed: () {
context.go(Routes.tab3);
},
child: Text("Navigate to: Tab 3"),
),
ElevatedButton(
onPressed: () {
context.go(Routes.tab4);
},
child: Text("Navigate to: Tab 4"),
),
ElevatedButton(
onPressed: () {
context.go(Routes.tab5);
},
child: Text("Navigate to: Tab 5"),
),
ElevatedButton(
onPressed: () {
context.push(Routes.menuTab1);
},
child: Text("Navigate to: Menu 1"),
),
ElevatedButton(
onPressed: () {
context.push(Routes.menuTab2);
},
child: Text("Navigate to: Menu 2"),
),
ElevatedButton(
onPressed: () {
context.push(Routes.menuTab3);
},
child: Text("Navigate to: Menu 3"),
),
ElevatedButton(
onPressed: () {
context.push(Routes.test);
},
child: Text("Navigate to: Test"),
),
],
),
),
],
),
),
),
bottomNavigationBar: AppNavigationBar(
child: navigationShell,
),
);
}
}
class AppNavigationBar extends StatefulWidget {
const AppNavigationBar({
super.key,
required this.child,
});
final StatefulNavigationShell child;
@override
State<AppNavigationBar> createState() => _AppNavigationBarState();
}
class _AppNavigationBarState extends State<AppNavigationBar> {
@override
Widget build(BuildContext context) {
return NavigationBar(
selectedIndex: widget.child.currentIndex,
onDestinationSelected: (index) {
widget.child.goBranch(
index,
initialLocation: index == widget.child.currentIndex,
);
setState(() {});
},
destinations: const <Widget>[
NavigationDestination(
icon: Icon(Icons.hub_outlined),
selectedIcon: Icon(Icons.hub),
label: "Tab 1",
),
NavigationDestination(
icon: Icon(Icons.view_carousel_outlined),
selectedIcon: Icon(Icons.view_carousel),
label: "Tab 2",
),
NavigationDestination(
icon: Icon(Icons.view_carousel_outlined),
selectedIcon: Icon(Icons.view_carousel),
label: "Tab 3",
),
NavigationDestination(
icon: Icon(Icons.view_carousel_outlined),
selectedIcon: Icon(Icons.view_carousel),
label: "Tab 4",
),
NavigationDestination(
icon: Icon(Icons.view_carousel_outlined),
selectedIcon: Icon(Icons.view_carousel),
label: "Tab 5",
),
],
);
}
}
enum MenuSegment { MENU1, MENU2, MENU3 }
class Menu extends StatefulWidget {
Menu({
required this.shell,
});
final StatefulNavigationShell shell;
@override
State<Menu> createState() => _MenuState();
}
class _MenuState extends State<Menu> {
void initState() {
currentlySelectedMenuSegment =
MenuSegment.values[widget.shell.currentIndex];
super.initState();
}
late MenuSegment currentlySelectedMenuSegment;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
leading: IconButton(
onPressed: () {
context.pop();
},
icon: Icon(Icons.arrow_back),
),
actions: [
IconButton(
onPressed: () {
context.pop();
},
icon: Icon(Icons.close),
),
],
backgroundColor: Theme.of(context).colorScheme.background,
elevation: 0,
),
body: SafeArea(
bottom: false,
child: SingleChildScrollView(
child: Column(
children: [
Column(
children: [
const SizedBox(height: 16),
const Text(
"MENU WELCOME",
),
const SizedBox(height: 16),
SegmentedButton<MenuSegment>(
style: const ButtonStyle(
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
visualDensity: VisualDensity(
horizontal: -1,
vertical: -1,
),
),
onSelectionChanged: (e) {
// onMenuSegmentSelected(e.first);
int index = MenuSegment.values.indexOf(e.first);
widget.shell.goBranch(
index,
initialLocation: index == widget.shell.currentIndex,
);
setState(() {
currentlySelectedMenuSegment = e.first;
});
},
segments: const [
ButtonSegment<MenuSegment>(
value: MenuSegment.MENU1,
label: Text("Menu 1"),
icon: Icon(Icons.account_circle_outlined),
),
ButtonSegment<MenuSegment>(
value: MenuSegment.MENU2,
label: Text("Menu 2"),
icon: Icon(Icons.settings_outlined),
),
ButtonSegment<MenuSegment>(
value: MenuSegment.MENU3,
label: Text("Menu 3"),
icon: Icon(Icons.credit_card_outlined),
),
],
selected: {currentlySelectedMenuSegment},
showSelectedIcon: false,
),
],
),
widget.shell,
],
),
),
),
);
}
}
Try using
Navigator.of(context).pop();instead ofcontext.pop();.