mirror of
https://github.com/flutter/packages.git
synced 2025-07-01 15:23:25 +08:00
[go_router] Adds parent navigator key to ShellRoute and StatefulShell… (#4201)
â¦Route. fixes https://github.com/flutter/flutter/issues/111678 fixes https://github.com/flutter/flutter/issues/128793
This commit is contained in:
@ -1,3 +1,7 @@
|
||||
## 8.1.0
|
||||
|
||||
- Adds parent navigator key to ShellRoute and StatefulShellRoute.
|
||||
|
||||
## 8.0.5
|
||||
|
||||
- Fixes a bug that GoRouterState in top level redirect doesn't contain complete data.
|
||||
|
@ -204,77 +204,73 @@ class RouteBuilder {
|
||||
keyToPages.putIfAbsent(navigatorKey, () => <Page<Object?>>[]).add(page);
|
||||
_buildRecursive(context, matchList, startIndex + 1, pagePopContext,
|
||||
routerNeglect, keyToPages, navigatorKey, registry);
|
||||
} else if (route is GoRoute) {
|
||||
page = _buildPageForGoRoute(context, state, match, route, pagePopContext);
|
||||
// If this GoRoute is for a different Navigator, add it to the
|
||||
} else {
|
||||
// If this RouteBase is for a different Navigator, add it to the
|
||||
// list of out of scope pages
|
||||
final GlobalKey<NavigatorState> goRouteNavKey =
|
||||
final GlobalKey<NavigatorState> routeNavKey =
|
||||
route.parentNavigatorKey ?? navigatorKey;
|
||||
if (route is GoRoute) {
|
||||
page =
|
||||
_buildPageForGoRoute(context, state, match, route, pagePopContext);
|
||||
|
||||
keyToPages.putIfAbsent(goRouteNavKey, () => <Page<Object?>>[]).add(page);
|
||||
keyToPages.putIfAbsent(routeNavKey, () => <Page<Object?>>[]).add(page);
|
||||
|
||||
_buildRecursive(context, matchList, startIndex + 1, pagePopContext,
|
||||
routerNeglect, keyToPages, navigatorKey, registry);
|
||||
} else if (route is ShellRouteBase) {
|
||||
assert(startIndex + 1 < matchList.matches.length,
|
||||
'Shell routes must always have child routes');
|
||||
// The key for the Navigator that will display this ShellRoute's page.
|
||||
final GlobalKey<NavigatorState> parentNavigatorKey = navigatorKey;
|
||||
_buildRecursive(context, matchList, startIndex + 1, pagePopContext,
|
||||
routerNeglect, keyToPages, navigatorKey, registry);
|
||||
} else if (route is ShellRouteBase) {
|
||||
assert(startIndex + 1 < matchList.matches.length,
|
||||
'Shell routes must always have child routes');
|
||||
|
||||
// Add an entry for the parent navigator if none exists.
|
||||
keyToPages.putIfAbsent(parentNavigatorKey, () => <Page<Object?>>[]);
|
||||
// Add an entry for the parent navigator if none exists.
|
||||
//
|
||||
// Calling _buildRecursive can result in adding pages to the
|
||||
// parentNavigatorKey entry's list. Store the current length so
|
||||
// that the page for this ShellRoute is placed at the right index.
|
||||
final int shellPageIdx =
|
||||
keyToPages.putIfAbsent(routeNavKey, () => <Page<Object?>>[]).length;
|
||||
|
||||
// Calling _buildRecursive can result in adding pages to the
|
||||
// parentNavigatorKey entry's list. Store the current length so
|
||||
// that the page for this ShellRoute is placed at the right index.
|
||||
final int shellPageIdx = keyToPages[parentNavigatorKey]!.length;
|
||||
// Find the the navigator key for the sub-route of this shell route.
|
||||
final RouteBase subRoute = matchList.matches[startIndex + 1].route;
|
||||
final GlobalKey<NavigatorState> shellNavigatorKey =
|
||||
route.navigatorKeyForSubRoute(subRoute);
|
||||
|
||||
// Get the current sub-route of this shell route from the match list.
|
||||
final RouteBase subRoute = matchList.matches[startIndex + 1].route;
|
||||
keyToPages.putIfAbsent(shellNavigatorKey, () => <Page<Object?>>[]);
|
||||
|
||||
// The key to provide to the shell route's Navigator.
|
||||
final GlobalKey<NavigatorState> shellNavigatorKey =
|
||||
route.navigatorKeyForSubRoute(subRoute);
|
||||
// Build the remaining pages
|
||||
_buildRecursive(context, matchList, startIndex + 1, pagePopContext,
|
||||
routerNeglect, keyToPages, shellNavigatorKey, registry);
|
||||
|
||||
// Add an entry for the shell route's navigator
|
||||
keyToPages.putIfAbsent(shellNavigatorKey, () => <Page<Object?>>[]);
|
||||
final HeroController heroController = _goHeroCache.putIfAbsent(
|
||||
shellNavigatorKey, () => _getHeroController(context));
|
||||
|
||||
// Build the remaining pages
|
||||
_buildRecursive(context, matchList, startIndex + 1, pagePopContext,
|
||||
routerNeglect, keyToPages, shellNavigatorKey, registry);
|
||||
// Build the Navigator for this shell route
|
||||
Widget buildShellNavigator(
|
||||
List<NavigatorObserver>? observers, String? restorationScopeId) {
|
||||
return _buildNavigator(
|
||||
pagePopContext.onPopPage,
|
||||
keyToPages[shellNavigatorKey]!,
|
||||
shellNavigatorKey,
|
||||
observers: observers ?? const <NavigatorObserver>[],
|
||||
restorationScopeId: restorationScopeId,
|
||||
heroController: heroController,
|
||||
);
|
||||
}
|
||||
|
||||
final HeroController heroController = _goHeroCache.putIfAbsent(
|
||||
shellNavigatorKey, () => _getHeroController(context));
|
||||
|
||||
// Build the Navigator for this shell route
|
||||
Widget buildShellNavigator(
|
||||
List<NavigatorObserver>? observers, String? restorationScopeId) {
|
||||
return _buildNavigator(
|
||||
pagePopContext.onPopPage,
|
||||
keyToPages[shellNavigatorKey]!,
|
||||
shellNavigatorKey,
|
||||
observers: observers ?? const <NavigatorObserver>[],
|
||||
restorationScopeId: restorationScopeId,
|
||||
heroController: heroController,
|
||||
// Call the ShellRouteBase to create/update the shell route state
|
||||
final ShellRouteContext shellRouteContext = ShellRouteContext(
|
||||
route: route,
|
||||
routerState: state,
|
||||
navigatorKey: shellNavigatorKey,
|
||||
routeMatchList: matchList,
|
||||
navigatorBuilder: buildShellNavigator,
|
||||
);
|
||||
|
||||
// Build the Page for this route
|
||||
page = _buildPageForShellRoute(
|
||||
context, state, match, route, pagePopContext, shellRouteContext);
|
||||
// Place the ShellRoute's Page onto the list for the parent navigator.
|
||||
keyToPages[routeNavKey]!.insert(shellPageIdx, page);
|
||||
}
|
||||
|
||||
// Call the ShellRouteBase to create/update the shell route state
|
||||
final ShellRouteContext shellRouteContext = ShellRouteContext(
|
||||
route: route,
|
||||
routerState: state,
|
||||
navigatorKey: shellNavigatorKey,
|
||||
routeMatchList: matchList,
|
||||
navigatorBuilder: buildShellNavigator,
|
||||
);
|
||||
|
||||
// Build the Page for this route
|
||||
page = _buildPageForShellRoute(
|
||||
context, state, match, route, pagePopContext, shellRouteContext);
|
||||
// Place the ShellRoute's Page onto the list for the parent navigator.
|
||||
keyToPages
|
||||
.putIfAbsent(parentNavigatorKey, () => <Page<Object?>>[])
|
||||
.insert(shellPageIdx, page);
|
||||
}
|
||||
if (page != null) {
|
||||
registry[page] = state;
|
||||
|
@ -148,73 +148,61 @@ class GoRouterDelegate extends RouterDelegate<RouteMatchList>
|
||||
/// pageless route, such as a dialog or bottom sheet.
|
||||
class _NavigatorStateIterator extends Iterator<NavigatorState> {
|
||||
_NavigatorStateIterator(this.matchList, this.root)
|
||||
: index = matchList.matches.length;
|
||||
: index = matchList.matches.length - 1;
|
||||
|
||||
final RouteMatchList matchList;
|
||||
int index = 0;
|
||||
int index;
|
||||
|
||||
final NavigatorState root;
|
||||
@override
|
||||
late NavigatorState current;
|
||||
|
||||
RouteBase _getRouteAtIndex(int index) => matchList.matches[index].route;
|
||||
|
||||
void _findsNextIndex() {
|
||||
final GlobalKey<NavigatorState>? parentNavigatorKey =
|
||||
_getRouteAtIndex(index).parentNavigatorKey;
|
||||
if (parentNavigatorKey == null) {
|
||||
index -= 1;
|
||||
return;
|
||||
}
|
||||
|
||||
for (index -= 1; index >= 0; index -= 1) {
|
||||
final RouteBase route = _getRouteAtIndex(index);
|
||||
if (route is ShellRouteBase) {
|
||||
if (route.navigatorKeyForSubRoute(_getRouteAtIndex(index + 1)) ==
|
||||
parentNavigatorKey) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
assert(root == parentNavigatorKey.currentState);
|
||||
}
|
||||
|
||||
@override
|
||||
bool moveNext() {
|
||||
if (index < 0) {
|
||||
return false;
|
||||
}
|
||||
late RouteBase subRoute;
|
||||
for (index -= 1; index >= 0; index -= 1) {
|
||||
final RouteMatch match = matchList.matches[index];
|
||||
final RouteBase route = match.route;
|
||||
if (route is GoRoute && route.parentNavigatorKey != null) {
|
||||
final GlobalKey<NavigatorState> parentNavigatorKey =
|
||||
route.parentNavigatorKey!;
|
||||
final ModalRoute<Object?>? parentModalRoute =
|
||||
ModalRoute.of(parentNavigatorKey.currentContext!);
|
||||
// The ModalRoute can be null if the parentNavigatorKey references the
|
||||
// root navigator.
|
||||
if (parentModalRoute == null) {
|
||||
index = -1;
|
||||
assert(root == parentNavigatorKey.currentState);
|
||||
current = root;
|
||||
return true;
|
||||
}
|
||||
// It must be a ShellRoute that holds this parentNavigatorKey;
|
||||
// otherwise, parentModalRoute would have been null. Updates the index
|
||||
// to the ShellRoute
|
||||
for (index -= 1; index >= 0; index -= 1) {
|
||||
final RouteBase route = matchList.matches[index].route;
|
||||
if (route is ShellRoute) {
|
||||
if (route.navigatorKey == parentNavigatorKey) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// There may be a pageless route on top of ModalRoute that the
|
||||
// NavigatorState of parentNavigatorKey is in. For example, an open
|
||||
// dialog. In that case we want to find the navigator that host the
|
||||
// pageless route.
|
||||
if (parentModalRoute.isCurrent == false) {
|
||||
continue;
|
||||
}
|
||||
_findsNextIndex();
|
||||
|
||||
current = parentNavigatorKey.currentState!;
|
||||
return true;
|
||||
} else if (route is ShellRouteBase) {
|
||||
while (index >= 0) {
|
||||
final RouteBase route = _getRouteAtIndex(index);
|
||||
if (route is ShellRouteBase) {
|
||||
final GlobalKey<NavigatorState> navigatorKey =
|
||||
route.navigatorKeyForSubRoute(_getRouteAtIndex(index + 1));
|
||||
// Must have a ModalRoute parent because the navigator ShellRoute
|
||||
// created must not be the root navigator.
|
||||
final GlobalKey<NavigatorState> navigatorKey =
|
||||
route.navigatorKeyForSubRoute(subRoute);
|
||||
final ModalRoute<Object?> parentModalRoute =
|
||||
ModalRoute.of(navigatorKey.currentContext!)!;
|
||||
// There may be pageless route on top of ModalRoute that the
|
||||
// parentNavigatorKey is in. For example an open dialog.
|
||||
if (parentModalRoute.isCurrent == false) {
|
||||
continue;
|
||||
if (parentModalRoute.isCurrent) {
|
||||
current = navigatorKey.currentState!;
|
||||
return true;
|
||||
}
|
||||
current = navigatorKey.currentState!;
|
||||
return true;
|
||||
}
|
||||
subRoute = route;
|
||||
_findsNextIndex();
|
||||
}
|
||||
assert(index == -1);
|
||||
current = root;
|
||||
|
@ -98,12 +98,20 @@ import 'typedefs.dart';
|
||||
@immutable
|
||||
abstract class RouteBase {
|
||||
const RouteBase._({
|
||||
this.routes = const <RouteBase>[],
|
||||
required this.routes,
|
||||
required this.parentNavigatorKey,
|
||||
});
|
||||
|
||||
/// The list of child routes associated with this route.
|
||||
final List<RouteBase> routes;
|
||||
|
||||
/// An optional key specifying which Navigator to display this route's screen
|
||||
/// onto.
|
||||
///
|
||||
/// Specifying the root Navigator will stack this route onto that
|
||||
/// Navigator instead of the nearest ShellRoute ancestor.
|
||||
final GlobalKey<NavigatorState>? parentNavigatorKey;
|
||||
|
||||
/// Builds a lists containing the provided routes along with all their
|
||||
/// descendant [routes].
|
||||
static Iterable<RouteBase> routesRecursively(Iterable<RouteBase> routes) {
|
||||
@ -137,7 +145,7 @@ class GoRoute extends RouteBase {
|
||||
this.name,
|
||||
this.builder,
|
||||
this.pageBuilder,
|
||||
this.parentNavigatorKey,
|
||||
super.parentNavigatorKey,
|
||||
this.redirect,
|
||||
super.routes = const <RouteBase>[],
|
||||
}) : assert(path.isNotEmpty, 'GoRoute path cannot be empty'),
|
||||
@ -301,13 +309,6 @@ class GoRoute extends RouteBase {
|
||||
/// re-evaluation will be triggered if the [InheritedWidget] changes.
|
||||
final GoRouterRedirect? redirect;
|
||||
|
||||
/// An optional key specifying which Navigator to display this route's screen
|
||||
/// onto.
|
||||
///
|
||||
/// Specifying the root Navigator will stack this route onto that
|
||||
/// Navigator instead of the nearest ShellRoute ancestor.
|
||||
final GlobalKey<NavigatorState>? parentNavigatorKey;
|
||||
|
||||
// TODO(chunhtai): move all regex related help methods to path_utils.dart.
|
||||
/// Match this route against a location.
|
||||
RegExpMatch? matchPatternAsPrefix(String loc) =>
|
||||
@ -333,7 +334,9 @@ class GoRoute extends RouteBase {
|
||||
/// as [ShellRoute] and [StatefulShellRoute].
|
||||
abstract class ShellRouteBase extends RouteBase {
|
||||
/// Constructs a [ShellRouteBase].
|
||||
const ShellRouteBase._({super.routes}) : super._();
|
||||
const ShellRouteBase._(
|
||||
{required super.routes, required super.parentNavigatorKey})
|
||||
: super._();
|
||||
|
||||
/// Attempts to build the Widget representing this shell route.
|
||||
///
|
||||
@ -496,7 +499,8 @@ class ShellRoute extends ShellRouteBase {
|
||||
this.builder,
|
||||
this.pageBuilder,
|
||||
this.observers,
|
||||
super.routes,
|
||||
required super.routes,
|
||||
super.parentNavigatorKey,
|
||||
GlobalKey<NavigatorState>? navigatorKey,
|
||||
this.restorationScopeId,
|
||||
}) : assert(routes.isNotEmpty),
|
||||
@ -653,6 +657,7 @@ class StatefulShellRoute extends ShellRouteBase {
|
||||
this.builder,
|
||||
this.pageBuilder,
|
||||
required this.navigatorContainerBuilder,
|
||||
super.parentNavigatorKey,
|
||||
this.restorationScopeId,
|
||||
}) : assert(branches.isNotEmpty),
|
||||
assert((pageBuilder != null) ^ (builder != null),
|
||||
@ -676,12 +681,14 @@ class StatefulShellRoute extends ShellRouteBase {
|
||||
StatefulShellRoute.indexedStack({
|
||||
required List<StatefulShellBranch> branches,
|
||||
StatefulShellRouteBuilder? builder,
|
||||
GlobalKey<NavigatorState>? parentNavigatorKey,
|
||||
StatefulShellRoutePageBuilder? pageBuilder,
|
||||
String? restorationScopeId,
|
||||
}) : this(
|
||||
branches: branches,
|
||||
builder: builder,
|
||||
pageBuilder: pageBuilder,
|
||||
parentNavigatorKey: parentNavigatorKey,
|
||||
restorationScopeId: restorationScopeId,
|
||||
navigatorContainerBuilder: _indexedStackContainerBuilder,
|
||||
);
|
||||
|
@ -1,7 +1,7 @@
|
||||
name: go_router
|
||||
description: A declarative router for Flutter based on Navigation 2 supporting
|
||||
deep linking, data-driven routes and more
|
||||
version: 8.0.5
|
||||
version: 8.1.0
|
||||
repository: https://github.com/flutter/packages/tree/main/packages/go_router
|
||||
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+go_router%22
|
||||
|
||||
|
@ -7,6 +7,7 @@ import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:go_router/src/match.dart';
|
||||
import 'package:go_router/src/misc/error_screen.dart';
|
||||
import 'package:go_router/src/misc/errors.dart';
|
||||
|
||||
import 'test_helpers.dart';
|
||||
|
||||
@ -96,6 +97,73 @@ void main() {
|
||||
await goRouter.routerDelegate.popRoute();
|
||||
expect(await goRouter.routerDelegate.popRoute(), isFalse);
|
||||
});
|
||||
|
||||
testWidgets('throw if nothing to pop', (WidgetTester tester) async {
|
||||
final GlobalKey<NavigatorState> rootKey = GlobalKey<NavigatorState>();
|
||||
final GlobalKey<NavigatorState> navKey = GlobalKey<NavigatorState>();
|
||||
final GoRouter goRouter = await createRouter(
|
||||
<RouteBase>[
|
||||
ShellRoute(
|
||||
navigatorKey: rootKey,
|
||||
builder: (_, __, Widget child) => child,
|
||||
routes: <RouteBase>[
|
||||
ShellRoute(
|
||||
parentNavigatorKey: rootKey,
|
||||
navigatorKey: navKey,
|
||||
builder: (_, __, Widget child) => child,
|
||||
routes: <RouteBase>[
|
||||
GoRoute(
|
||||
path: '/',
|
||||
parentNavigatorKey: navKey,
|
||||
builder: (_, __) => const Text('Home'),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
tester,
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text('Home'), findsOneWidget);
|
||||
String? message;
|
||||
try {
|
||||
goRouter.pop();
|
||||
} on GoError catch (e) {
|
||||
message = e.message;
|
||||
}
|
||||
expect(message, 'There is nothing to pop');
|
||||
});
|
||||
|
||||
testWidgets('poproute return false if nothing to pop',
|
||||
(WidgetTester tester) async {
|
||||
final GlobalKey<NavigatorState> rootKey = GlobalKey<NavigatorState>();
|
||||
final GlobalKey<NavigatorState> navKey = GlobalKey<NavigatorState>();
|
||||
final GoRouter goRouter = await createRouter(
|
||||
<RouteBase>[
|
||||
ShellRoute(
|
||||
navigatorKey: rootKey,
|
||||
builder: (_, __, Widget child) => child,
|
||||
routes: <RouteBase>[
|
||||
ShellRoute(
|
||||
parentNavigatorKey: rootKey,
|
||||
navigatorKey: navKey,
|
||||
builder: (_, __, Widget child) => child,
|
||||
routes: <RouteBase>[
|
||||
GoRoute(
|
||||
path: '/',
|
||||
parentNavigatorKey: navKey,
|
||||
builder: (_, __) => const Text('Home'),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
tester,
|
||||
);
|
||||
expect(await goRouter.routerDelegate.popRoute(), isFalse);
|
||||
});
|
||||
});
|
||||
|
||||
group('push', () {
|
||||
|
@ -2,9 +2,12 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
|
||||
import 'test_helpers.dart';
|
||||
|
||||
void main() {
|
||||
test('throws when a builder is not set', () {
|
||||
expect(() => GoRoute(path: '/'), throwsA(isAssertionError));
|
||||
@ -17,4 +20,139 @@ void main() {
|
||||
test('does not throw when only redirect is provided', () {
|
||||
GoRoute(path: '/', redirect: (_, __) => '/a');
|
||||
});
|
||||
|
||||
testWidgets('ShellRoute can use parent navigator key',
|
||||
(WidgetTester tester) async {
|
||||
final GlobalKey<NavigatorState> rootNavigatorKey =
|
||||
GlobalKey<NavigatorState>();
|
||||
final GlobalKey<NavigatorState> shellNavigatorKey =
|
||||
GlobalKey<NavigatorState>();
|
||||
|
||||
final List<RouteBase> routes = <RouteBase>[
|
||||
ShellRoute(
|
||||
navigatorKey: shellNavigatorKey,
|
||||
builder: (BuildContext context, GoRouterState state, Widget child) {
|
||||
return Scaffold(
|
||||
body: Column(
|
||||
children: <Widget>[
|
||||
const Text('Screen A'),
|
||||
Expanded(child: child),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
routes: <RouteBase>[
|
||||
GoRoute(
|
||||
path: '/b',
|
||||
builder: (BuildContext context, GoRouterState state) {
|
||||
return const Scaffold(
|
||||
body: Text('Screen B'),
|
||||
);
|
||||
},
|
||||
routes: <RouteBase>[
|
||||
ShellRoute(
|
||||
parentNavigatorKey: rootNavigatorKey,
|
||||
builder:
|
||||
(BuildContext context, GoRouterState state, Widget child) {
|
||||
return Scaffold(
|
||||
body: Column(
|
||||
children: <Widget>[
|
||||
const Text('Screen D'),
|
||||
Expanded(child: child),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
routes: <RouteBase>[
|
||||
GoRoute(
|
||||
path: 'c',
|
||||
builder: (BuildContext context, GoRouterState state) {
|
||||
return const Scaffold(
|
||||
body: Text('Screen C'),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
];
|
||||
|
||||
await createRouter(routes, tester,
|
||||
initialLocation: '/b/c', navigatorKey: rootNavigatorKey);
|
||||
expect(find.text('Screen A'), findsNothing);
|
||||
expect(find.text('Screen B'), findsNothing);
|
||||
expect(find.text('Screen D'), findsOneWidget);
|
||||
expect(find.text('Screen C'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('StatefulShellRoute can use parent navigator key',
|
||||
(WidgetTester tester) async {
|
||||
final GlobalKey<NavigatorState> rootNavigatorKey =
|
||||
GlobalKey<NavigatorState>();
|
||||
final GlobalKey<NavigatorState> shellNavigatorKey =
|
||||
GlobalKey<NavigatorState>();
|
||||
|
||||
final List<RouteBase> routes = <RouteBase>[
|
||||
ShellRoute(
|
||||
navigatorKey: shellNavigatorKey,
|
||||
builder: (BuildContext context, GoRouterState state, Widget child) {
|
||||
return Scaffold(
|
||||
body: Column(
|
||||
children: <Widget>[
|
||||
const Text('Screen A'),
|
||||
Expanded(child: child),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
routes: <RouteBase>[
|
||||
GoRoute(
|
||||
path: '/b',
|
||||
builder: (BuildContext context, GoRouterState state) {
|
||||
return const Scaffold(
|
||||
body: Text('Screen B'),
|
||||
);
|
||||
},
|
||||
routes: <RouteBase>[
|
||||
StatefulShellRoute.indexedStack(
|
||||
parentNavigatorKey: rootNavigatorKey,
|
||||
builder: (_, __, StatefulNavigationShell navigationShell) {
|
||||
return Column(
|
||||
children: <Widget>[
|
||||
const Text('Screen D'),
|
||||
Expanded(child: navigationShell),
|
||||
],
|
||||
);
|
||||
},
|
||||
branches: <StatefulShellBranch>[
|
||||
StatefulShellBranch(
|
||||
routes: <RouteBase>[
|
||||
GoRoute(
|
||||
path: 'c',
|
||||
builder: (BuildContext context, GoRouterState state) {
|
||||
return const Scaffold(
|
||||
body: Text('Screen C'),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
];
|
||||
|
||||
await createRouter(routes, tester,
|
||||
initialLocation: '/b/c', navigatorKey: rootNavigatorKey);
|
||||
expect(find.text('Screen A'), findsNothing);
|
||||
expect(find.text('Screen B'), findsNothing);
|
||||
expect(find.text('Screen D'), findsOneWidget);
|
||||
expect(find.text('Screen C'), findsOneWidget);
|
||||
});
|
||||
}
|
||||
|
Reference in New Issue
Block a user