diff --git a/packages/go_router/lib/src/delegate.dart b/packages/go_router/lib/src/delegate.dart index 1459d2f395ce..8b43fa716530 100644 --- a/packages/go_router/lib/src/delegate.dart +++ b/packages/go_router/lib/src/delegate.dart @@ -137,7 +137,13 @@ class GoRouterDelegate extends RouterDelegate with ChangeNotifie assert(!popped); return popped; } - final RouteBase routeBase = match.route; + + var leafMatch = match; + while (leafMatch is ShellRouteMatch) { + leafMatch = leafMatch.matches.last; + } + + final RouteBase routeBase = leafMatch.route; if (routeBase is! GoRoute || routeBase.onExit == null) { route.didPop(result); _completeRouteMatch(result, match); @@ -150,7 +156,7 @@ class GoRouterDelegate extends RouterDelegate with ChangeNotifie scheduleMicrotask(() async { final bool onExitResult = await routeBase.onExit!( navigatorKey.currentContext!, - match.buildState(_configuration, currentConfiguration), + leafMatch.buildState(_configuration, currentConfiguration), ); if (onExitResult) { _completeRouteMatch(result, match); diff --git a/packages/go_router/pending_changelogs/change_2026_06_07_1780787180781.yaml b/packages/go_router/pending_changelogs/change_2026_06_07_1780787180781.yaml new file mode 100644 index 000000000000..4f539b000741 --- /dev/null +++ b/packages/go_router/pending_changelogs/change_2026_06_07_1780787180781.yaml @@ -0,0 +1,3 @@ +changelog: | + - Fixes onExit ignored for GoRoute nested inside ShellRoute +version: patch diff --git a/packages/go_router/test/on_exit_test.dart b/packages/go_router/test/on_exit_test.dart index ef27973d29d4..990ee9a9a6a3 100644 --- a/packages/go_router/test/on_exit_test.dart +++ b/packages/go_router/test/on_exit_test.dart @@ -528,4 +528,82 @@ void main() { expect(find.byKey(detailKey), findsOneWidget); expect(find.byKey(homeKey), findsNothing); }); + + // Regression test for https://github.com/flutter/flutter/issues/137829 + testWidgets('back button works synchronously with ShellRoute', (WidgetTester tester) async { + var allow = false; + final home = UniqueKey(); + final page = UniqueKey(); + final routes = [ + GoRoute( + path: '/', + builder: (_, _) => DummyScreen(key: home), + routes: [ + ShellRoute( + builder: (_, _, Widget child) => child, + routes: [ + GoRoute( + path: 'page', + builder: (_, _) => DummyScreen(key: page), + onExit: (BuildContext context, GoRouterState state) => allow, + ), + ], + ), + ], + ), + ]; + + final GoRouter router = await createRouter(routes, tester, initialLocation: '/page'); + expect(find.byKey(page), findsOneWidget); + + router.pop(); + await tester.pumpAndSettle(); + expect(find.byKey(page), findsOneWidget); + + allow = true; + router.pop(); + await tester.pumpAndSettle(); + expect(find.byKey(home), findsOneWidget); + }); + + // Regression test for https://github.com/flutter/flutter/issues/137829 + testWidgets('back button works asynchronously with ShellRoute', (WidgetTester tester) async { + var allow = Completer(); + final home = UniqueKey(); + final page = UniqueKey(); + final routes = [ + GoRoute( + path: '/', + builder: (_, _) => DummyScreen(key: home), + routes: [ + ShellRoute( + builder: (_, _, Widget child) => child, + routes: [ + GoRoute( + path: 'page', + builder: (_, _) => DummyScreen(key: page), + onExit: (BuildContext context, GoRouterState state) async => allow.future, + ), + ], + ), + ], + ), + ]; + + final GoRouter router = await createRouter(routes, tester, initialLocation: '/page'); + expect(find.byKey(page), findsOneWidget); + + router.pop(); + await tester.pumpAndSettle(); + allow.complete(false); + await tester.pumpAndSettle(); + expect(find.byKey(page), findsOneWidget); + + allow = Completer(); + router.pop(); + await tester.pumpAndSettle(); + allow.complete(true); + await tester.pumpAndSettle(); + expect(find.byKey(home), findsOneWidget); + }); }