Skip to content

Commit 1f67d0c

Browse files
Return value when pop (#3368)
Return value when pop
1 parent 0826798 commit 1f67d0c

12 files changed

+117
-31
lines changed

packages/go_router/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 6.5.0
2+
3+
- Supports returning values on pop.
4+
15
## 6.4.1
26
- Adds `initialExtra` to **GoRouter** to pass extra data alongside `initialRoute`.
37

@@ -22,6 +26,7 @@
2226

2327
- Adds `GoRouter.maybeOf` to get the closest `GoRouter` from the context, if there is any.
2428

29+
2530
## 6.0.10
2631

2732
- Adds helpers for go_router_builder for ShellRoute support

packages/go_router/doc/navigation.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,4 +68,21 @@ Navigator.of(context).push(
6868
);
6969
```
7070

71+
## Returning values
72+
Waiting for a value to be returned:
73+
74+
```dart
75+
onTap: () {
76+
final bool? result = await context.push<bool>('/page2');
77+
if(result ?? false)...
78+
}
79+
```
80+
81+
Returning a value:
82+
83+
```dart
84+
onTap: () => context.pop(true)
85+
```
86+
87+
7188
[Named routes]: https://pub.dev/documentation/go_router/latest/topics/Named%20routes-topic.html

packages/go_router/lib/src/delegate.dart

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -82,8 +82,9 @@ class GoRouterDelegate extends RouterDelegate<RouteMatchList>
8282
return ValueKey<String>('$path-p$count');
8383
}
8484

85-
void _push(RouteMatchList matches, ValueKey<String> pageKey) {
86-
final ImperativeRouteMatch newPageKeyMatch = ImperativeRouteMatch(
85+
Future<T?> _push<T extends Object?>(
86+
RouteMatchList matches, ValueKey<String> pageKey) async {
87+
final ImperativeRouteMatch<T> newPageKeyMatch = ImperativeRouteMatch<T>(
8788
route: matches.last.route,
8889
subloc: matches.last.subloc,
8990
extra: matches.last.extra,
@@ -93,6 +94,7 @@ class GoRouterDelegate extends RouterDelegate<RouteMatchList>
9394
);
9495

9596
_matchList.push(newPageKeyMatch);
97+
return newPageKeyMatch._future;
9698
}
9799

98100
/// Pushes the given location onto the page stack.
@@ -103,12 +105,13 @@ class GoRouterDelegate extends RouterDelegate<RouteMatchList>
103105
/// * [replace] which replaces the top-most page of the page stack but treats
104106
/// it as the same page. The page key will be reused. This will preserve the
105107
/// state and not run any page animation.
106-
void push(RouteMatchList matches) {
108+
Future<T?> push<T extends Object?>(RouteMatchList matches) async {
107109
assert(matches.last.route is! ShellRoute);
108110

109111
final ValueKey<String> pageKey = _getNewKeyForPath(matches.fullpath);
110-
_push(matches, pageKey);
112+
final Future<T?> future = _push(matches, pageKey);
111113
notifyListeners();
114+
return future;
112115
}
113116

114117
/// Returns `true` if the active Navigator can pop.
@@ -127,6 +130,7 @@ class GoRouterDelegate extends RouterDelegate<RouteMatchList>
127130
final _NavigatorStateIterator iterator = _createNavigatorStateIterator();
128131
while (iterator.moveNext()) {
129132
if (iterator.current.canPop()) {
133+
iterator.matchList.last.complete(result);
130134
iterator.current.pop<T>(result);
131135
return;
132136
}
@@ -308,7 +312,7 @@ class _NavigatorStateIterator extends Iterator<NavigatorState> {
308312

309313
/// The route match that represent route pushed through [GoRouter.push].
310314
// TODO(chunhtai): Removes this once imperative API no longer insert route match.
311-
class ImperativeRouteMatch extends RouteMatch {
315+
class ImperativeRouteMatch<T> extends RouteMatch {
312316
/// Constructor for [ImperativeRouteMatch].
313317
ImperativeRouteMatch({
314318
required super.route,
@@ -317,8 +321,20 @@ class ImperativeRouteMatch extends RouteMatch {
317321
required super.error,
318322
required super.pageKey,
319323
required this.matches,
320-
});
324+
}) : _completer = Completer<T?>();
321325

322326
/// The matches that produces this route match.
323327
final RouteMatchList matches;
328+
329+
/// The completer for the future returned by [GoRouter.push].
330+
final Completer<T?> _completer;
331+
332+
@override
333+
void complete([dynamic value]) {
334+
_completer.complete(value as T?);
335+
}
336+
337+
/// The future of the [RouteMatch] completer.
338+
/// When the future completes, this will return the value passed to [complete].
339+
Future<T?> get _future => _completer.future;
324340
}

packages/go_router/lib/src/match.dart

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file.
44

5-
import 'package:flutter/foundation.dart';
65
import 'package:flutter/widgets.dart';
76

87
import 'matching.dart';
@@ -61,6 +60,9 @@ class RouteMatch {
6160
throw MatcherError('Unexpected route type: $route', restLoc);
6261
}
6362

63+
/// Called when the corresponding [Route] associated with this route match is completed.
64+
void complete([Object? value]) {}
65+
6466
/// The matched route.
6567
final RouteBase route;
6668

packages/go_router/lib/src/misc/extensions.dart

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,17 +44,17 @@ extension GoRouterHelper on BuildContext {
4444
/// * [replace] which replaces the top-most page of the page stack but treats
4545
/// it as the same page. The page key will be reused. This will preserve the
4646
/// state and not run any page animation.
47-
void push(String location, {Object? extra}) =>
48-
GoRouter.of(this).push(location, extra: extra);
47+
Future<T?> push<T extends Object?>(String location, {Object? extra}) =>
48+
GoRouter.of(this).push<T>(location, extra: extra);
4949

5050
/// Navigate to a named route onto the page stack.
51-
void pushNamed(
51+
Future<T?> pushNamed<T extends Object?>(
5252
String name, {
5353
Map<String, String> params = const <String, String>{},
5454
Map<String, dynamic> queryParams = const <String, dynamic>{},
5555
Object? extra,
5656
}) =>
57-
GoRouter.of(this).pushNamed(
57+
GoRouter.of(this).pushNamed<T>(
5858
name,
5959
params: params,
6060
queryParams: queryParams,

packages/go_router/lib/src/parser.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ class GoRouteInformationParser extends RouteInformationParser<RouteMatchList> {
104104
}
105105
if (configuration.matches.last is ImperativeRouteMatch) {
106106
configuration =
107-
(configuration.matches.last as ImperativeRouteMatch).matches;
107+
(configuration.matches.last as ImperativeRouteMatch<Object?>).matches;
108108
}
109109
return RouteInformation(
110110
location: configuration.uri.toString(),

packages/go_router/lib/src/router.dart

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ class GoRouter extends ChangeNotifier implements RouterConfig<RouteMatchList> {
159159
routerDelegate.currentConfiguration.matches.last
160160
is ImperativeRouteMatch) {
161161
newLocation = (routerDelegate.currentConfiguration.matches.last
162-
as ImperativeRouteMatch)
162+
as ImperativeRouteMatch<Object?>)
163163
.matches
164164
.uri
165165
.toString();
@@ -219,32 +219,31 @@ class GoRouter extends ChangeNotifier implements RouterConfig<RouteMatchList> {
219219
/// * [replace] which replaces the top-most page of the page stack but treats
220220
/// it as the same page. The page key will be reused. This will preserve the
221221
/// state and not run any page animation.
222-
void push(String location, {Object? extra}) {
222+
Future<T?> push<T extends Object?>(String location, {Object? extra}) async {
223223
assert(() {
224224
log.info('pushing $location');
225225
return true;
226226
}());
227-
_routeInformationParser
228-
.parseRouteInformationWithDependencies(
227+
final RouteMatchList matches =
228+
await _routeInformationParser.parseRouteInformationWithDependencies(
229229
RouteInformation(location: location, state: extra),
230230
// TODO(chunhtai): avoid accessing the context directly through global key.
231231
// https://github.com/flutter/flutter/issues/99112
232232
_routerDelegate.navigatorKey.currentContext!,
233-
)
234-
.then<void>((RouteMatchList matches) {
235-
_routerDelegate.push(matches);
236-
});
233+
);
234+
235+
return _routerDelegate.push<T>(matches);
237236
}
238237

239238
/// Push a named route onto the page stack w/ optional parameters, e.g.
240239
/// `name='person', params={'fid': 'f2', 'pid': 'p1'}`
241-
void pushNamed(
240+
Future<T?> pushNamed<T extends Object?>(
242241
String name, {
243242
Map<String, String> params = const <String, String>{},
244243
Map<String, dynamic> queryParams = const <String, dynamic>{},
245244
Object? extra,
246245
}) =>
247-
push(
246+
push<T>(
248247
namedLocation(name, params: params, queryParams: queryParams),
249248
extra: extra,
250249
);

packages/go_router/pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name: go_router
22
description: A declarative router for Flutter based on Navigation 2 supporting
33
deep linking, data-driven routes and more
4-
version: 6.4.1
4+
version: 6.5.0
55
repository: https://github.com/flutter/packages/tree/main/packages/go_router
66
issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+go_router%22
77

packages/go_router/test/delegate_test.dart

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ void main() {
141141
reason: 'The last match should have been removed',
142142
);
143143
expect(
144-
(goRouter.routerDelegate.matches.last as ImperativeRouteMatch)
144+
(goRouter.routerDelegate.matches.last as ImperativeRouteMatch<Object?>)
145145
.matches
146146
.uri
147147
.toString(),
@@ -270,7 +270,7 @@ void main() {
270270
reason: 'The last match should have been removed',
271271
);
272272
expect(
273-
(goRouter.routerDelegate.matches.last as ImperativeRouteMatch)
273+
(goRouter.routerDelegate.matches.last as ImperativeRouteMatch<Object?>)
274274
.matches
275275
.uri
276276
.toString(),
@@ -393,7 +393,7 @@ void main() {
393393
reason: 'The last match should have been removed',
394394
);
395395
expect(
396-
(goRouter.routerDelegate.matches.last as ImperativeRouteMatch)
396+
(goRouter.routerDelegate.matches.last as ImperativeRouteMatch<Object?>)
397397
.matches
398398
.uri
399399
.toString(),

packages/go_router/test/go_router_test.dart

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2439,8 +2439,8 @@ void main() {
24392439
expect(router.location, loc);
24402440
expect(matches.matches, hasLength(2));
24412441
expect(find.byType(PersonScreen), findsOneWidget);
2442-
final ImperativeRouteMatch imperativeRouteMatch =
2443-
matches.matches.last as ImperativeRouteMatch;
2442+
final ImperativeRouteMatch<Object?> imperativeRouteMatch =
2443+
matches.matches.last as ImperativeRouteMatch<Object?>;
24442444
expect(imperativeRouteMatch.matches.pathParameters['fid'], fid);
24452445
expect(imperativeRouteMatch.matches.pathParameters['pid'], pid);
24462446
});
@@ -2698,6 +2698,26 @@ void main() {
26982698
expect(router.extra, extra);
26992699
});
27002700

2701+
testWidgets('calls [push] on closest GoRouter and waits for result',
2702+
(WidgetTester tester) async {
2703+
final GoRouterPushSpy router = GoRouterPushSpy(routes: routes);
2704+
await tester.pumpWidget(
2705+
MaterialApp.router(
2706+
routeInformationProvider: router.routeInformationProvider,
2707+
routeInformationParser: router.routeInformationParser,
2708+
routerDelegate: router.routerDelegate,
2709+
title: 'GoRouter Example',
2710+
),
2711+
);
2712+
final String? result = await router.push<String>(
2713+
location,
2714+
extra: extra,
2715+
);
2716+
expect(result, extra);
2717+
expect(router.myLocation, location);
2718+
expect(router.extra, extra);
2719+
});
2720+
27012721
testWidgets('calls [pushNamed] on closest GoRouter',
27022722
(WidgetTester tester) async {
27032723
final GoRouterPushNamedSpy router = GoRouterPushNamedSpy(routes: routes);
@@ -2719,6 +2739,30 @@ void main() {
27192739
expect(router.extra, extra);
27202740
});
27212741

2742+
testWidgets('calls [pushNamed] on closest GoRouter and waits for result',
2743+
(WidgetTester tester) async {
2744+
final GoRouterPushNamedSpy router = GoRouterPushNamedSpy(routes: routes);
2745+
await tester.pumpWidget(
2746+
MaterialApp.router(
2747+
routeInformationProvider: router.routeInformationProvider,
2748+
routeInformationParser: router.routeInformationParser,
2749+
routerDelegate: router.routerDelegate,
2750+
title: 'GoRouter Example',
2751+
),
2752+
);
2753+
final String? result = await router.pushNamed<String>(
2754+
name,
2755+
params: params,
2756+
queryParams: queryParams,
2757+
extra: extra,
2758+
);
2759+
expect(result, extra);
2760+
expect(router.extra, extra);
2761+
expect(router.name, name);
2762+
expect(router.params, params);
2763+
expect(router.queryParams, queryParams);
2764+
});
2765+
27222766
testWidgets('calls [pop] on closest GoRouter', (WidgetTester tester) async {
27232767
final GoRouterPopSpy router = GoRouterPopSpy(routes: routes);
27242768
await tester.pumpWidget(

packages/go_router/test/inherited_test.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,11 +129,12 @@ class MockGoRouter extends GoRouter {
129129
late String latestPushedName;
130130

131131
@override
132-
void pushNamed(String name,
132+
Future<T?> pushNamed<T extends Object?>(String name,
133133
{Map<String, String> params = const <String, String>{},
134134
Map<String, dynamic> queryParams = const <String, dynamic>{},
135135
Object? extra}) {
136136
latestPushedName = name;
137+
return Future<T?>.value();
137138
}
138139

139140
@override

packages/go_router/test/test_helpers.dart

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,9 +95,10 @@ class GoRouterPushSpy extends GoRouter {
9595
Object? extra;
9696

9797
@override
98-
void push(String location, {Object? extra}) {
98+
Future<T?> push<T extends Object?>(String location, {Object? extra}) {
9999
myLocation = location;
100100
this.extra = extra;
101+
return Future<T?>.value(extra as T?);
101102
}
102103
}
103104

@@ -110,7 +111,7 @@ class GoRouterPushNamedSpy extends GoRouter {
110111
Object? extra;
111112

112113
@override
113-
void pushNamed(
114+
Future<T?> pushNamed<T extends Object?>(
114115
String name, {
115116
Map<String, String> params = const <String, String>{},
116117
Map<String, dynamic> queryParams = const <String, dynamic>{},
@@ -120,6 +121,7 @@ class GoRouterPushNamedSpy extends GoRouter {
120121
this.params = params;
121122
this.queryParams = queryParams;
122123
this.extra = extra;
124+
return Future<T?>.value(extra as T?);
123125
}
124126
}
125127

0 commit comments

Comments
 (0)