[go_router] Make `replace` use `pop` and `push` to generate a new `pageKey` (#2747)
* :bug: Use pop and push in replace to generate a new pageKey
* :white_check_mark: Test that replace creates a new page key
* :arrow_up: Increase the version number
* :recycle: Move the asserts to the router deleguate
* Wrap _debugAssertMatchListNotEmpty in an assert
* Update packages/go_router/lib/src/delegate.dart
Co-authored-by: John Ryan <ryjohn@google.com>
diff --git a/packages/go_router/CHANGELOG.md b/packages/go_router/CHANGELOG.md
index 7653429..5c448b0 100644
--- a/packages/go_router/CHANGELOG.md
+++ b/packages/go_router/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 5.1.8
+
+- Fixes a bug with `replace` where it was not generated a new `pageKey`.
+
## 5.1.7
- Adds documentation using dartdoc topics
diff --git a/packages/go_router/lib/src/delegate.dart b/packages/go_router/lib/src/delegate.dart
index ae15383..1077358 100644
--- a/packages/go_router/lib/src/delegate.dart
+++ b/packages/go_router/lib/src/delegate.dart
@@ -45,6 +45,18 @@
final bool routerNeglect;
RouteMatchList _matchList = RouteMatchList.empty();
+
+ /// Stores the number of times each route route has been pushed.
+ ///
+ /// This is used to generate a unique key for each route.
+ ///
+ /// For example, it would could be equal to:
+ /// ```dart
+ /// {
+ /// 'family': 1,
+ /// 'family/:fid': 2,
+ /// }
+ /// ```
final Map<String, int> _pushCounts = <String, int>{};
final RouteConfiguration _configuration;
@@ -136,9 +148,21 @@
return navigatorKey.currentState?.canPop() ?? false;
}
+ void _debugAssertMatchListNotEmpty() {
+ assert(
+ _matchList.isNotEmpty,
+ 'You have popped the last page off of the stack,'
+ ' there are no pages left to show',
+ );
+ }
+
/// Pop the top page off the GoRouter's page stack.
void pop() {
_matchList.pop();
+ assert(() {
+ _debugAssertMatchListNotEmpty();
+ return true;
+ }());
notifyListeners();
}
@@ -147,8 +171,8 @@
/// See also:
/// * [push] which pushes the given location onto the page stack.
void replace(RouteMatch match) {
- _matchList.matches.last = match;
- notifyListeners();
+ _matchList.pop();
+ push(match); // [push] will notify the listeners.
}
/// For internal use; visible for testing only.
diff --git a/packages/go_router/lib/src/matching.dart b/packages/go_router/lib/src/matching.dart
index 1775c71..36add3a 100644
--- a/packages/go_router/lib/src/matching.dart
+++ b/packages/go_router/lib/src/matching.dart
@@ -74,14 +74,10 @@
void pop() {
_matches.removeLast();
- _debugAssertNotEmpty();
-
// Also pop ShellRoutes when there are no subsequent route matches
while (_matches.isNotEmpty && _matches.last.route is ShellRoute) {
_matches.removeLast();
}
-
- _debugAssertNotEmpty();
}
/// An optional object provided by the app during navigation.
@@ -98,13 +94,6 @@
/// Returns the error that this match intends to display.
Exception? get error => matches.first.error;
-
- void _debugAssertNotEmpty() {
- assert(
- _matches.isNotEmpty,
- 'You have popped the last page off of the stack,'
- ' there are no pages left to show');
- }
}
/// An error that occurred during matching.
diff --git a/packages/go_router/pubspec.yaml b/packages/go_router/pubspec.yaml
index ff28571..2adf1ac 100644
--- a/packages/go_router/pubspec.yaml
+++ b/packages/go_router/pubspec.yaml
@@ -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: 5.1.7
+version: 5.1.8
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
diff --git a/packages/go_router/test/delegate_test.dart b/packages/go_router/test/delegate_test.dart
index f60ce65..61110b4 100644
--- a/packages/go_router/test/delegate_test.dart
+++ b/packages/go_router/test/delegate_test.dart
@@ -154,6 +154,35 @@
);
},
);
+ testWidgets(
+ 'It should return different pageKey when replace is called',
+ (WidgetTester tester) async {
+ final GoRouter goRouter = await createGoRouter(tester);
+ expect(goRouter.routerDelegate.matches.matches.length, 1);
+ expect(
+ goRouter.routerDelegate.matches.matches[0].pageKey,
+ null,
+ );
+
+ goRouter.push('/a');
+ await tester.pumpAndSettle();
+
+ expect(goRouter.routerDelegate.matches.matches.length, 2);
+ expect(
+ goRouter.routerDelegate.matches.matches.last.pageKey,
+ const Key('/a-p1'),
+ );
+
+ goRouter.replace('/a');
+ await tester.pumpAndSettle();
+
+ expect(goRouter.routerDelegate.matches.matches.length, 2);
+ expect(
+ goRouter.routerDelegate.matches.matches.last.pageKey,
+ const Key('/a-p2'),
+ );
+ },
+ );
});
group('replaceNamed', () {