Skip to content

Commit 21e3340

Browse files
authored
[go_router] Makes GoRouterState lookup more robust. (#6920)
fixes flutter/flutter#132824
1 parent da04812 commit 21e3340

File tree

4 files changed

+75
-21
lines changed

4 files changed

+75
-21
lines changed

packages/go_router/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 14.2.1
2+
3+
- Makes GoRouterState lookup more robust.
4+
15
## 14.2.0
26

37
- Added proper `redirect` handling for `ShellRoute.$route` and `StatefulShellRoute.$route` for proper redirection handling in case of code generation.

packages/go_router/lib/src/state.dart

Lines changed: 37 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ class GoRouterState {
9898
///
9999
/// This method cannot be called during [GoRoute.pageBuilder] or
100100
/// [ShellRoute.pageBuilder] since there is no [GoRouterState] to be
101-
/// associated with.
101+
/// associated with yet.
102102
///
103103
/// To access GoRouterState from a widget.
104104
///
@@ -116,26 +116,41 @@ class GoRouterState {
116116
/// }
117117
/// ```
118118
static GoRouterState of(BuildContext context) {
119-
final ModalRoute<Object?>? route = ModalRoute.of(context);
120-
if (route == null) {
121-
throw GoError('There is no modal route above the current context.');
122-
}
123-
final RouteSettings settings = route.settings;
124-
if (settings is! Page<Object?>) {
125-
throw GoError(
126-
'The parent route must be a page route to have a GoRouterState');
127-
}
128-
final GoRouterStateRegistryScope? scope = context
129-
.dependOnInheritedWidgetOfExactType<GoRouterStateRegistryScope>();
130-
if (scope == null) {
131-
throw GoError(
132-
'There is no GoRouterStateRegistryScope above the current context.');
119+
ModalRoute<Object?>? route;
120+
GoRouterStateRegistryScope? scope;
121+
while (true) {
122+
route = ModalRoute.of(context);
123+
if (route == null) {
124+
throw _noGoRouterStateError;
125+
}
126+
final RouteSettings settings = route.settings;
127+
if (settings is Page<Object?>) {
128+
scope = context
129+
.dependOnInheritedWidgetOfExactType<GoRouterStateRegistryScope>();
130+
if (scope == null) {
131+
throw _noGoRouterStateError;
132+
}
133+
final GoRouterState? state = scope.notifier!
134+
._createPageRouteAssociation(
135+
route.settings as Page<Object?>, route);
136+
if (state != null) {
137+
return state;
138+
}
139+
}
140+
final NavigatorState? state = Navigator.maybeOf(context);
141+
if (state == null) {
142+
throw _noGoRouterStateError;
143+
}
144+
context = state.context;
133145
}
134-
final GoRouterState state =
135-
scope.notifier!._createPageRouteAssociation(settings, route);
136-
return state;
137146
}
138147

148+
static GoError get _noGoRouterStateError => GoError(
149+
'There is no GoRouterState above the current context. '
150+
'This method should only be called under the sub tree of a '
151+
'RouteBase.builder.',
152+
);
153+
139154
/// Get a location from route name and parameters.
140155
/// This is useful for redirecting to a named location.
141156
String namedLocation(
@@ -207,10 +222,12 @@ class GoRouterStateRegistry extends ChangeNotifier {
207222
final Map<Route<Object?>, Page<Object?>> _routePageAssociation =
208223
<ModalRoute<Object?>, Page<Object?>>{};
209224

210-
GoRouterState _createPageRouteAssociation(
225+
GoRouterState? _createPageRouteAssociation(
211226
Page<Object?> page, ModalRoute<Object?> route) {
212227
assert(route.settings == page);
213-
assert(registry.containsKey(page));
228+
if (!registry.containsKey(page)) {
229+
return null;
230+
}
214231
final Page<Object?>? oldPage = _routePageAssociation[route];
215232
if (oldPage == null) {
216233
// This is a new association.

packages/go_router/pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name: go_router
22
description: A declarative router for Flutter based on Navigation 2 supporting
33
deep linking, data-driven routes and more
4-
version: 14.2.0
4+
version: 14.2.1
55
repository: https://github.com/flutter/packages/tree/main/packages/go_router
66
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+go_router%22
77

packages/go_router/test/go_router_state_test.dart

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,39 @@ void main() {
185185
expect(find.byKey(key), findsNothing);
186186
});
187187

188+
testWidgets(
189+
'GoRouterState look up should be resilient when there is a nested navigator.',
190+
(WidgetTester tester) async {
191+
final List<GoRoute> routes = <GoRoute>[
192+
GoRoute(
193+
path: '/',
194+
builder: (_, __) {
195+
return Scaffold(
196+
appBar: AppBar(),
197+
body: Navigator(
198+
pages: <Page<void>>[
199+
MaterialPage<void>(
200+
child: Builder(
201+
builder: (BuildContext context) {
202+
return Center(
203+
child: Text(GoRouterState.of(context).uri.toString()),
204+
);
205+
},
206+
),
207+
),
208+
],
209+
onPopPage: (Route<Object?> route, Object? result) {
210+
throw UnimplementedError();
211+
},
212+
),
213+
);
214+
},
215+
)
216+
];
217+
await createRouter(routes, tester);
218+
expect(find.text('/'), findsOneWidget);
219+
});
220+
188221
testWidgets('GoRouterState topRoute accessible from StatefulShellRoute',
189222
(WidgetTester tester) async {
190223
final GlobalKey<NavigatorState> rootNavigatorKey =

0 commit comments

Comments
 (0)