Skip to content

Commit 97ab964

Browse files
authored
[go_router] fix onExit ignored for GoRoute nested inside ShellRoute (#11853)
`onExit` was silently ignored when the route was nested inside a ShellRoute + added test for async and sync pop for this specific case Attempt to fix flutter/flutter#137829 flutter/flutter#137829 *List which issues are fixed by this PR. You must list at least one issue.* ## Pre-Review Checklist **Note**: The Flutter team is currently trialing the use of [Gemini Code Assist for GitHub](https://developers.google.com/gemini-code-assist/docs/review-github-code). Comments from the `gemini-code-assist` bot should not be taken as authoritative feedback from the Flutter team. If you find its comments useful you can update your code accordingly, but if you are unsure or disagree with the feedback, please feel free to wait for a Flutter team member's review for guidance on which automated comments should be addressed. [^1]: Regular contributors who have demonstrated familiarity with the repository guidelines only need to comment if the PR is not auto-exempted by repo tooling.
1 parent c37e11b commit 97ab964

3 files changed

Lines changed: 89 additions & 2 deletions

File tree

packages/go_router/lib/src/delegate.dart

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,13 @@ class GoRouterDelegate extends RouterDelegate<RouteMatchList> with ChangeNotifie
137137
assert(!popped);
138138
return popped;
139139
}
140-
final RouteBase routeBase = match.route;
140+
141+
var leafMatch = match;
142+
while (leafMatch is ShellRouteMatch) {
143+
leafMatch = leafMatch.matches.last;
144+
}
145+
146+
final RouteBase routeBase = leafMatch.route;
141147
if (routeBase is! GoRoute || routeBase.onExit == null) {
142148
route.didPop(result);
143149
_completeRouteMatch(result, match);
@@ -150,7 +156,7 @@ class GoRouterDelegate extends RouterDelegate<RouteMatchList> with ChangeNotifie
150156
scheduleMicrotask(() async {
151157
final bool onExitResult = await routeBase.onExit!(
152158
navigatorKey.currentContext!,
153-
match.buildState(_configuration, currentConfiguration),
159+
leafMatch.buildState(_configuration, currentConfiguration),
154160
);
155161
if (onExitResult) {
156162
_completeRouteMatch(result, match);
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
changelog: |
2+
- Fixes onExit ignored for GoRoute nested inside ShellRoute
3+
version: patch

packages/go_router/test/on_exit_test.dart

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -528,4 +528,82 @@ void main() {
528528
expect(find.byKey(detailKey), findsOneWidget);
529529
expect(find.byKey(homeKey), findsNothing);
530530
});
531+
532+
// Regression test for https://github.com/flutter/flutter/issues/137829
533+
testWidgets('back button works synchronously with ShellRoute', (WidgetTester tester) async {
534+
var allow = false;
535+
final home = UniqueKey();
536+
final page = UniqueKey();
537+
final routes = <RouteBase>[
538+
GoRoute(
539+
path: '/',
540+
builder: (_, _) => DummyScreen(key: home),
541+
routes: <RouteBase>[
542+
ShellRoute(
543+
builder: (_, _, Widget child) => child,
544+
routes: <RouteBase>[
545+
GoRoute(
546+
path: 'page',
547+
builder: (_, _) => DummyScreen(key: page),
548+
onExit: (BuildContext context, GoRouterState state) => allow,
549+
),
550+
],
551+
),
552+
],
553+
),
554+
];
555+
556+
final GoRouter router = await createRouter(routes, tester, initialLocation: '/page');
557+
expect(find.byKey(page), findsOneWidget);
558+
559+
router.pop();
560+
await tester.pumpAndSettle();
561+
expect(find.byKey(page), findsOneWidget);
562+
563+
allow = true;
564+
router.pop();
565+
await tester.pumpAndSettle();
566+
expect(find.byKey(home), findsOneWidget);
567+
});
568+
569+
// Regression test for https://github.com/flutter/flutter/issues/137829
570+
testWidgets('back button works asynchronously with ShellRoute', (WidgetTester tester) async {
571+
var allow = Completer<bool>();
572+
final home = UniqueKey();
573+
final page = UniqueKey();
574+
final routes = <RouteBase>[
575+
GoRoute(
576+
path: '/',
577+
builder: (_, _) => DummyScreen(key: home),
578+
routes: <RouteBase>[
579+
ShellRoute(
580+
builder: (_, _, Widget child) => child,
581+
routes: <RouteBase>[
582+
GoRoute(
583+
path: 'page',
584+
builder: (_, _) => DummyScreen(key: page),
585+
onExit: (BuildContext context, GoRouterState state) async => allow.future,
586+
),
587+
],
588+
),
589+
],
590+
),
591+
];
592+
593+
final GoRouter router = await createRouter(routes, tester, initialLocation: '/page');
594+
expect(find.byKey(page), findsOneWidget);
595+
596+
router.pop();
597+
await tester.pumpAndSettle();
598+
allow.complete(false);
599+
await tester.pumpAndSettle();
600+
expect(find.byKey(page), findsOneWidget);
601+
602+
allow = Completer<bool>();
603+
router.pop();
604+
await tester.pumpAndSettle();
605+
allow.complete(true);
606+
await tester.pumpAndSettle();
607+
expect(find.byKey(home), findsOneWidget);
608+
});
531609
}

0 commit comments

Comments
 (0)