NavigatorPopHandler.onPopWithResult (#155618)
NavigatorPopHandler now includes the return value from Route. Recently some navigation infrastructure was updated to support passing through these return values, but NavigatorPopHandler was missed until now.
This commit is contained in:
parent
fc865ed9e7
commit
22635e19c1
@ -12,6 +12,9 @@ import 'pop_scope.dart';
|
||||
/// Typically wraps a nested [Navigator] widget and allows it to handle system
|
||||
/// back gestures in the [onPop] callback.
|
||||
///
|
||||
/// The type parameter `<T>` indicates the type of the [Route]'s return value,
|
||||
/// which is passed to [onPopWithResult.
|
||||
///
|
||||
/// {@tool dartpad}
|
||||
/// This sample demonstrates how to use this widget to properly handle system
|
||||
/// back gestures when using nested [Navigator]s.
|
||||
@ -33,14 +36,20 @@ import 'pop_scope.dart';
|
||||
/// handle pops.
|
||||
/// * [NavigationNotification], which indicates whether a [Navigator] in a
|
||||
/// subtree can handle pops.
|
||||
class NavigatorPopHandler extends StatefulWidget {
|
||||
@optionalTypeArgs
|
||||
class NavigatorPopHandler<T> extends StatefulWidget {
|
||||
/// Creates an instance of [NavigatorPopHandler].
|
||||
const NavigatorPopHandler({
|
||||
super.key,
|
||||
@Deprecated(
|
||||
'Use onPopWithResult instead. '
|
||||
'This feature was deprecated after v3.26.0-0.1.pre.',
|
||||
)
|
||||
this.onPop,
|
||||
this.onPopWithResult,
|
||||
this.enabled = true,
|
||||
required this.child,
|
||||
});
|
||||
}) : assert(onPop == null || onPopWithResult == null);
|
||||
|
||||
/// The widget to place below this in the widget tree.
|
||||
///
|
||||
@ -68,26 +77,43 @@ class NavigatorPopHandler extends StatefulWidget {
|
||||
///
|
||||
/// Typically this is used to pop the [Navigator] in [child]. See the sample
|
||||
/// code on [NavigatorPopHandler] for a full example of this.
|
||||
@Deprecated(
|
||||
'Use onPopWithResult instead. '
|
||||
'This feature was deprecated after v3.26.0-0.1.pre.',
|
||||
)
|
||||
final VoidCallback? onPop;
|
||||
|
||||
/// Called when a handleable pop event happens.
|
||||
///
|
||||
/// For example, a pop is handleable when a [Navigator] in [child] has
|
||||
/// multiple routes on its stack. It's not handleable when it has only a
|
||||
/// single route, and so [onPop] will not be called.
|
||||
///
|
||||
/// Typically this is used to pop the [Navigator] in [child]. See the sample
|
||||
/// code on [NavigatorPopHandler] for a full example of this.
|
||||
///
|
||||
/// The passed `result` is the result of the popped [Route].
|
||||
final PopResultCallback<T>? onPopWithResult;
|
||||
|
||||
@override
|
||||
State<NavigatorPopHandler> createState() => _NavigatorPopHandlerState();
|
||||
State<NavigatorPopHandler<T>> createState() => _NavigatorPopHandlerState<T>();
|
||||
}
|
||||
|
||||
class _NavigatorPopHandlerState extends State<NavigatorPopHandler> {
|
||||
class _NavigatorPopHandlerState<T> extends State<NavigatorPopHandler<T>> {
|
||||
bool _canPop = true;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// When the widget subtree indicates it can handle a pop, disable popping
|
||||
// here, so that it can be manually handled in canPop.
|
||||
return PopScope<Object?>(
|
||||
return PopScope<T>(
|
||||
canPop: !widget.enabled || _canPop,
|
||||
onPopInvokedWithResult: (bool didPop, Object? result) {
|
||||
onPopInvokedWithResult: (bool didPop, T? result) {
|
||||
if (didPop) {
|
||||
return;
|
||||
}
|
||||
widget.onPop?.call();
|
||||
widget.onPopWithResult?.call(result);
|
||||
},
|
||||
// Listen to changes in the navigation stack in the widget subtree.
|
||||
child: NotificationListener<NavigationNotification>(
|
||||
@ -108,3 +134,6 @@ class _NavigatorPopHandlerState extends State<NavigatorPopHandler> {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// A signature for a function that is passed the result of a [Route].
|
||||
typedef PopResultCallback<T> = void Function(T? result);
|
||||
|
@ -5404,6 +5404,78 @@ void main() {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
testWidgets('NavigatorPopHandler.onPopWithResult', (WidgetTester tester) async {
|
||||
final GlobalKey<NavigatorState> nav = GlobalKey<NavigatorState>();
|
||||
final GlobalKey<NavigatorState> nestedNav = GlobalKey<NavigatorState>();
|
||||
const String result = 'i am a result';
|
||||
final List<String?> results = <String>[];
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
navigatorKey: nav,
|
||||
initialRoute: '/',
|
||||
routes: <String, WidgetBuilder>{
|
||||
'/': (BuildContext context) => _LinksPage(
|
||||
title: 'Home page',
|
||||
buttons: <Widget>[
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pushNamed('/one');
|
||||
},
|
||||
child: const Text('Go to one'),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pushNamed('/nested');
|
||||
},
|
||||
child: const Text('Go to nested'),
|
||||
),
|
||||
],
|
||||
),
|
||||
'/one': (BuildContext context) => _LinksPage(
|
||||
title: 'Page one',
|
||||
buttons: <Widget>[
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pushNamed('/one/one');
|
||||
},
|
||||
child: const Text('Go to one/one'),
|
||||
),
|
||||
],
|
||||
),
|
||||
'/nested': (BuildContext context) => _NestedNavigatorsPage(
|
||||
navigatorKey: nestedNav,
|
||||
onPopWithResult: (String? result) {
|
||||
results.add(result);
|
||||
},
|
||||
),
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
expect(find.text('Home page'), findsOneWidget);
|
||||
|
||||
await tester.tap(find.text('Go to nested'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('Nested - home'), findsOneWidget);
|
||||
|
||||
await tester.tap(find.text('Go to nested/one'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('Nested - page one'), findsOneWidget);
|
||||
expect(results, isEmpty);
|
||||
|
||||
// Pop the root Navigator, despite being on a route in the nested
|
||||
// Navigator. This is to trigger NavigatorPopHandler.onPopWithResult with
|
||||
// a the given result.
|
||||
await nav.currentState?.maybePop(result);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('Nested - home'), findsOneWidget);
|
||||
expect(results, hasLength(1));
|
||||
expect(results.first, result);
|
||||
});
|
||||
}
|
||||
|
||||
typedef AnnouncementCallBack = void Function(Route<dynamic>?);
|
||||
@ -5783,6 +5855,7 @@ class _NestedNavigatorsPage extends StatefulWidget {
|
||||
const _NestedNavigatorsPage({
|
||||
this.popScopePageEnabled,
|
||||
this.navigatorKey,
|
||||
this.onPopWithResult,
|
||||
});
|
||||
|
||||
/// Whether the PopScope on the /popscope page is enabled.
|
||||
@ -5792,6 +5865,8 @@ class _NestedNavigatorsPage extends StatefulWidget {
|
||||
|
||||
final GlobalKey<NavigatorState>? navigatorKey;
|
||||
|
||||
final PopResultCallback<String>? onPopWithResult;
|
||||
|
||||
@override
|
||||
State<_NestedNavigatorsPage> createState() => _NestedNavigatorsPageState();
|
||||
}
|
||||
@ -5808,12 +5883,13 @@ class _NestedNavigatorsPageState extends State<_NestedNavigatorsPage> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final BuildContext rootContext = context;
|
||||
return NavigatorPopHandler(
|
||||
onPop: () {
|
||||
return NavigatorPopHandler<String>(
|
||||
onPopWithResult: (String? result) {
|
||||
widget.onPopWithResult?.call(result);
|
||||
if (widget.popScopePageEnabled == false) {
|
||||
return;
|
||||
}
|
||||
_navigatorKey.currentState!.pop();
|
||||
_navigatorKey.currentState!.pop(result);
|
||||
},
|
||||
child: Navigator(
|
||||
key: _navigatorKey,
|
||||
|
Loading…
x
Reference in New Issue
Block a user