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
7 changes: 7 additions & 0 deletions packages/go_router/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
## 17.3.1

- Fixes a "Bad state: Future already completed" crash when an imperatively-pushed
route's completion is triggered more than once (for example a rapid double-pop).
`ImperativeRouteMatch.complete` is now idempotent. See
https://github.com/flutter/flutter/issues/156809.

## 17.3.0

- Updates minimum supported SDK version to Flutter 3.38/Dart 3.10.
Expand Down
9 changes: 9 additions & 0 deletions packages/go_router/lib/src/match.dart
Original file line number Diff line number Diff line change
Expand Up @@ -444,6 +444,15 @@ class ImperativeRouteMatch extends RouteMatch {
/// Called when the corresponding [Route] associated with this route match is
/// completed.
void complete([dynamic value]) {
// Completing is idempotent. The pushed route can be removed more than once
// — e.g. a rapid double-pop, or a pop dispatched while a prior removal is

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 feels like bug that it calls complete on the same match in the first place. we should make sure it calls pop on the page under a already out going page

// still in flight on a slow device — which would otherwise call complete()
// a second time and throw "Bad state: Future already completed". The
// awaited result is delivered by the first completion, so the duplicate is
// a no-op. See https://github.com/flutter/flutter/issues/156809.
if (completer.isCompleted) {
return;
}
completer.complete(value);
}

Expand Down
2 changes: 1 addition & 1 deletion packages/go_router/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name: go_router
description: A declarative router for Flutter based on Navigation 2 supporting
deep linking, data-driven routes and more
version: 17.3.0
version: 17.3.1
repository: https://github.com/flutter/packages/tree/main/packages/go_router
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+go_router%22

Expand Down
14 changes: 14 additions & 0 deletions packages/go_router/test/match_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,20 @@ void main() {
final completer1 = Completer<void>();
final completer2 = Completer<void>();

test('complete is idempotent and does not throw when called twice', () async {
// Regression test for https://github.com/flutter/flutter/issues/156809:
// the pushed route's completer could be completed more than once
// (e.g. a rapid double-pop), throwing "Bad state: Future already
// completed".
final completer = Completer<Object?>();
final match = ImperativeRouteMatch(pageKey: key1, matches: matchList1, completer: completer);

match.complete('result');
// A second completion must be a no-op rather than a crash.
expect(() => match.complete('ignored'), returnsNormally);
expect(await completer.future, 'result');
});

test('can equal and has', () async {
var match1 = ImperativeRouteMatch(pageKey: key1, matches: matchList1, completer: completer1);
var match2 = ImperativeRouteMatch(pageKey: key1, matches: matchList1, completer: completer1);
Expand Down