From 98991576917a4d1c9cdf617e95952d134379d941 Mon Sep 17 00:00:00 2001 From: Broto Date: Mon, 6 Jul 2026 16:54:29 +1200 Subject: [PATCH] [go_router] Fix popRoute crash when a shell route's navigator is not mounted GoRouterDelegate._findCurrentNavigators force-unwraps each shell navigator's state while walking the current configuration. When the system back button is pressed while the configuration references a shell route whose navigator is not mounted (e.g. mid-redirect away from the shell, or while resuming from background), popRoute throws 'Null check operator used on a null value'. Stop walking when a shell navigator is not mounted, matching the null-tolerant handling canPop already uses for the same state. Fixes flutter/flutter#188993 --- packages/go_router/lib/src/delegate.dart | 8 ++++++- ...ix_poproute_unmounted_shell_navigator.yaml | 5 ++++ packages/go_router/test/delegate_test.dart | 24 +++++++++++++++++++ 3 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 packages/go_router/pending_changelogs/fix_poproute_unmounted_shell_navigator.yaml diff --git a/packages/go_router/lib/src/delegate.dart b/packages/go_router/lib/src/delegate.dart index 8b43fa716530..858c8c231412 100644 --- a/packages/go_router/lib/src/delegate.dart +++ b/packages/go_router/lib/src/delegate.dart @@ -118,7 +118,13 @@ class GoRouterDelegate extends RouterDelegate with ChangeNotifie RouteMatchBase walker = currentConfiguration.matches.last; while (walker is ShellRouteMatch) { - final NavigatorState potentialCandidate = walker.navigatorKey.currentState!; + final NavigatorState? potentialCandidate = walker.navigatorKey.currentState; + if (potentialCandidate == null) { + // Stop if the shell route's navigator is not mounted, e.g. when the + // configuration references a shell route whose widget tree has not + // been built yet or has already been removed. + break; + } final ModalRoute? modalRoute = ModalRoute.of(potentialCandidate.context); if (modalRoute == null || !modalRoute.isCurrent) { diff --git a/packages/go_router/pending_changelogs/fix_poproute_unmounted_shell_navigator.yaml b/packages/go_router/pending_changelogs/fix_poproute_unmounted_shell_navigator.yaml new file mode 100644 index 000000000000..b5f5156a57be --- /dev/null +++ b/packages/go_router/pending_changelogs/fix_poproute_unmounted_shell_navigator.yaml @@ -0,0 +1,5 @@ +changelog: | + - Fixes `popRoute()` crashing with "Null check operator used on a null value" + when the current configuration references a shell route whose navigator is + not mounted. +version: patch diff --git a/packages/go_router/test/delegate_test.dart b/packages/go_router/test/delegate_test.dart index 12d2d914ad6a..b543c1ca7fa2 100644 --- a/packages/go_router/test/delegate_test.dart +++ b/packages/go_router/test/delegate_test.dart @@ -343,6 +343,30 @@ void main() { ], tester); expect(await goRouter.routerDelegate.popRoute(), isFalse); }); + + testWidgets('popRoute does not throw when a shell navigator in the configuration ' + 'is not mounted', (WidgetTester tester) async { + final GoRouter goRouter = await createRouter([ + GoRoute(path: '/', builder: (_, _) => const Text('Home')), + ShellRoute( + builder: (_, _, Widget child) => child, + routes: [ + GoRoute(path: '/dashboard', builder: (_, _) => const Text('Dashboard')), + ], + ), + ], tester); + + // Simulate the transient state where the configuration references a + // shell route whose navigator is not (or no longer) mounted, e.g. while + // the widget tree rebuilds during a redirect or after resuming from + // background. + goRouter.routerDelegate.currentConfiguration = goRouter.configuration.findMatch( + Uri.parse('/dashboard'), + ); + + // Should not throw "Null check operator used on a null value". + expect(await goRouter.routerDelegate.popRoute(), isFalse); + }); }); group('push', () {