Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions packages/go_router/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 14.2.0

- Adds preload support to StatefulShellRoute, configurable via `preload` parameter on StatefulShellBranch.

## 14.1.4

- Fixes a URL in `navigation.md`.
Expand Down Expand Up @@ -39,6 +43,10 @@

- Fixes an issue where deep links without path caused an exception

## 13.3.0

- Adds preload support to StatefulShellRoute, configurable via `preload` parameter on StatefulShellBranch.

## 13.2.2

- Fixes restoreRouteInformation issue when GoRouter.optionURLReflectsImperativeAPIs is true and the last match is ShellRouteMatch
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ final GlobalKey<NavigatorState> _rootNavigatorKey =
GlobalKey<NavigatorState>(debugLabel: 'root');
final GlobalKey<NavigatorState> _tabANavigatorKey =
GlobalKey<NavigatorState>(debugLabel: 'tabANav');
final GlobalKey<NavigatorState> _tabBNavigatorKey =
GlobalKey<NavigatorState>(debugLabel: 'tabBNav');
final GlobalKey<NavigatorState> _tabB1NavigatorKey =
GlobalKey<NavigatorState>(debugLabel: 'tabB1Nav');
final GlobalKey<NavigatorState> _tabB2NavigatorKey =
GlobalKey<NavigatorState>(debugLabel: 'tabB2Nav');

