Skip to content

[go_router] Make ImperativeRouteMatch.complete idempotent#12112

Open
Khairul989 wants to merge 1 commit into
flutter:mainfrom
Khairul989:go_router-idempotent-complete
Open

[go_router] Make ImperativeRouteMatch.complete idempotent#12112
Khairul989 wants to merge 1 commit into
flutter:mainfrom
Khairul989:go_router-idempotent-complete

Conversation

@Khairul989

@Khairul989 Khairul989 commented Jul 6, 2026

Copy link
Copy Markdown

Description

Popping an imperatively-pushed route (context.push) can complete its result Completer more than once, throwing an uncaught Bad state: Future already completed. The second completion originates inside go_router itself (GoRouterDelegate._handlePopPageWithRouteMatch_completeRouteMatchImperativeRouteMatch.complete), triggered when the same route's removal is dispatched twice before the first settles — commonly a rapid double-pop, and more likely on lower-end devices where the frame window is wide.

This is a long-standing, still-open crash reported across many issues, reproducible through the current version:

  • #156809 (go_router_builder, two consecutive pops)
  • #123369, #146938, #157219, #163813, #160696, #180002

ImperativeRouteMatch.complete is non-idempotent in every version to date.

Fix

Make ImperativeRouteMatch.complete idempotent: return early when the completer is already completed. The awaited push result is delivered by the first completion, so the duplicate is a safe no-op rather than a crash.

void complete([dynamic value]) {
  if (completer.isCompleted) {
    return;
  }
  completer.complete(value);
}

(Alternative considered: guarding in GoRouterDelegate._completeRouteMatch. Happy to move it there if maintainers prefer.)

Tests

Added a regression test in match_test.dart asserting a second complete() call does not throw and the first value wins.

Related Issues

Fixes flutter/flutter#156809

Pre-launch Checklist

  • I read the [Contributor Guide] and followed the process outlined there for submitting PRs.
  • I read the [Tree Hygiene] wiki page, which explains my responsibilities.
  • I read and followed the [relevant style guides] and ran the auto-formatter. (dart format)
  • I signed the [CLA].
  • The title of the PR starts with the name of the package surrounded by square brackets, e.g. [go_router]
  • I [linked to at least one issue that this PR fixes] in the description above.
  • I updated pubspec.yaml with an appropriate new version according to the [pub versioning philosophy], or this PR is [exempt from version changes].
  • I updated CHANGELOG.md to add a description of the change, [following repository CHANGELOG style], or this PR is [exempt from CHANGELOG changes].
  • I updated/added relevant documentation (doc comments with ///).
  • I added new tests to check the change I am making, or this PR is [test-exempt].
  • All existing and new tests are passing.

Popping an imperatively-pushed route can complete its result Completer
more than once (e.g. a rapid double-pop, or a pop dispatched while a
prior removal is still in flight), throwing an uncaught
'Bad state: Future already completed'. This is a long-standing, still-open
crash: flutter/flutter#156809 (and #123369,
#146938, #157219, #163813, #180002).

Make complete() idempotent: skip when the completer is already completed.
The awaited push result is delivered by the first completion, so the
duplicate is a safe no-op rather than a crash.

Adds a regression test.
@google-cla

google-cla Bot commented Jul 6, 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.

@github-actions github-actions Bot added p: go_router triage-framework Should be looked at in framework triage labels Jul 6, 2026

@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 updates the go_router package to version 17.3.1, introducing a fix for a crash caused by completing an imperatively-pushed route's future more than once. Specifically, ImperativeRouteMatch.complete is made idempotent by checking if the underlying completer is already completed before attempting to complete it. A corresponding regression test has been added to verify this behavior. There are no review comments, and I have no feedback to provide.

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.

[go_router_builder] Two consecutive pop operations trigger "Future already completed" error

1 participant