From f74ad0d9c48b07deec34b4b415f4da016e32ee71 Mon Sep 17 00:00:00 2001 From: Nazareno Cavazzon Date: Fri, 3 Mar 2023 15:31:05 -0300 Subject: [PATCH 01/16] Return value when pop --- packages/go_router/CHANGELOG.md | 4 ++ packages/go_router/doc/navigation.md | 19 ++++++++ packages/go_router/example/pubspec.yaml | 1 + packages/go_router/lib/src/delegate.dart | 7 ++- packages/go_router/lib/src/match.dart | 9 ++++ .../go_router/lib/src/misc/extensions.dart | 6 +-- packages/go_router/lib/src/router.dart | 16 +++---- packages/go_router/pubspec.yaml | 2 +- packages/go_router/test/go_router_test.dart | 44 +++++++++++++++++++ packages/go_router/test/inherited_test.dart | 3 +- packages/go_router/test/test_helpers.dart | 6 ++- 11 files changed, 100 insertions(+), 17 deletions(-) diff --git a/packages/go_router/CHANGELOG.md b/packages/go_router/CHANGELOG.md index 31958fe101c..a17953e9bf7 100644 --- a/packages/go_router/CHANGELOG.md +++ b/packages/go_router/CHANGELOG.md @@ -3,6 +3,10 @@ - Updates compileSdkVersion to 33. - Updates example app to iOS 11. +## 6.3.0 + +- Support for returning values on pop. + ## 6.2.0 - Export supertypes in route_data.dart library diff --git a/packages/go_router/doc/navigation.md b/packages/go_router/doc/navigation.md index 93d51ed97dd..d72c2cfafe9 100644 --- a/packages/go_router/doc/navigation.md +++ b/packages/go_router/doc/navigation.md @@ -68,4 +68,23 @@ Navigator.of(context).push( ); ``` +## Returning values +Waiting for a value to be returned: + +```dart +onTap: () { + final bool? result = await context.push('/page2'); + WidgetsBinding.instance.addPostFrameCallback((_) { + if(result ?? false)... + }); +} +``` + +Returning a value: + +```dart +onTap: () => context.pop(true) +``` + + [Named routes]: https://pub.dev/documentation/go_router/latest/topics/Named%20routes-topic.html diff --git a/packages/go_router/example/pubspec.yaml b/packages/go_router/example/pubspec.yaml index c5b85197f83..88d1ad2bdac 100644 --- a/packages/go_router/example/pubspec.yaml +++ b/packages/go_router/example/pubspec.yaml @@ -17,6 +17,7 @@ dependencies: go_router: path: .. logging: ^1.0.0 + package_info_plus_web: ^2.0.0 provider: ^6.0.0 shared_preferences: ^2.0.11 url_launcher: ^6.0.7 diff --git a/packages/go_router/lib/src/delegate.dart b/packages/go_router/lib/src/delegate.dart index 07bcdf0dbb4..5b583075219 100644 --- a/packages/go_router/lib/src/delegate.dart +++ b/packages/go_router/lib/src/delegate.dart @@ -76,8 +76,9 @@ class GoRouterDelegate extends RouterDelegate } /// Pushes the given location onto the page stack - void push(RouteMatchList matches) { + Future push(RouteMatchList matches) async { assert(matches.last.route is! ShellRoute); + final Completer completer = Completer(); // Remap the pageKey to allow any number of the same page on the stack final int count = (_pushCounts[matches.fullpath] ?? 0) + 1; @@ -91,10 +92,12 @@ class GoRouterDelegate extends RouterDelegate error: matches.last.error, pageKey: pageKey, matches: matches, + completer: completer, ); _matchList.push(newPageKeyMatch); notifyListeners(); + return completer.future; } /// Returns `true` if the active Navigator can pop. @@ -113,6 +116,7 @@ class GoRouterDelegate extends RouterDelegate final _NavigatorStateIterator iterator = _createNavigatorStateIterator(); while (iterator.moveNext()) { if (iterator.current.canPop()) { + iterator.matchList.last.completer?.complete(result); iterator.current.pop(result); return; } @@ -278,6 +282,7 @@ class ImperativeRouteMatch extends RouteMatch { required super.error, required super.pageKey, required this.matches, + super.completer, }); /// The matches that produces this route match. diff --git a/packages/go_router/lib/src/match.dart b/packages/go_router/lib/src/match.dart index 62a4d550054..1126b9ccb32 100644 --- a/packages/go_router/lib/src/match.dart +++ b/packages/go_router/lib/src/match.dart @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'dart:async'; + import 'package:flutter/foundation.dart'; import 'package:flutter/widgets.dart'; @@ -18,6 +20,7 @@ class RouteMatch { required this.extra, required this.error, required this.pageKey, + this.completer, }); // ignore: public_member_api_docs @@ -27,6 +30,7 @@ class RouteMatch { required String parentSubloc, // e.g. /family/f2 required Map pathParameters, required Object? extra, + Completer? completer, }) { if (route is ShellRoute) { return RouteMatch( @@ -35,6 +39,7 @@ class RouteMatch { extra: extra, error: null, pageKey: ValueKey(route.hashCode.toString()), + completer: completer, ); } else if (route is GoRoute) { assert(!route.path.contains('//')); @@ -56,6 +61,7 @@ class RouteMatch { extra: extra, error: null, pageKey: ValueKey(route.hashCode.toString()), + completer: completer, ); } throw MatcherError('Unexpected route type: $route', restLoc); @@ -75,4 +81,7 @@ class RouteMatch { /// Value key of type string, to hold a unique reference to a page. final ValueKey pageKey; + + /// The completer for the promise returned by [GoRouter.push]. + final Completer? completer; } diff --git a/packages/go_router/lib/src/misc/extensions.dart b/packages/go_router/lib/src/misc/extensions.dart index fe37e342fb1..08410ba7fff 100644 --- a/packages/go_router/lib/src/misc/extensions.dart +++ b/packages/go_router/lib/src/misc/extensions.dart @@ -37,17 +37,17 @@ extension GoRouterHelper on BuildContext { ); /// Push a location onto the page stack. - void push(String location, {Object? extra}) => + Future push(String location, {Object? extra}) => GoRouter.of(this).push(location, extra: extra); /// Navigate to a named route onto the page stack. - void pushNamed( + Future pushNamed( String name, { Map params = const {}, Map queryParams = const {}, Object? extra, }) => - GoRouter.of(this).pushNamed( + GoRouter.of(this).pushNamed( name, params: params, queryParams: queryParams, diff --git a/packages/go_router/lib/src/router.dart b/packages/go_router/lib/src/router.dart index b2a48e2d447..1c6d6b1b77b 100644 --- a/packages/go_router/lib/src/router.dart +++ b/packages/go_router/lib/src/router.dart @@ -204,32 +204,30 @@ class GoRouter extends ChangeNotifier implements RouterConfig { /// Push a URI location onto the page stack w/ optional query parameters, e.g. /// `/family/f2/person/p1?color=blue` - void push(String location, {Object? extra}) { + Future push(String location, {Object? extra}) async { assert(() { log.info('pushing $location'); return true; }()); - _routeInformationParser - .parseRouteInformationWithDependencies( + final RouteMatchList matches = + await _routeInformationParser.parseRouteInformationWithDependencies( RouteInformation(location: location, state: extra), // TODO(chunhtai): avoid accessing the context directly through global key. // https://github.com/flutter/flutter/issues/99112 _routerDelegate.navigatorKey.currentContext!, - ) - .then((RouteMatchList matches) { - _routerDelegate.push(matches); - }); + ); + return _routerDelegate.push(matches); } /// Push a named route onto the page stack w/ optional parameters, e.g. /// `name='person', params={'fid': 'f2', 'pid': 'p1'}` - void pushNamed( + Future pushNamed( String name, { Map params = const {}, Map queryParams = const {}, Object? extra, }) => - push( + push( namedLocation(name, params: params, queryParams: queryParams), extra: extra, ); diff --git a/packages/go_router/pubspec.yaml b/packages/go_router/pubspec.yaml index 8e833aea8f5..09b4a9d51da 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: 6.2.0 +version: 6.3.0 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/go_router_test.dart b/packages/go_router/test/go_router_test.dart index 84a3768484e..e65b0328b8b 100644 --- a/packages/go_router/test/go_router_test.dart +++ b/packages/go_router/test/go_router_test.dart @@ -2655,6 +2655,26 @@ void main() { expect(router.extra, extra); }); + testWidgets('calls [push] on closest GoRouter with a promise', + (WidgetTester tester) async { + final GoRouterPushSpy router = GoRouterPushSpy(routes: routes); + await tester.pumpWidget( + MaterialApp.router( + routeInformationProvider: router.routeInformationProvider, + routeInformationParser: router.routeInformationParser, + routerDelegate: router.routerDelegate, + title: 'GoRouter Example', + ), + ); + final String? result = await router.push( + location, + extra: extra, + ); + expect(result, extra); + expect(router.myLocation, location); + expect(router.extra, extra); + }); + testWidgets('calls [pushNamed] on closest GoRouter', (WidgetTester tester) async { final GoRouterPushNamedSpy router = GoRouterPushNamedSpy(routes: routes); @@ -2676,6 +2696,30 @@ void main() { expect(router.extra, extra); }); + testWidgets('calls [pushNamed] on closest GoRouter with a promise', + (WidgetTester tester) async { + final GoRouterPushNamedSpy router = GoRouterPushNamedSpy(routes: routes); + await tester.pumpWidget( + MaterialApp.router( + routeInformationProvider: router.routeInformationProvider, + routeInformationParser: router.routeInformationParser, + routerDelegate: router.routerDelegate, + title: 'GoRouter Example', + ), + ); + final String? result = await router.pushNamed( + name, + params: params, + queryParams: queryParams, + extra: extra, + ); + expect(result, extra); + expect(router.extra, extra); + expect(router.name, name); + expect(router.params, params); + expect(router.queryParams, queryParams); + }); + testWidgets('calls [pop] on closest GoRouter', (WidgetTester tester) async { final GoRouterPopSpy router = GoRouterPopSpy(routes: routes); await tester.pumpWidget( diff --git a/packages/go_router/test/inherited_test.dart b/packages/go_router/test/inherited_test.dart index 9fbb915a4a2..7afa9fe5752 100644 --- a/packages/go_router/test/inherited_test.dart +++ b/packages/go_router/test/inherited_test.dart @@ -129,11 +129,12 @@ class MockGoRouter extends GoRouter { late String latestPushedName; @override - void pushNamed(String name, + Future pushNamed(String name, {Map params = const {}, Map queryParams = const {}, Object? extra}) { latestPushedName = name; + return Future.value(); } @override diff --git a/packages/go_router/test/test_helpers.dart b/packages/go_router/test/test_helpers.dart index 11d42a09fa0..ca79fa5e9de 100644 --- a/packages/go_router/test/test_helpers.dart +++ b/packages/go_router/test/test_helpers.dart @@ -95,9 +95,10 @@ class GoRouterPushSpy extends GoRouter { Object? extra; @override - void push(String location, {Object? extra}) { + Future push(String location, {Object? extra}) { myLocation = location; this.extra = extra; + return Future.value(extra as T?); } } @@ -110,7 +111,7 @@ class GoRouterPushNamedSpy extends GoRouter { Object? extra; @override - void pushNamed( + Future pushNamed( String name, { Map params = const {}, Map queryParams = const {}, @@ -120,6 +121,7 @@ class GoRouterPushNamedSpy extends GoRouter { this.params = params; this.queryParams = queryParams; this.extra = extra; + return Future.value(extra as T?); } } From ba08b81496a8d58e59b5b060283641570811d22d Mon Sep 17 00:00:00 2001 From: Nazareno Cavazzon Date: Fri, 3 Mar 2023 16:40:08 -0300 Subject: [PATCH 02/16] Fixes --- packages/go_router/CHANGELOG.md | 8 +++---- packages/go_router/example/pubspec.yaml | 1 - packages/go_router/lib/src/delegate.dart | 12 ++++------- packages/go_router/lib/src/match.dart | 27 ++++++++++++++++-------- 4 files changed, 26 insertions(+), 22 deletions(-) diff --git a/packages/go_router/CHANGELOG.md b/packages/go_router/CHANGELOG.md index a17953e9bf7..1b7f5ead874 100644 --- a/packages/go_router/CHANGELOG.md +++ b/packages/go_router/CHANGELOG.md @@ -1,12 +1,12 @@ +## 6.3.0 + +- Supports for returning values on pop. + ## NEXT - Updates compileSdkVersion to 33. - Updates example app to iOS 11. -## 6.3.0 - -- Support for returning values on pop. - ## 6.2.0 - Export supertypes in route_data.dart library diff --git a/packages/go_router/example/pubspec.yaml b/packages/go_router/example/pubspec.yaml index 88d1ad2bdac..c5b85197f83 100644 --- a/packages/go_router/example/pubspec.yaml +++ b/packages/go_router/example/pubspec.yaml @@ -17,7 +17,6 @@ dependencies: go_router: path: .. logging: ^1.0.0 - package_info_plus_web: ^2.0.0 provider: ^6.0.0 shared_preferences: ^2.0.11 url_launcher: ^6.0.7 diff --git a/packages/go_router/lib/src/delegate.dart b/packages/go_router/lib/src/delegate.dart index 5b583075219..86585ba2e5f 100644 --- a/packages/go_router/lib/src/delegate.dart +++ b/packages/go_router/lib/src/delegate.dart @@ -78,26 +78,24 @@ class GoRouterDelegate extends RouterDelegate /// Pushes the given location onto the page stack Future push(RouteMatchList matches) async { assert(matches.last.route is! ShellRoute); - final Completer completer = Completer(); // Remap the pageKey to allow any number of the same page on the stack final int count = (_pushCounts[matches.fullpath] ?? 0) + 1; _pushCounts[matches.fullpath] = count; final ValueKey pageKey = ValueKey('${matches.fullpath}-p$count'); - final ImperativeRouteMatch newPageKeyMatch = ImperativeRouteMatch( + final ImperativeRouteMatch newPageKeyMatch = ImperativeRouteMatch( route: matches.last.route, subloc: matches.last.subloc, extra: matches.last.extra, error: matches.last.error, pageKey: pageKey, matches: matches, - completer: completer, ); _matchList.push(newPageKeyMatch); notifyListeners(); - return completer.future; + return newPageKeyMatch.future; } /// Returns `true` if the active Navigator can pop. @@ -116,7 +114,7 @@ class GoRouterDelegate extends RouterDelegate final _NavigatorStateIterator iterator = _createNavigatorStateIterator(); while (iterator.moveNext()) { if (iterator.current.canPop()) { - iterator.matchList.last.completer?.complete(result); + iterator.matchList.last.complete(result); iterator.current.pop(result); return; } @@ -272,8 +270,7 @@ class _NavigatorStateIterator extends Iterator { } /// The route match that represent route pushed through [GoRouter.push]. -// TODO(chunhtai): Removes this once imperative API no longer insert route match. -class ImperativeRouteMatch extends RouteMatch { +class ImperativeRouteMatch extends RouteMatch { /// Constructor for [ImperativeRouteMatch]. ImperativeRouteMatch({ required super.route, @@ -282,7 +279,6 @@ class ImperativeRouteMatch extends RouteMatch { required super.error, required super.pageKey, required this.matches, - super.completer, }); /// The matches that produces this route match. diff --git a/packages/go_router/lib/src/match.dart b/packages/go_router/lib/src/match.dart index 1126b9ccb32..fd3e49e5101 100644 --- a/packages/go_router/lib/src/match.dart +++ b/packages/go_router/lib/src/match.dart @@ -12,7 +12,7 @@ import 'path_utils.dart'; import 'route.dart'; /// An instance of a GoRoute plus information about the current location. -class RouteMatch { +class RouteMatch { /// Constructor for [RouteMatch]. RouteMatch({ required this.route, @@ -20,11 +20,10 @@ class RouteMatch { required this.extra, required this.error, required this.pageKey, - this.completer, - }); + }) : _completer = Completer(); // ignore: public_member_api_docs - static RouteMatch? match({ + static RouteMatch? match({ required RouteBase route, required String restLoc, // e.g. person/p1 required String parentSubloc, // e.g. /family/f2 @@ -33,13 +32,12 @@ class RouteMatch { Completer? completer, }) { if (route is ShellRoute) { - return RouteMatch( + return RouteMatch( route: route, subloc: restLoc, extra: extra, error: null, pageKey: ValueKey(route.hashCode.toString()), - completer: completer, ); } else if (route is GoRoute) { assert(!route.path.contains('//')); @@ -55,18 +53,29 @@ class RouteMatch { } final String pathLoc = patternToPath(route.path, encodedParams); final String subloc = concatenatePaths(parentSubloc, pathLoc); - return RouteMatch( + return RouteMatch( route: route, subloc: subloc, extra: extra, error: null, pageKey: ValueKey(route.hashCode.toString()), - completer: completer, ); } throw MatcherError('Unexpected route type: $route', restLoc); } + /// Completes the promise returned by [GoRouter.push]. + void complete([T? value]) { + _completer.complete(value); + } + + /// Returns `true` if the promise returned by [GoRouter.push] has been completed. + bool didComplete() => _completer.isCompleted; + + /// The future of the [RouteMatch] completer. When the future completes, this + /// will return the value passed to [complete]. + Future get future => _completer.future; + /// The matched route. final RouteBase route; @@ -83,5 +92,5 @@ class RouteMatch { final ValueKey pageKey; /// The completer for the promise returned by [GoRouter.push]. - final Completer? completer; + final Completer _completer; } From 845dfbd7ade1ea0b7fc9fd2647d1917982ed6c93 Mon Sep 17 00:00:00 2001 From: Nazareno Cavazzon Date: Fri, 3 Mar 2023 16:42:10 -0300 Subject: [PATCH 03/16] Removed unnecessary completer --- packages/go_router/lib/src/match.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/go_router/lib/src/match.dart b/packages/go_router/lib/src/match.dart index fd3e49e5101..77eda32a64b 100644 --- a/packages/go_router/lib/src/match.dart +++ b/packages/go_router/lib/src/match.dart @@ -29,7 +29,6 @@ class RouteMatch { required String parentSubloc, // e.g. /family/f2 required Map pathParameters, required Object? extra, - Completer? completer, }) { if (route is ShellRoute) { return RouteMatch( From 0105de8710139e350bd778669602d099e0a5f877 Mon Sep 17 00:00:00 2001 From: Nazareno Cavazzon Date: Fri, 3 Mar 2023 16:46:14 -0300 Subject: [PATCH 04/16] removed addPostFrameCallback from docs --- packages/go_router/doc/navigation.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/go_router/doc/navigation.md b/packages/go_router/doc/navigation.md index d72c2cfafe9..f5fa16ac4ba 100644 --- a/packages/go_router/doc/navigation.md +++ b/packages/go_router/doc/navigation.md @@ -74,9 +74,7 @@ Waiting for a value to be returned: ```dart onTap: () { final bool? result = await context.push('/page2'); - WidgetsBinding.instance.addPostFrameCallback((_) { - if(result ?? false)... - }); + if(result ?? false)... } ``` From 4a6352cb24099bf6e2ef681c8a6936bf144917c1 Mon Sep 17 00:00:00 2001 From: Nazareno Cavazzon Date: Fri, 3 Mar 2023 17:23:47 -0300 Subject: [PATCH 05/16] Moved implementation to ImperativeRouteMatch --- packages/go_router/lib/src/delegate.dart | 21 +++++++++++++++-- packages/go_router/lib/src/match.dart | 25 ++++++--------------- packages/go_router/lib/src/parser.dart | 2 +- packages/go_router/lib/src/router.dart | 2 +- packages/go_router/test/delegate_test.dart | 2 +- packages/go_router/test/go_router_test.dart | 4 ++-- 6 files changed, 31 insertions(+), 25 deletions(-) diff --git a/packages/go_router/lib/src/delegate.dart b/packages/go_router/lib/src/delegate.dart index 86585ba2e5f..cd42ecbbfbe 100644 --- a/packages/go_router/lib/src/delegate.dart +++ b/packages/go_router/lib/src/delegate.dart @@ -270,7 +270,7 @@ class _NavigatorStateIterator extends Iterator { } /// The route match that represent route pushed through [GoRouter.push]. -class ImperativeRouteMatch extends RouteMatch { +class ImperativeRouteMatch extends RouteMatch { /// Constructor for [ImperativeRouteMatch]. ImperativeRouteMatch({ required super.route, @@ -279,8 +279,25 @@ class ImperativeRouteMatch extends RouteMatch { required super.error, required super.pageKey, required this.matches, - }); + }) : _completer = Completer(); /// The matches that produces this route match. final RouteMatchList matches; + + /// The completer for the promise returned by [GoRouter.push]. + final Completer _completer; + + /// Completes the promise returned by [GoRouter.push]. + @override + void complete([dynamic value]) { + _completer.complete(value as T?); + } + + /// Returns `true` if the promise returned by [GoRouter.push] has been completed. + @override + bool didComplete() => _completer.isCompleted; + + /// The future of the [RouteMatch] completer. When the future completes, this + /// will return the value passed to [complete]. + Future get future => _completer.future; } diff --git a/packages/go_router/lib/src/match.dart b/packages/go_router/lib/src/match.dart index 77eda32a64b..f013d402c47 100644 --- a/packages/go_router/lib/src/match.dart +++ b/packages/go_router/lib/src/match.dart @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:async'; - import 'package:flutter/foundation.dart'; import 'package:flutter/widgets.dart'; @@ -12,7 +10,7 @@ import 'path_utils.dart'; import 'route.dart'; /// An instance of a GoRoute plus information about the current location. -class RouteMatch { +class RouteMatch { /// Constructor for [RouteMatch]. RouteMatch({ required this.route, @@ -20,10 +18,10 @@ class RouteMatch { required this.extra, required this.error, required this.pageKey, - }) : _completer = Completer(); + }); // ignore: public_member_api_docs - static RouteMatch? match({ + static RouteMatch? match({ required RouteBase route, required String restLoc, // e.g. person/p1 required String parentSubloc, // e.g. /family/f2 @@ -31,7 +29,7 @@ class RouteMatch { required Object? extra, }) { if (route is ShellRoute) { - return RouteMatch( + return RouteMatch( route: route, subloc: restLoc, extra: extra, @@ -52,7 +50,7 @@ class RouteMatch { } final String pathLoc = patternToPath(route.path, encodedParams); final String subloc = concatenatePaths(parentSubloc, pathLoc); - return RouteMatch( + return RouteMatch( route: route, subloc: subloc, extra: extra, @@ -64,16 +62,10 @@ class RouteMatch { } /// Completes the promise returned by [GoRouter.push]. - void complete([T? value]) { - _completer.complete(value); - } + void complete([dynamic value]) {} /// Returns `true` if the promise returned by [GoRouter.push] has been completed. - bool didComplete() => _completer.isCompleted; - - /// The future of the [RouteMatch] completer. When the future completes, this - /// will return the value passed to [complete]. - Future get future => _completer.future; + bool? didComplete() => null; /// The matched route. final RouteBase route; @@ -89,7 +81,4 @@ class RouteMatch { /// Value key of type string, to hold a unique reference to a page. final ValueKey pageKey; - - /// The completer for the promise returned by [GoRouter.push]. - final Completer _completer; } diff --git a/packages/go_router/lib/src/parser.dart b/packages/go_router/lib/src/parser.dart index e91eaf352a0..b45526391ee 100644 --- a/packages/go_router/lib/src/parser.dart +++ b/packages/go_router/lib/src/parser.dart @@ -104,7 +104,7 @@ class GoRouteInformationParser extends RouteInformationParser { } if (configuration.matches.last is ImperativeRouteMatch) { configuration = - (configuration.matches.last as ImperativeRouteMatch).matches; + (configuration.matches.last as ImperativeRouteMatch).matches; } return RouteInformation( location: configuration.uri.toString(), diff --git a/packages/go_router/lib/src/router.dart b/packages/go_router/lib/src/router.dart index 1c6d6b1b77b..e32cbcf9b49 100644 --- a/packages/go_router/lib/src/router.dart +++ b/packages/go_router/lib/src/router.dart @@ -151,7 +151,7 @@ class GoRouter extends ChangeNotifier implements RouterConfig { routerDelegate.currentConfiguration.matches.last is ImperativeRouteMatch) { newLocation = (routerDelegate.currentConfiguration.matches.last - as ImperativeRouteMatch) + as ImperativeRouteMatch) .matches .uri .toString(); diff --git a/packages/go_router/test/delegate_test.dart b/packages/go_router/test/delegate_test.dart index 62f4b691d98..1cb3db5fcf7 100644 --- a/packages/go_router/test/delegate_test.dart +++ b/packages/go_router/test/delegate_test.dart @@ -141,7 +141,7 @@ void main() { reason: 'The last match should have been removed', ); expect( - (goRouter.routerDelegate.matches.last as ImperativeRouteMatch) + (goRouter.routerDelegate.matches.last as ImperativeRouteMatch) .matches .uri .toString(), diff --git a/packages/go_router/test/go_router_test.dart b/packages/go_router/test/go_router_test.dart index e65b0328b8b..621f627c7d2 100644 --- a/packages/go_router/test/go_router_test.dart +++ b/packages/go_router/test/go_router_test.dart @@ -2396,8 +2396,8 @@ void main() { expect(router.location, loc); expect(matches.matches, hasLength(2)); expect(find.byType(PersonScreen), findsOneWidget); - final ImperativeRouteMatch imperativeRouteMatch = - matches.matches.last as ImperativeRouteMatch; + final ImperativeRouteMatch imperativeRouteMatch = + matches.matches.last as ImperativeRouteMatch; expect(imperativeRouteMatch.matches.pathParameters['fid'], fid); expect(imperativeRouteMatch.matches.pathParameters['pid'], pid); }); From ecee639faf7bb9458025d13a993d3c5d7cfc3b2a Mon Sep 17 00:00:00 2001 From: Nazareno Cavazzon Date: Fri, 3 Mar 2023 17:28:31 -0300 Subject: [PATCH 06/16] Documented complete() method --- packages/go_router/lib/src/match.dart | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/packages/go_router/lib/src/match.dart b/packages/go_router/lib/src/match.dart index f013d402c47..3fbe892dc54 100644 --- a/packages/go_router/lib/src/match.dart +++ b/packages/go_router/lib/src/match.dart @@ -61,7 +61,24 @@ class RouteMatch { throw MatcherError('Unexpected route type: $route', restLoc); } - /// Completes the promise returned by [GoRouter.push]. + /// Completes the promise returned by [GoRouter.push], allowing comunication + /// between pages. + /// + /// If the promise has already been completed, this method does nothing. + /// + /// E.g.: + /// ```dart + /// final bool? result = await context.push('/page2'); + /// if(result ?? false){ + /// // do something + /// } + /// ``` + /// When the page is popped, the promise is completed with the value passed, + /// and the push method returns. + /// to [Navigator.pop]. + /// ```dart + /// context.pop(true); + /// ``` void complete([dynamic value]) {} /// Returns `true` if the promise returned by [GoRouter.push] has been completed. From 98e1216b7ee2e5b08eebd53ade054fb47c351c5d Mon Sep 17 00:00:00 2001 From: Nazareno Cavazzon Date: Fri, 3 Mar 2023 19:04:19 -0300 Subject: [PATCH 07/16] Changes --- packages/go_router/lib/src/delegate.dart | 5 ----- packages/go_router/lib/src/match.dart | 24 ++------------------- packages/go_router/lib/src/parser.dart | 2 +- packages/go_router/lib/src/router.dart | 2 +- packages/go_router/test/delegate_test.dart | 2 +- packages/go_router/test/go_router_test.dart | 4 ++-- 6 files changed, 7 insertions(+), 32 deletions(-) diff --git a/packages/go_router/lib/src/delegate.dart b/packages/go_router/lib/src/delegate.dart index cd42ecbbfbe..200ddcd34d7 100644 --- a/packages/go_router/lib/src/delegate.dart +++ b/packages/go_router/lib/src/delegate.dart @@ -287,16 +287,11 @@ class ImperativeRouteMatch extends RouteMatch { /// The completer for the promise returned by [GoRouter.push]. final Completer _completer; - /// Completes the promise returned by [GoRouter.push]. @override void complete([dynamic value]) { _completer.complete(value as T?); } - /// Returns `true` if the promise returned by [GoRouter.push] has been completed. - @override - bool didComplete() => _completer.isCompleted; - /// The future of the [RouteMatch] completer. When the future completes, this /// will return the value passed to [complete]. Future get future => _completer.future; diff --git a/packages/go_router/lib/src/match.dart b/packages/go_router/lib/src/match.dart index 3fbe892dc54..7808f4362cb 100644 --- a/packages/go_router/lib/src/match.dart +++ b/packages/go_router/lib/src/match.dart @@ -61,28 +61,8 @@ class RouteMatch { throw MatcherError('Unexpected route type: $route', restLoc); } - /// Completes the promise returned by [GoRouter.push], allowing comunication - /// between pages. - /// - /// If the promise has already been completed, this method does nothing. - /// - /// E.g.: - /// ```dart - /// final bool? result = await context.push('/page2'); - /// if(result ?? false){ - /// // do something - /// } - /// ``` - /// When the page is popped, the promise is completed with the value passed, - /// and the push method returns. - /// to [Navigator.pop]. - /// ```dart - /// context.pop(true); - /// ``` - void complete([dynamic value]) {} - - /// Returns `true` if the promise returned by [GoRouter.push] has been completed. - bool? didComplete() => null; + /// Called when the corresponding [Route] associated with this route match is completed. + void complete([Object? value]) {} /// The matched route. final RouteBase route; diff --git a/packages/go_router/lib/src/parser.dart b/packages/go_router/lib/src/parser.dart index b45526391ee..37c3e98bd02 100644 --- a/packages/go_router/lib/src/parser.dart +++ b/packages/go_router/lib/src/parser.dart @@ -104,7 +104,7 @@ class GoRouteInformationParser extends RouteInformationParser { } if (configuration.matches.last is ImperativeRouteMatch) { configuration = - (configuration.matches.last as ImperativeRouteMatch).matches; + (configuration.matches.last as ImperativeRouteMatch).matches; } return RouteInformation( location: configuration.uri.toString(), diff --git a/packages/go_router/lib/src/router.dart b/packages/go_router/lib/src/router.dart index e32cbcf9b49..320a2d0a8f0 100644 --- a/packages/go_router/lib/src/router.dart +++ b/packages/go_router/lib/src/router.dart @@ -151,7 +151,7 @@ class GoRouter extends ChangeNotifier implements RouterConfig { routerDelegate.currentConfiguration.matches.last is ImperativeRouteMatch) { newLocation = (routerDelegate.currentConfiguration.matches.last - as ImperativeRouteMatch) + as ImperativeRouteMatch) .matches .uri .toString(); diff --git a/packages/go_router/test/delegate_test.dart b/packages/go_router/test/delegate_test.dart index 1cb3db5fcf7..3a67912f34d 100644 --- a/packages/go_router/test/delegate_test.dart +++ b/packages/go_router/test/delegate_test.dart @@ -141,7 +141,7 @@ void main() { reason: 'The last match should have been removed', ); expect( - (goRouter.routerDelegate.matches.last as ImperativeRouteMatch) + (goRouter.routerDelegate.matches.last as ImperativeRouteMatch) .matches .uri .toString(), diff --git a/packages/go_router/test/go_router_test.dart b/packages/go_router/test/go_router_test.dart index 621f627c7d2..f8aa6646218 100644 --- a/packages/go_router/test/go_router_test.dart +++ b/packages/go_router/test/go_router_test.dart @@ -2396,8 +2396,8 @@ void main() { expect(router.location, loc); expect(matches.matches, hasLength(2)); expect(find.byType(PersonScreen), findsOneWidget); - final ImperativeRouteMatch imperativeRouteMatch = - matches.matches.last as ImperativeRouteMatch; + final ImperativeRouteMatch imperativeRouteMatch = + matches.matches.last as ImperativeRouteMatch; expect(imperativeRouteMatch.matches.pathParameters['fid'], fid); expect(imperativeRouteMatch.matches.pathParameters['pid'], pid); }); From 1b5e37b0ecbeb58cb9da92f479bfbfbb049262d9 Mon Sep 17 00:00:00 2001 From: Nazareno Cavazzon Date: Fri, 3 Mar 2023 19:04:52 -0300 Subject: [PATCH 08/16] Update delegate.dart --- packages/go_router/lib/src/delegate.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/go_router/lib/src/delegate.dart b/packages/go_router/lib/src/delegate.dart index 200ddcd34d7..574bb4db352 100644 --- a/packages/go_router/lib/src/delegate.dart +++ b/packages/go_router/lib/src/delegate.dart @@ -284,7 +284,7 @@ class ImperativeRouteMatch extends RouteMatch { /// The matches that produces this route match. final RouteMatchList matches; - /// The completer for the promise returned by [GoRouter.push]. + /// The completer for the future returned by [GoRouter.push]. final Completer _completer; @override From b73471430b576d2b7ef7da83dc3b106bbaf87e28 Mon Sep 17 00:00:00 2001 From: Nazareno Cavazzon Date: Fri, 3 Mar 2023 19:05:35 -0300 Subject: [PATCH 09/16] Update go_router_test.dart --- packages/go_router/test/go_router_test.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/go_router/test/go_router_test.dart b/packages/go_router/test/go_router_test.dart index f8aa6646218..73c66b36f78 100644 --- a/packages/go_router/test/go_router_test.dart +++ b/packages/go_router/test/go_router_test.dart @@ -2655,7 +2655,7 @@ void main() { expect(router.extra, extra); }); - testWidgets('calls [push] on closest GoRouter with a promise', + testWidgets('calls [push] on closest GoRouter and waits for result', (WidgetTester tester) async { final GoRouterPushSpy router = GoRouterPushSpy(routes: routes); await tester.pumpWidget( @@ -2696,7 +2696,7 @@ void main() { expect(router.extra, extra); }); - testWidgets('calls [pushNamed] on closest GoRouter with a promise', + testWidgets('calls [pushNamed] on closest GoRouter and waits for result', (WidgetTester tester) async { final GoRouterPushNamedSpy router = GoRouterPushNamedSpy(routes: routes); await tester.pumpWidget( From 9b556a30708db7775e860c4625a958148bb7a3ff Mon Sep 17 00:00:00 2001 From: Nazareno Cavazzon Date: Fri, 3 Mar 2023 20:24:46 -0300 Subject: [PATCH 10/16] Last fixes --- packages/go_router/CHANGELOG.md | 3 --- packages/go_router/lib/src/delegate.dart | 8 ++++---- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/packages/go_router/CHANGELOG.md b/packages/go_router/CHANGELOG.md index 1b7f5ead874..d1fefcc64db 100644 --- a/packages/go_router/CHANGELOG.md +++ b/packages/go_router/CHANGELOG.md @@ -1,9 +1,6 @@ ## 6.3.0 - Supports for returning values on pop. - -## NEXT - - Updates compileSdkVersion to 33. - Updates example app to iOS 11. diff --git a/packages/go_router/lib/src/delegate.dart b/packages/go_router/lib/src/delegate.dart index 574bb4db352..ffddf524ce3 100644 --- a/packages/go_router/lib/src/delegate.dart +++ b/packages/go_router/lib/src/delegate.dart @@ -95,7 +95,7 @@ class GoRouterDelegate extends RouterDelegate _matchList.push(newPageKeyMatch); notifyListeners(); - return newPageKeyMatch.future; + return newPageKeyMatch._future; } /// Returns `true` if the active Navigator can pop. @@ -292,7 +292,7 @@ class ImperativeRouteMatch extends RouteMatch { _completer.complete(value as T?); } - /// The future of the [RouteMatch] completer. When the future completes, this - /// will return the value passed to [complete]. - Future get future => _completer.future; + /// The future of the [RouteMatch] completer. + /// When the future completes, this will return the value passed to [complete]. + Future get _future => _completer.future; } From 8d34c156644ca778c0e5ed04e27b068de52cba10 Mon Sep 17 00:00:00 2001 From: Nazareno Cavazzon Date: Fri, 3 Mar 2023 20:58:02 -0300 Subject: [PATCH 11/16] Update match.dart --- packages/go_router/lib/src/match.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/go_router/lib/src/match.dart b/packages/go_router/lib/src/match.dart index 7808f4362cb..f4840f2ec86 100644 --- a/packages/go_router/lib/src/match.dart +++ b/packages/go_router/lib/src/match.dart @@ -2,7 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'package:flutter/foundation.dart'; import 'package:flutter/widgets.dart'; import 'matching.dart'; From 0f0eafc01e008ab7f44697b1ef8314ffe911c6d1 Mon Sep 17 00:00:00 2001 From: Nazareno Cavazzon Date: Fri, 3 Mar 2023 22:08:28 -0300 Subject: [PATCH 12/16] Update packages/go_router/CHANGELOG.md Co-authored-by: HBS <30705623+MrHBS@users.noreply.github.com> --- packages/go_router/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/go_router/CHANGELOG.md b/packages/go_router/CHANGELOG.md index d1fefcc64db..5b0822af91b 100644 --- a/packages/go_router/CHANGELOG.md +++ b/packages/go_router/CHANGELOG.md @@ -1,6 +1,6 @@ ## 6.3.0 -- Supports for returning values on pop. +- Supports returning values on pop. - Updates compileSdkVersion to 33. - Updates example app to iOS 11. From c3ed204f8e18877b5f3f04b5801cdad4ecc1ca23 Mon Sep 17 00:00:00 2001 From: Nazareno Cavazzon Date: Thu, 23 Mar 2023 14:10:55 -0300 Subject: [PATCH 13/16] Update version to 6.5.0 --- packages/go_router/CHANGELOG.md | 13 ++++++++++++- packages/go_router/pubspec.yaml | 2 +- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/packages/go_router/CHANGELOG.md b/packages/go_router/CHANGELOG.md index ba58b4e319c..e8c655bf8df 100644 --- a/packages/go_router/CHANGELOG.md +++ b/packages/go_router/CHANGELOG.md @@ -1,6 +1,16 @@ -## 6.3.0 +## 6.5.0 - Supports returning values on pop. + +## 6.4.1 +- Adds `initialExtra` to **GoRouter** to pass extra data alongside `initialRoute`. + +## 6.4.0 + +- Adds `replace` method to that replaces the current route with a new one and keeps the same page key. This is useful for when you want to update the query params without changing the page key ([#115902]https://github.com/flutter/flutter/issues/115902). + +## 6.3.0 + - Aligns Dart and Flutter SDK constraints. - Updates compileSdkVersion to 33. - Updates example app to iOS 11. @@ -16,6 +26,7 @@ - Adds `GoRouter.maybeOf` to get the closest `GoRouter` from the context, if there is any. + ## 6.0.10 - Adds helpers for go_router_builder for ShellRoute support diff --git a/packages/go_router/pubspec.yaml b/packages/go_router/pubspec.yaml index c5f6ea14d35..546769e0607 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: 6.3.0 +version: 6.5.0 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 From 02a4f5c36e351d796430561dafc6525a3977e423 Mon Sep 17 00:00:00 2001 From: Nazareno Cavazzon Date: Thu, 23 Mar 2023 14:41:10 -0300 Subject: [PATCH 14/16] Updated from main --- packages/go_router/lib/src/delegate.dart | 62 ++++++++++++--- .../go_router/lib/src/misc/extensions.dart | 46 ++++++++++- packages/go_router/lib/src/router.dart | 79 +++++++++++++++++-- 3 files changed, 168 insertions(+), 19 deletions(-) diff --git a/packages/go_router/lib/src/delegate.dart b/packages/go_router/lib/src/delegate.dart index ffddf524ce3..7c4c6e32401 100644 --- a/packages/go_router/lib/src/delegate.dart +++ b/packages/go_router/lib/src/delegate.dart @@ -50,7 +50,7 @@ class GoRouterDelegate extends RouterDelegate /// /// This is used to generate a unique key for each route. /// - /// For example, it would could be equal to: + /// For example, it could be equal to: /// ```dart /// { /// 'family': 1, @@ -75,15 +75,15 @@ class GoRouterDelegate extends RouterDelegate return false; } - /// Pushes the given location onto the page stack - Future push(RouteMatchList matches) async { - assert(matches.last.route is! ShellRoute); - + ValueKey _getNewKeyForPath(String path) { // Remap the pageKey to allow any number of the same page on the stack - final int count = (_pushCounts[matches.fullpath] ?? 0) + 1; - _pushCounts[matches.fullpath] = count; - final ValueKey pageKey = - ValueKey('${matches.fullpath}-p$count'); + final int count = (_pushCounts[path] ?? -1) + 1; + _pushCounts[path] = count; + return ValueKey('$path-p$count'); + } + + Future _push( + RouteMatchList matches, ValueKey pageKey) async { final ImperativeRouteMatch newPageKeyMatch = ImperativeRouteMatch( route: matches.last.route, subloc: matches.last.subloc, @@ -94,10 +94,26 @@ class GoRouterDelegate extends RouterDelegate ); _matchList.push(newPageKeyMatch); - notifyListeners(); return newPageKeyMatch._future; } + /// Pushes the given location onto the page stack. + /// + /// See also: + /// * [pushReplacement] which replaces the top-most page of the page stack and + /// always use a new page key. + /// * [replace] which replaces the top-most page of the page stack but treats + /// it as the same page. The page key will be reused. This will preserve the + /// state and not run any page animation. + Future push(RouteMatchList matches) async { + assert(matches.last.route is! ShellRoute); + + final ValueKey pageKey = _getNewKeyForPath(matches.fullpath); + final Future future = _push(matches, pageKey); + notifyListeners(); + return future; + } + /// Returns `true` if the active Navigator can pop. bool canPop() { final _NavigatorStateIterator iterator = _createNavigatorStateIterator(); @@ -150,13 +166,38 @@ class GoRouterDelegate extends RouterDelegate /// Replaces the top-most page of the page stack with the given one. /// + /// The page key of the new page will always be different from the old one. + /// /// See also: /// * [push] which pushes the given location onto the page stack. + /// * [replace] which replaces the top-most page of the page stack but treats + /// it as the same page. The page key will be reused. This will preserve the + /// state and not run any page animation. void pushReplacement(RouteMatchList matches) { + assert(matches.last.route is! ShellRoute); _matchList.remove(_matchList.last); push(matches); // [push] will notify the listeners. } + /// Replaces the top-most page of the page stack with the given one but treats + /// it as the same page. + /// + /// The page key will be reused. This will preserve the state and not run any + /// page animation. + /// + /// See also: + /// * [push] which pushes the given location onto the page stack. + /// * [pushReplacement] which replaces the top-most page of the page stack but + /// always uses a new page key. + void replace(RouteMatchList matches) { + assert(matches.last.route is! ShellRoute); + final RouteMatch routeMatch = _matchList.last; + final ValueKey pageKey = routeMatch.pageKey; + _matchList.remove(routeMatch); + _push(matches, pageKey); + notifyListeners(); + } + /// For internal use; visible for testing only. @visibleForTesting RouteMatchList get matches => _matchList; @@ -270,6 +311,7 @@ class _NavigatorStateIterator extends Iterator { } /// The route match that represent route pushed through [GoRouter.push]. +// TODO(chunhtai): Removes this once imperative API no longer insert route match. class ImperativeRouteMatch extends RouteMatch { /// Constructor for [ImperativeRouteMatch]. ImperativeRouteMatch({ diff --git a/packages/go_router/lib/src/misc/extensions.dart b/packages/go_router/lib/src/misc/extensions.dart index 08410ba7fff..47f5b08cd8e 100644 --- a/packages/go_router/lib/src/misc/extensions.dart +++ b/packages/go_router/lib/src/misc/extensions.dart @@ -37,8 +37,15 @@ extension GoRouterHelper on BuildContext { ); /// Push a location onto the page stack. + /// + /// See also: + /// * [pushReplacement] which replaces the top-most page of the page stack and + /// always uses a new page key. + /// * [replace] which replaces the top-most page of the page stack but treats + /// it as the same page. The page key will be reused. This will preserve the + /// state and not run any page animation. Future push(String location, {Object? extra}) => - GoRouter.of(this).push(location, extra: extra); + GoRouter.of(this).push(location, extra: extra); /// Navigate to a named route onto the page stack. Future pushNamed( @@ -66,7 +73,10 @@ extension GoRouterHelper on BuildContext { /// /// See also: /// * [go] which navigates to the location. - /// * [push] which pushes the location onto the page stack. + /// * [push] which pushes the given location onto the page stack. + /// * [replace] which replaces the top-most page of the page stack but treats + /// it as the same page. The page key will be reused. This will preserve the + /// state and not run any page animation. void pushReplacement(String location, {Object? extra}) => GoRouter.of(this).pushReplacement(location, extra: extra); @@ -89,4 +99,36 @@ extension GoRouterHelper on BuildContext { queryParams: queryParams, extra: extra, ); + + /// Replaces the top-most page of the page stack with the given one but treats + /// it as the same page. + /// + /// The page key will be reused. This will preserve the state and not run any + /// page animation. + /// + /// See also: + /// * [push] which pushes the given location onto the page stack. + /// * [pushReplacement] which replaces the top-most page of the page stack but + /// always uses a new page key. + void replace(String location, {Object? extra}) => + GoRouter.of(this).replace(location, extra: extra); + + /// Replaces the top-most page with the named route and optional parameters, + /// preserving the page key. + /// + /// This will preserve the state and not run any page animation. Optional + /// parameters can be providded to the named route, e.g. `name='person', + /// params={'fid': 'f2', 'pid': 'p1'}`. + /// + /// See also: + /// * [pushNamed] which pushes the given location onto the page stack. + /// * [pushReplacementNamed] which replaces the top-most page of the page + /// stack but always uses a new page key. + void replaceNamed( + String name, { + Map params = const {}, + Map queryParams = const {}, + Object? extra, + }) => + GoRouter.of(this).replaceNamed(name, extra: extra); } diff --git a/packages/go_router/lib/src/router.dart b/packages/go_router/lib/src/router.dart index 320a2d0a8f0..21dc114e21f 100644 --- a/packages/go_router/lib/src/router.dart +++ b/packages/go_router/lib/src/router.dart @@ -59,11 +59,16 @@ class GoRouter extends ChangeNotifier implements RouterConfig { int redirectLimit = 5, bool routerNeglect = false, String? initialLocation, + Object? initialExtra, List? observers, bool debugLogDiagnostics = false, GlobalKey? navigatorKey, String? restorationScopeId, - }) : backButtonDispatcher = RootBackButtonDispatcher() { + }) : backButtonDispatcher = RootBackButtonDispatcher(), + assert( + initialExtra == null || initialLocation != null, + 'initialLocation must be set in order to use initialExtra', + ) { setLogging(enabled: debugLogDiagnostics); WidgetsFlutterBinding.ensureInitialized(); @@ -82,9 +87,12 @@ class GoRouter extends ChangeNotifier implements RouterConfig { ); _routeInformationProvider = GoRouteInformationProvider( - initialRouteInformation: RouteInformation( - location: _effectiveInitialLocation(initialLocation)), - refreshListenable: refreshListenable); + initialRouteInformation: RouteInformation( + location: _effectiveInitialLocation(initialLocation), + state: initialExtra, + ), + refreshListenable: refreshListenable, + ); _routerDelegate = GoRouterDelegate( configuration: _routeConfiguration, @@ -151,7 +159,7 @@ class GoRouter extends ChangeNotifier implements RouterConfig { routerDelegate.currentConfiguration.matches.last is ImperativeRouteMatch) { newLocation = (routerDelegate.currentConfiguration.matches.last - as ImperativeRouteMatch) + as ImperativeRouteMatch) .matches .uri .toString(); @@ -203,7 +211,14 @@ class GoRouter extends ChangeNotifier implements RouterConfig { ); /// Push a URI location onto the page stack w/ optional query parameters, e.g. - /// `/family/f2/person/p1?color=blue` + /// `/family/f2/person/p1?color=blue`. + /// + /// See also: + /// * [pushReplacement] which replaces the top-most page of the page stack and + /// always use a new page key. + /// * [replace] which replaces the top-most page of the page stack but treats + /// it as the same page. The page key will be reused. This will preserve the + /// state and not run any page animation. Future push(String location, {Object? extra}) async { assert(() { log.info('pushing $location'); @@ -216,6 +231,7 @@ class GoRouter extends ChangeNotifier implements RouterConfig { // https://github.com/flutter/flutter/issues/99112 _routerDelegate.navigatorKey.currentContext!, ); + return _routerDelegate.push(matches); } @@ -237,7 +253,10 @@ class GoRouter extends ChangeNotifier implements RouterConfig { /// /// See also: /// * [go] which navigates to the location. - /// * [push] which pushes the location onto the page stack. + /// * [push] which pushes the given location onto the page stack. + /// * [replace] which replaces the top-most page of the page stack but treats + /// it as the same page. The page key will be reused. This will preserve the + /// state and not run any page animation. void pushReplacement(String location, {Object? extra}) { routeInformationParser .parseRouteInformationWithDependencies( @@ -270,6 +289,52 @@ class GoRouter extends ChangeNotifier implements RouterConfig { ); } + /// Replaces the top-most page of the page stack with the given one but treats + /// it as the same page. + /// + /// The page key will be reused. This will preserve the state and not run any + /// page animation. + /// + /// See also: + /// * [push] which pushes the given location onto the page stack. + /// * [pushReplacement] which replaces the top-most page of the page stack but + /// always uses a new page key. + void replace(String location, {Object? extra}) { + routeInformationParser + .parseRouteInformationWithDependencies( + RouteInformation(location: location, state: extra), + // TODO(chunhtai): avoid accessing the context directly through global key. + // https://github.com/flutter/flutter/issues/99112 + _routerDelegate.navigatorKey.currentContext!, + ) + .then((RouteMatchList matchList) { + routerDelegate.replace(matchList); + }); + } + + /// Replaces the top-most page with the named route and optional parameters, + /// preserving the page key. + /// + /// This will preserve the state and not run any page animation. Optional + /// parameters can be providded to the named route, e.g. `name='person', + /// params={'fid': 'f2', 'pid': 'p1'}`. + /// + /// See also: + /// * [pushNamed] which pushes the given location onto the page stack. + /// * [pushReplacementNamed] which replaces the top-most page of the page + /// stack but always uses a new page key. + void replaceNamed( + String name, { + Map params = const {}, + Map queryParams = const {}, + Object? extra, + }) { + replace( + namedLocation(name, params: params, queryParams: queryParams), + extra: extra, + ); + } + /// Pop the top-most route off the current screen. /// /// If the top-most route is a pop up or dialog, this method pops it instead From 801c7c62451f5f7837cbb882c59d8a3aab1888e0 Mon Sep 17 00:00:00 2001 From: Nazareno Cavazzon Date: Thu, 23 Mar 2023 14:42:44 -0300 Subject: [PATCH 15/16] Type annotations --- packages/go_router/test/delegate_test.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/go_router/test/delegate_test.dart b/packages/go_router/test/delegate_test.dart index 4f3d051bb75..e40084a77fd 100644 --- a/packages/go_router/test/delegate_test.dart +++ b/packages/go_router/test/delegate_test.dart @@ -270,7 +270,7 @@ void main() { reason: 'The last match should have been removed', ); expect( - (goRouter.routerDelegate.matches.last as ImperativeRouteMatch) + (goRouter.routerDelegate.matches.last as ImperativeRouteMatch) .matches .uri .toString(), @@ -393,7 +393,7 @@ void main() { reason: 'The last match should have been removed', ); expect( - (goRouter.routerDelegate.matches.last as ImperativeRouteMatch) + (goRouter.routerDelegate.matches.last as ImperativeRouteMatch) .matches .uri .toString(), From 5d883367e3fb040e0fb2116b57f62a1ffa781cda Mon Sep 17 00:00:00 2001 From: Nazareno Cavazzon Date: Thu, 23 Mar 2023 14:53:00 -0300 Subject: [PATCH 16/16] Update router.dart --- packages/go_router/lib/src/router.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/go_router/lib/src/router.dart b/packages/go_router/lib/src/router.dart index 21dc114e21f..b63cb997320 100644 --- a/packages/go_router/lib/src/router.dart +++ b/packages/go_router/lib/src/router.dart @@ -159,7 +159,7 @@ class GoRouter extends ChangeNotifier implements RouterConfig { routerDelegate.currentConfiguration.matches.last is ImperativeRouteMatch) { newLocation = (routerDelegate.currentConfiguration.matches.last - as ImperativeRouteMatch) + as ImperativeRouteMatch) .matches .uri .toString();