[go_router] Make ImperativeRouteMatch.complete idempotent#12112
[go_router] Make ImperativeRouteMatch.complete idempotent#12112Khairul989 wants to merge 1 commit into
Conversation
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.
|
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. |
There was a problem hiding this comment.
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.
Description
Popping an imperatively-pushed route (
context.push) can complete its resultCompletermore than once, throwing an uncaughtBad state: Future already completed. The second completion originates inside go_router itself (GoRouterDelegate._handlePopPageWithRouteMatch→_completeRouteMatch→ImperativeRouteMatch.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:
ImperativeRouteMatch.completeis non-idempotent in every version to date.Fix
Make
ImperativeRouteMatch.completeidempotent: 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.(Alternative considered: guarding in
GoRouterDelegate._completeRouteMatch. Happy to move it there if maintainers prefer.)Tests
Added a regression test in
match_test.dartasserting a secondcomplete()call does not throw and the first value wins.Related Issues
Fixes flutter/flutter#156809
Pre-launch Checklist
dart format)[go_router]pubspec.yamlwith an appropriate new version according to the [pub versioning philosophy], or this PR is [exempt from version changes].CHANGELOG.mdto add a description of the change, [following repository CHANGELOG style], or this PR is [exempt from CHANGELOG changes].///).