Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion packages/go_router/lib/src/delegate.dart
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,13 @@ class GoRouterDelegate extends RouterDelegate<RouteMatchList> 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) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looking at the issue, do you have a reproducible code that show case the transient state?

also I am not too sure what may be the correct behavior when the backbutton is pressed during that state. It felt wrong to pop the parent navigator because child navigator is in transient state. Perhaps the correct solution will be disallow the current configuration to be in such state

// 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<dynamic>? modalRoute = ModalRoute.of(potentialCandidate.context);
if (modalRoute == null || !modalRoute.isCurrent) {
Expand Down
Original file line number Diff line number Diff line change
@@ -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
24 changes: 24 additions & 0 deletions packages/go_router/test/delegate_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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(<RouteBase>[
GoRoute(path: '/', builder: (_, _) => const Text('Home')),
ShellRoute(
builder: (_, _, Widget child) => child,
routes: <RouteBase>[
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', () {
Expand Down