diff --git a/packages/go_router/CHANGELOG.md b/packages/go_router/CHANGELOG.md index bb8d8d72027..b0df45c5fe6 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 d68958cbaa7..c98f2f8bd12 100644 --- a/packages/go_router/lib/src/builder.dart +++ b/packages/go_router/lib/src/builder.dart @@ -145,8 +145,7 @@ class RouteBuilder { if (matchList.isError) { keyToPage = , List>>{ navigatorKey: >[ - _buildErrorPage( - context, _buildErrorState(matchList.error!, matchList.uri)), + _buildErrorPage(context, _buildErrorState(matchList)), ] }; } else { @@ -325,8 +324,7 @@ class RouteBuilder { 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 @@ class RouteBuilder { 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('$location(error)'), ); } diff --git a/packages/go_router/lib/src/configuration.dart b/packages/go_router/lib/src/configuration.dart index 89fb5400de6..865007d7659 100644 --- a/packages/go_router/lib/src/configuration.dart +++ b/packages/go_router/lib/src/configuration.dart @@ -414,9 +414,10 @@ class RouteConfiguration { 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 9c634eddbee..13ee9add91e 100644 --- a/packages/go_router/lib/src/state.dart +++ b/packages/go_router/lib/src/state.dart @@ -17,12 +17,12 @@ class GoRouterState { this._configuration, { required this.location, required this.matchedLocation, - required this.name, + this.name, this.path, - this.fullPath, - this.pathParameters = const {}, - this.queryParameters = const {}, - this.queryParametersAll = const >{}, + required this.fullPath, + required this.pathParameters, + required this.queryParameters, + required this.queryParametersAll, this.extra, this.error, required this.pageKey, @@ -42,16 +42,24 @@ class GoRouterState { /// 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 pathParameters; /// The query parameters for the location, e.g. {'from': '/family/f2'} @@ -64,7 +72,7 @@ class GoRouterState { /// 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 @@ class GoRouterState { /// 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 pathParameters = const {}, diff --git a/packages/go_router/pubspec.yaml b/packages/go_router/pubspec.yaml index 390811da2d1..db489edff28 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 ed258aa7e9b..2ebb4afca77 100644 --- a/packages/go_router/test/go_router_test.dart +++ b/packages/go_router/test/go_router_test.dart @@ -2022,7 +2022,7 @@ void main() { 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 @@ void main() { expect(find.byType(LoginScreen), findsOneWidget); }); + testWidgets('top-level redirect state contains path parameters', + (WidgetTester tester) async { + final List routes = [ + GoRoute( + path: '/', + builder: (BuildContext context, GoRouterState state) => + const DummyScreen(), + routes: [ + 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 matches = + router.routerDelegate.currentConfiguration.matches; + expect(matches, hasLength(2)); + }); + testWidgets('route-level redirect state', (WidgetTester tester) async { const String loc = '/book/0'; final List routes = [