[go_router] Fixes bug that GoRouterState in top level redirect doesn'… (#4173)
â¦t contain complete data
fixes https://github.com/flutter/flutter/issues/106461
diff --git a/packages/go_router/CHANGELOG.md b/packages/go_router/CHANGELOG.md
index bb8d8d7..b0df45c 100644
--- a/packages/go_router/CHANGELOG.md
+++ b/packages/go_router/CHANGELOG.md
@@ -1,8 +1,11 @@
+## 8.0.5
+
+- Fixes a bug that GoRouterState in top level redirect doesn't contain complete data.
+
## 8.0.4
- Updates documentations around `GoRouter.of`, `GoRouter.maybeOf`, and `BuildContext` extension.
-
## 8.0.3
- Makes namedLocation and route name related APIs case sensitive.
diff --git a/packages/go_router/lib/src/builder.dart b/packages/go_router/lib/src/builder.dart
index d68958c..c98f2f8 100644
--- a/packages/go_router/lib/src/builder.dart
+++ b/packages/go_router/lib/src/builder.dart
@@ -145,8 +145,7 @@
if (matchList.isError) {
keyToPage = <GlobalKey<NavigatorState>, List<Page<Object?>>>{
navigatorKey: <Page<Object?>>[
- _buildErrorPage(
- context, _buildErrorState(matchList.error!, matchList.uri)),
+ _buildErrorPage(context, _buildErrorState(matchList)),
]
};
} else {
@@ -325,8 +324,7 @@
if (match is ImperativeRouteMatch) {
effectiveMatchList = match.matches;
if (effectiveMatchList.isError) {
- return _buildErrorState(
- effectiveMatchList.error!, effectiveMatchList.uri);
+ return _buildErrorState(effectiveMatchList);
}
} else {
effectiveMatchList = matchList;
@@ -491,19 +489,18 @@
child: child,
);
- GoRouterState _buildErrorState(
- Exception error,
- Uri uri,
- ) {
- final String location = uri.toString();
+ GoRouterState _buildErrorState(RouteMatchList matchList) {
+ final String location = matchList.uri.toString();
+ assert(matchList.isError);
return GoRouterState(
configuration,
location: location,
- matchedLocation: uri.path,
- name: null,
- queryParameters: uri.queryParameters,
- queryParametersAll: uri.queryParametersAll,
- error: error,
+ matchedLocation: matchList.uri.path,
+ fullPath: matchList.fullPath,
+ pathParameters: matchList.pathParameters,
+ queryParameters: matchList.uri.queryParameters,
+ queryParametersAll: matchList.uri.queryParametersAll,
+ error: matchList.error,
pageKey: ValueKey<String>('$location(error)'),
);
}
diff --git a/packages/go_router/lib/src/configuration.dart b/packages/go_router/lib/src/configuration.dart
index 89fb540..865007d 100644
--- a/packages/go_router/lib/src/configuration.dart
+++ b/packages/go_router/lib/src/configuration.dart
@@ -414,9 +414,10 @@
GoRouterState(
this,
location: prevLocation,
- name: null,
// No name available at the top level trim the query params off the
// sub-location to match route.redirect
+ fullPath: prevMatchList.fullPath,
+ pathParameters: prevMatchList.pathParameters,
matchedLocation: prevMatchList.uri.path,
queryParameters: prevMatchList.uri.queryParameters,
queryParametersAll: prevMatchList.uri.queryParametersAll,
diff --git a/packages/go_router/lib/src/state.dart b/packages/go_router/lib/src/state.dart
index 9c634ed..13ee9ad 100644
--- a/packages/go_router/lib/src/state.dart
+++ b/packages/go_router/lib/src/state.dart
@@ -17,12 +17,12 @@
this._configuration, {
required this.location,
required this.matchedLocation,
- required this.name,
+ this.name,
this.path,
- this.fullPath,
- this.pathParameters = const <String, String>{},
- this.queryParameters = const <String, String>{},
- this.queryParametersAll = const <String, List<String>>{},
+ required this.fullPath,
+ required this.pathParameters,
+ required this.queryParameters,
+ required this.queryParametersAll,
this.extra,
this.error,
required this.pageKey,
@@ -42,16 +42,24 @@
/// matchedLocation = /family/f2
final String matchedLocation;
- /// The optional name of the route.
+ /// The optional name of the route associated with this app.
+ ///
+ /// This can be null for GoRouterState pass into top level redirect.
final String? name;
- /// The path to this sub-route, e.g. family/:fid
+ /// The path of the route associated with this app. e.g. family/:fid
+ ///
+ /// This can be null for GoRouterState pass into top level redirect.
final String? path;
/// The full path to this sub-route, e.g. /family/:fid
+ ///
+ /// For top level redirect, this is the entire path that matches the location.
+ /// It can be empty if go router can't find a match. In that case, the [error]
+ /// contains more information.
final String? fullPath;
- /// The parameters for this sub-route, e.g. {'fid': 'f2'}
+ /// The parameters for this match, e.g. {'fid': 'f2'}
final Map<String, String> pathParameters;
/// The query parameters for the location, e.g. {'from': '/family/f2'}
@@ -64,7 +72,7 @@
/// An extra object to pass along with the navigation.
final Object? extra;
- /// The error associated with this sub-route.
+ /// The error associated with this match.
final Exception? error;
/// A unique string key for this sub-route.
@@ -129,8 +137,6 @@
/// Get a location from route name and parameters.
/// This is useful for redirecting to a named location.
- // TODO(chunhtai): remove this method when go_router can provide a way to
- // look up named location during redirect.
String namedLocation(
String name, {
Map<String, String> pathParameters = const <String, String>{},
diff --git a/packages/go_router/pubspec.yaml b/packages/go_router/pubspec.yaml
index 390811d..db489ed 100644
--- a/packages/go_router/pubspec.yaml
+++ b/packages/go_router/pubspec.yaml
@@ -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.4
+version: 8.0.5
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
diff --git a/packages/go_router/test/go_router_test.dart b/packages/go_router/test/go_router_test.dart
index ed258aa..2ebb4af 100644
--- a/packages/go_router/test/go_router_test.dart
+++ b/packages/go_router/test/go_router_test.dart
@@ -2022,7 +2022,7 @@
expect(Uri.parse(state.location).queryParameters, isNotEmpty);
expect(Uri.parse(state.matchedLocation).queryParameters, isEmpty);
expect(state.path, isNull);
- expect(state.fullPath, isNull);
+ expect(state.fullPath, '/login');
expect(state.pathParameters.length, 0);
expect(state.queryParameters.length, 1);
expect(state.queryParameters['from'], '/');
@@ -2036,6 +2036,40 @@
expect(find.byType(LoginScreen), findsOneWidget);
});
+ testWidgets('top-level redirect state contains path parameters',
+ (WidgetTester tester) async {
+ final List<GoRoute> routes = <GoRoute>[
+ GoRoute(
+ path: '/',
+ builder: (BuildContext context, GoRouterState state) =>
+ const DummyScreen(),
+ routes: <RouteBase>[
+ GoRoute(
+ path: ':id',
+ builder: (BuildContext context, GoRouterState state) =>
+ const DummyScreen(),
+ ),
+ ]),
+ ];
+
+ final GoRouter router = await createRouter(
+ routes,
+ tester,
+ initialLocation: '/123',
+ redirect: (BuildContext context, GoRouterState state) {
+ expect(state.path, isNull);
+ expect(state.fullPath, '/:id');
+ expect(state.pathParameters.length, 1);
+ expect(state.pathParameters['id'], '123');
+ return null;
+ },
+ );
+
+ final List<RouteMatch> matches =
+ router.routerDelegate.currentConfiguration.matches;
+ expect(matches, hasLength(2));
+ });
+
testWidgets('route-level redirect state', (WidgetTester tester) async {
const String loc = '/book/0';
final List<GoRoute> routes = <GoRoute>[