Skip to content

[go_router] Add maybePop for safe navigation pops#11927

Open
muhammadkamel wants to merge 5 commits into
flutter:mainfrom
muhammadkamel:go-router-maybe-pop
Open

[go_router] Add maybePop for safe navigation pops#11927
muhammadkamel wants to merge 5 commits into
flutter:mainfrom
muhammadkamel:go-router-maybe-pop

Conversation

@muhammadkamel

@muhammadkamel muhammadkamel commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Adds maybePop to GoRouter, GoRouterDelegate, and GoRouterHelper (context.maybePop()), mirroring Navigator.maybePop and Material BackButton behavior.
  • Unlike pop(), maybePop() returns false instead of throwing when nothing can be popped.
  • When a pop completes synchronously, maybePop() calls restore() to keep routeInformationProvider in sync with GoRouterDelegate.currentConfiguration (same as pop()).
  • Adds test coverage for maybePop, restore(), canPop, mixed Navigator.pop / context.pop flows, and framework BackButton integration.
  • Ignores /coverage/ in go_router .gitignore.

Fixes flutter/flutter#115997

Motivation

Apps commonly gate back navigation with if (context.canPop()) context.pop(). Framework widgets like BackButton use Navigator.maybePop(), which is safe but does not sync GoRouter URL/state. maybePop() provides a single GoRouter-native safe pop API:

await context.maybePop();

Test plan

  • flutter test packages/go_router/test/maybe_pop_test.dart
  • flutter test packages/go_router/test/restore_test.dart
  • flutter test packages/go_router/test/extension_test.dart
  • flutter test packages/go_router/test/delegate_test.dart
  • flutter test packages/go_router/test/router_test.dart

Pre-Review Checklist

Footnotes

  1. go_router uses batched release; the changelog entry is in pending_changelogs/add_maybe_pop.yaml instead of editing CHANGELOG.md or pubspec.yaml directly. 2

@github-actions github-actions Bot added p: go_router triage-framework Should be looked at in framework triage labels Jun 17, 2026
@google-cla

google-cla Bot commented Jun 17, 2026

Copy link
Copy Markdown

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

Introduce maybePop on GoRouter, GoRouterDelegate, and GoRouterHelper
to mirror Navigator.maybePop and BackButton while calling restore()
when a pop completes synchronously. Adds comprehensive tests for
maybePop, restore, canPop, and mixed Navigator.pop usage.

@gemini-code-assist gemini-code-assist Bot left a comment

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.

Code Review

This pull request introduces a maybePop method to GoRouter, GoRouterDelegate, and GoRouterHelper to support popping routes without throwing exceptions when the stack is empty, mirroring Navigator.maybePop. It also adds extensive tests covering the new method, state restoration, and integration scenarios. The review feedback suggests adding a state.mounted check within the asynchronous loop in GoRouterDelegate.maybePop to prevent potential exceptions if a navigator is unmounted during execution.

Comment thread packages/go_router/lib/src/delegate.dart
Skip NavigatorState instances that are disposed during the async await
gap when iterating the navigator stack.
@muhammadkamel
muhammadkamel force-pushed the go-router-maybe-pop branch 3 times, most recently from 8d95ecb to 4b39b05 Compare June 17, 2026 17:00
@muhammadkamel

muhammadkamel commented Jun 17, 2026

Copy link
Copy Markdown
Contributor Author

Hi @chunhtai @hannah-hyj

Would appreciate a review when you have time. Thanks!

@stuartmorgan-g

Copy link
Copy Markdown
Collaborator

@muhammadkamel While waiting for full review, please take a look at the current docs for version/CHANGELOG updates; this package now uses batched release, which follows a different process for updating those PRs.

@stuartmorgan-g
stuartmorgan-g requested a review from chunhtai June 23, 2026 18:47
@chunhtai

Copy link
Copy Markdown
Contributor

Also please do not remove the pre review checklist

@muhammadkamel
muhammadkamel force-pushed the go-router-maybe-pop branch from 9122c9a to ee2a7dd Compare July 3, 2026 08:02
go_router uses batch release, so revert direct CHANGELOG.md and
pubspec.yaml version updates and add a pending_changelogs entry instead.
@muhammadkamel
muhammadkamel force-pushed the go-router-maybe-pop branch from ee2a7dd to b0806eb Compare July 3, 2026 08:03
@muhammadkamel

Copy link
Copy Markdown
Contributor Author

@chunhtai

Could you please take another look?

if (!state.mounted) {
continue;
}
final bool didPop = await state.maybePop<T>(result);

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.

This doesn't feel right

This will only return false if all navigators in the widget tree return false. maybePop shouldn't be this aggressive.

I actually not entirely sure what is the correct behavior here. maybePop is scoped to consult only one navigator. but in GoRouter, there is a group of navigators

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Thanks for the feedback — agreed that walking every navigator was too aggressive.

maybePop now consults only the first navigator from _findCurrentNavigators(), matching Navigator.maybePop's single-navigator scope. If that navigator has nothing to pop, we return false and do not fall through to parents (unlike popRoute, which still walks the stack for system back).

One intentional consequence under shell routes: canPop() can be true while maybePop() returns false, because canPop/pop consider any navigator that can pop, while maybePop only asks the current one. Docs and tests now call that out explicitly — please let me know if you'd rather have maybePop follow pop()'s multi-navigator selection instead.

@muhammadkamel
muhammadkamel requested a review from chunhtai July 22, 2026 22:49
Address review feedback by stopping fall-through across nested
navigators, documenting the canPop/pop asymmetry, and covering both
shell cases in tests.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

p: go_router triage-framework Should be looked at in framework triage

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add GoRouter maybePop

3 participants