Skip to content

[go_router] Fixes bug that GoRouterState in top level redirect doesn'… #4173

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jun 15, 2023
Merged
Show file tree
Hide file tree
Changes from all 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
5 changes: 4 additions & 1 deletion packages/go_router/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
25 changes: 11 additions & 14 deletions packages/go_router/lib/src/builder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -145,8 +145,7 @@ class RouteBuilder {
if (matchList.isError) {
keyToPage = <GlobalKey<NavigatorState>, List<Page<Object?>>>{
navigatorKey: <Page<Object?>>[
_buildErrorPage(
context, _buildErrorState(matchList.error!, matchList.uri)),
_buildErrorPage(context, _buildErrorState(matchList)),
]
};
} else {
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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<String>('$location(error)'),
);
}
Expand Down
3 changes: 2 additions & 1 deletion packages/go_router/lib/src/configuration.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
28 changes: 17 additions & 11 deletions packages/go_router/lib/src/state.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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 <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,
Expand All @@ -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<String, String> pathParameters;

/// The query parameters for the location, e.g. {'from': '/family/f2'}
Expand All @@ -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.
Expand Down Expand Up @@ -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<String, String> pathParameters = const <String, String>{},
Expand Down
2 changes: 1 addition & 1 deletion packages/go_router/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -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

Expand Down
36 changes: 35 additions & 1 deletion packages/go_router/test/go_router_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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'], '/');
Expand All @@ -2036,6 +2036,40 @@ void main() {
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>[
Expand Down