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', () {