// This example demonstrates how to setup nested navigation using a
// BottomNavigationBar, where each bar item uses its own persistent navigator,
Expand Down Expand Up @@ -80,71 +86,69 @@ class NestedTabNavigationExampleApp extends StatelessWidget {

// The route branch for the third tab of the bottom navigation bar.
StatefulShellBranch(
navigatorKey: _tabBNavigatorKey,
// To enable preloading of the initial locations of branches, pass
// `true` for the parameter `preload` (`false` is default).
preload: true,
// StatefulShellBranch will automatically use the first descendant
// GoRoute as the initial location of the branch. If another route
// is desired, specify the location of it using the defaultLocation
// parameter.
// defaultLocation: '/c2',
// defaultLocation: '/b1',
routes: <RouteBase>[
StatefulShellRoute(
StatefulShellRoute.indexedStack(
builder: (BuildContext context, GoRouterState state,
StatefulNavigationShell navigationShell) {
// Just like with the top level StatefulShellRoute, no
// customization is done in the builder function.
return navigationShell;
return TabbedRootScreen(navigationShell: navigationShell);
},
navigatorContainerBuilder: (BuildContext context,
StatefulNavigationShell navigationShell,
List<Widget> children) {
// Returning a customized container for the branch
// Navigators (i.e. the `List<Widget> children` argument).
//
// See TabbedRootScreen for more details on how the children
// are managed (in a TabBarView).
return TabbedRootScreen(
navigationShell: navigationShell, children: children);
},
// This bottom tab uses a nested shell, wrapping sub routes in a
// top TabBar.
branches: <StatefulShellBranch>[
StatefulShellBranch(routes: <GoRoute>[
GoRoute(
path: '/b1',
builder: (BuildContext context, GoRouterState state) =>
const TabScreen(
label: 'B1', detailsPath: '/b1/details'),
routes: <RouteBase>[
StatefulShellBranch(
navigatorKey: _tabB1NavigatorKey,
routes: <GoRoute>[
GoRoute(
path: 'details',
path: '/b1',
builder:
(BuildContext context, GoRouterState state) =>
const DetailsScreen(
label: 'B1',
withScaffold: false,
),
const TabScreen(
label: 'B1', detailsPath: '/b1/details'),
routes: <RouteBase>[
GoRoute(
path: 'details',
builder:
(BuildContext context, GoRouterState state) =>
const DetailsScreen(
label: 'B1',
withScaffold: false,
),
),
],
),
],
),
]),
StatefulShellBranch(routes: <GoRoute>[
GoRoute(
path: '/b2',
builder: (BuildContext context, GoRouterState state) =>
const TabScreen(
label: 'B2', detailsPath: '/b2/details'),
routes: <RouteBase>[
]),
StatefulShellBranch(
navigatorKey: _tabB2NavigatorKey,
// To enable preloading for all nested branches, set
// `preload` to `true` (`false` is default).
preload: true,
routes: <GoRoute>[
GoRoute(
path: 'details',
path: '/b2',
builder:
(BuildContext context, GoRouterState state) =>
const DetailsScreen(
label: 'B2',
withScaffold: false,
),
const TabScreen(
label: 'B2', detailsPath: '/b2/details'),
routes: <RouteBase>[
GoRoute(
path: 'details',
builder:
(BuildContext context, GoRouterState state) =>
const DetailsScreen(
label: 'B2',
withScaffold: false,
),
),
],
),
],
),
]),
]),
],
),
],
Expand Down Expand Up @@ -376,23 +380,20 @@ class DetailsScreenState extends State<DetailsScreen> {
/// Builds a nested shell using a [TabBar] and [TabBarView].
class TabbedRootScreen extends StatefulWidget {
/// Constructs a TabbedRootScreen
const TabbedRootScreen(
{required this.navigationShell, required this.children, super.key});
const TabbedRootScreen({required this.navigationShell, super.key});

/// The current state of the parent StatefulShellRoute.
final StatefulNavigationShell navigationShell;

/// The children (branch Navigators) to display in the [TabBarView].
final List<Widget> children;

@override
State<StatefulWidget> createState() => _TabbedRootScreenState();
}

class _TabbedRootScreenState extends State<TabbedRootScreen>
with SingleTickerProviderStateMixin {
late final int branchCount = widget.navigationShell.route.branches.length;
late final TabController _tabController = TabController(
length: widget.children.length,
length: branchCount,
vsync: this,
initialIndex: widget.navigationShell.currentIndex);

Expand All @@ -404,9 +405,9 @@ class _TabbedRootScreenState extends State<TabbedRootScreen>

@override
Widget build(BuildContext context) {
final List<Tab> tabs = widget.children
.mapIndexed((int i, _) => Tab(text: 'Tab ${i + 1}'))
.toList();
final List<Tab> tabs =
List<Tab>.generate(branchCount, (int i) => Tab(text: 'Tab ${i + 1}'))
.toList();

return Scaffold(
appBar: AppBar(
Expand All @@ -416,10 +417,7 @@ class _TabbedRootScreenState extends State<TabbedRootScreen>
tabs: tabs,
onTap: (int tappedIndex) => _onTabTap(context, tappedIndex),
)),
body: TabBarView(
controller: _tabController,
children: widget.children,
),
body: widget.navigationShell,
);
}

Expand All @@ -441,6 +439,11 @@ class TabScreen extends StatelessWidget {

@override
Widget build(BuildContext context) {
/// If preloading is enabled on the top StatefulShellRoute, this will be
/// printed directly after the app has been started, but only for the route
/// that is the initial location ('/b1')
debugPrint('Building TabScreen - $label');

return Center(
child: Column(
mainAxisSize: MainAxisSize.min,
Expand Down
2 changes: 2 additions & 0 deletions packages/go_router/example/lib/stateful_shell_route.dart
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ class NestedTabNavigationExampleApp extends StatelessWidget {
],
),
],
// To enable preloading of the initial locations of branches, pass
// 'true' for the parameter `preload` (false is default).
),

// The route branch for the second tab of the bottom navigation bar.
Expand Down
12 changes: 9 additions & 3 deletions packages/go_router/lib/src/builder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -270,16 +270,22 @@ class _CustomNavigatorState extends State<_CustomNavigator> {
route: match.route,
routerState: state,
navigatorKey: navigatorKey,
match: match,
routeMatchList: widget.matchList,
navigatorBuilder:
(List<NavigatorObserver>? observers, String? restorationScopeId) {
navigatorBuilder: (
GlobalKey<NavigatorState> navigatorKey,
ShellRouteMatch match,
RouteMatchList matchList,
List<NavigatorObserver>? observers,
String? restorationScopeId,
) {
return _CustomNavigator(
// The state needs to persist across rebuild.
key: GlobalObjectKey(navigatorKey.hashCode),
navigatorRestorationId: restorationScopeId,
navigatorKey: navigatorKey,
matches: match.matches,
matchList: widget.matchList,
matchList: matchList,
configuration: widget.configuration,
observers: observers ?? const <NavigatorObserver>[],
onPopPageWithRouteMatch: widget.onPopPageWithRouteMatch,
Expand Down
Loading