Revert "add a new PopScope.onPopWithResultInvoke widget to replace Po… (#147598)
diff --git a/dev/integration_tests/flutter_gallery/lib/demo/cupertino/cupertino_navigation_demo.dart b/dev/integration_tests/flutter_gallery/lib/demo/cupertino/cupertino_navigation_demo.dart index a063c7c..a68c2a1 100644 --- a/dev/integration_tests/flutter_gallery/lib/demo/cupertino/cupertino_navigation_demo.dart +++ b/dev/integration_tests/flutter_gallery/lib/demo/cupertino/cupertino_navigation_demo.dart
@@ -48,7 +48,7 @@ @override Widget build(BuildContext context) { - return PopScope<Object?>( + return PopScope( // Prevent swipe popping of this page. Use explicit exit buttons only. canPop: false, child: DefaultTextStyle(
diff --git a/dev/integration_tests/flutter_gallery/lib/demo/material/full_screen_dialog_demo.dart b/dev/integration_tests/flutter_gallery/lib/demo/material/full_screen_dialog_demo.dart index 1402113..81ee4da 100644 --- a/dev/integration_tests/flutter_gallery/lib/demo/material/full_screen_dialog_demo.dart +++ b/dev/integration_tests/flutter_gallery/lib/demo/material/full_screen_dialog_demo.dart
@@ -110,7 +110,7 @@ bool _hasName = false; late String _eventName; - Future<void> _handlePopInvoked(bool didPop, Object? result) async { + Future<void> _handlePopInvoked(bool didPop) async { if (didPop) { return; } @@ -175,7 +175,7 @@ ), body: Form( canPop: !_saveNeeded && !_hasLocation && !_hasName, - onPopInvokedWithResult: _handlePopInvoked, + onPopInvoked: _handlePopInvoked, child: Scrollbar( child: ListView( primary: true,
diff --git a/dev/integration_tests/flutter_gallery/lib/demo/material/text_form_field_demo.dart b/dev/integration_tests/flutter_gallery/lib/demo/material/text_form_field_demo.dart index 47811d8..c6f644e 100644 --- a/dev/integration_tests/flutter_gallery/lib/demo/material/text_form_field_demo.dart +++ b/dev/integration_tests/flutter_gallery/lib/demo/material/text_form_field_demo.dart
@@ -143,7 +143,7 @@ return null; } - Future<void> _handlePopInvoked(bool didPop, Object? result) async { + Future<void> _handlePopInvoked(bool didPop) async { if (didPop) { return; } @@ -192,7 +192,7 @@ key: _formKey, autovalidateMode: _autovalidateMode, canPop: _formKey.currentState == null || !_formWasEdited || _formKey.currentState!.validate(), - onPopInvokedWithResult: _handlePopInvoked, + onPopInvoked: _handlePopInvoked, child: Scrollbar( child: SingleChildScrollView( primary: true,
diff --git a/dev/integration_tests/flutter_gallery/lib/demo/shrine/expanding_bottom_sheet.dart b/dev/integration_tests/flutter_gallery/lib/demo/shrine/expanding_bottom_sheet.dart index 0dfbcbe..5e7a951 100644 --- a/dev/integration_tests/flutter_gallery/lib/demo/shrine/expanding_bottom_sheet.dart +++ b/dev/integration_tests/flutter_gallery/lib/demo/shrine/expanding_bottom_sheet.dart
@@ -355,7 +355,7 @@ // Closes the cart if the cart is open, otherwise exits the app (this should // only be relevant for Android). - void _handlePopInvoked(bool didPop, Object? result) { + void _handlePopInvoked(bool didPop) { if (didPop) { return; } @@ -370,9 +370,9 @@ duration: const Duration(milliseconds: 225), curve: Curves.easeInOut, alignment: FractionalOffset.topLeft, - child: PopScope<Object?>( + child: PopScope( canPop: !_isOpen, - onPopInvokedWithResult: _handlePopInvoked, + onPopInvoked: _handlePopInvoked, child: AnimatedBuilder( animation: widget.hideController, builder: _buildSlideAnimation,
diff --git a/dev/integration_tests/flutter_gallery/lib/gallery/home.dart b/dev/integration_tests/flutter_gallery/lib/gallery/home.dart index fc7c4d8..a6ceab8 100644 --- a/dev/integration_tests/flutter_gallery/lib/gallery/home.dart +++ b/dev/integration_tests/flutter_gallery/lib/gallery/home.dart
@@ -326,9 +326,9 @@ backgroundColor: isDark ? _kFlutterBlue : theme.primaryColor, body: SafeArea( bottom: false, - child: PopScope<Object?>( + child: PopScope( canPop: _category == null, - onPopInvokedWithResult: (bool didPop, Object? result) { + onPopInvoked: (bool didPop) { if (didPop) { return; }
diff --git a/examples/api/lib/widgets/form/form.1.dart b/examples/api/lib/widgets/form/form.1.dart index a768515..e008f5a 100644 --- a/examples/api/lib/widgets/form/form.1.dart +++ b/examples/api/lib/widgets/form/form.1.dart
@@ -111,7 +111,7 @@ const SizedBox(height: 20.0), Form( canPop: !_isDirty, - onPopInvokedWithResult: (bool didPop, Object? result) async { + onPopInvoked: (bool didPop) async { if (didPop) { return; }
diff --git a/examples/api/lib/widgets/pop_scope/pop_scope.0.dart b/examples/api/lib/widgets/pop_scope/pop_scope.0.dart index e2ed446..2400b09 100644 --- a/examples/api/lib/widgets/pop_scope/pop_scope.0.dart +++ b/examples/api/lib/widgets/pop_scope/pop_scope.0.dart
@@ -109,9 +109,9 @@ mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ const Text('Page Two'), - PopScope<Object?>( + PopScope( canPop: false, - onPopInvokedWithResult: (bool didPop, Object? result) async { + onPopInvoked: (bool didPop) async { if (didPop) { return; }
diff --git a/examples/api/lib/widgets/pop_scope/pop_scope.1.dart b/examples/api/lib/widgets/pop_scope/pop_scope.1.dart deleted file mode 100644 index 7a05883..0000000 --- a/examples/api/lib/widgets/pop_scope/pop_scope.1.dart +++ /dev/null
@@ -1,233 +0,0 @@ -// Copyright 2014 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// This sample demonstrates how to use a PopScope to wrap a widget that -// may pop the page with a result. - -import 'package:flutter/material.dart'; - -void main() => runApp(const NavigatorPopHandlerApp()); - -class NavigatorPopHandlerApp extends StatelessWidget { - const NavigatorPopHandlerApp({super.key}); - - @override - Widget build(BuildContext context) { - return MaterialApp( - initialRoute: '/home', - onGenerateRoute: (RouteSettings settings) { - return switch (settings.name) { - '/two' => MaterialPageRoute<FormData>( - builder: (BuildContext context) => const _PageTwo(), - ), - _ => MaterialPageRoute<void>( - builder: (BuildContext context) => const _HomePage(), - ), - }; - }, - ); - } -} - -class _HomePage extends StatefulWidget { - const _HomePage(); - - @override - State<_HomePage> createState() => _HomePageState(); -} - -class _HomePageState extends State<_HomePage> { - FormData? _formData; - - @override - Widget build(BuildContext context) { - return Scaffold( - body: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: <Widget>[ - const Text('Page One'), - if (_formData != null) - Text('Hello ${_formData!.name}, whose favorite food is ${_formData!.favoriteFood}.'), - TextButton( - onPressed: () async { - final FormData formData = - await Navigator.of(context).pushNamed<FormData?>('/two') - ?? const FormData(); - if (formData != _formData) { - setState(() { - _formData = formData; - }); - } - }, - child: const Text('Next page'), - ), - ], - ), - ), - ); - } -} - -class _PopScopeWrapper extends StatelessWidget { - const _PopScopeWrapper({required this.child}); - - final Widget child; - - Future<bool?> _showBackDialog(BuildContext context) { - return showDialog<bool>( - context: context, - builder: (BuildContext context) { - return AlertDialog( - title: const Text('Are you sure?'), - content: const Text( - 'Are you sure you want to leave this page?', - ), - actions: <Widget>[ - TextButton( - style: TextButton.styleFrom( - textStyle: Theme.of(context).textTheme.labelLarge, - ), - child: const Text('Never mind'), - onPressed: () { - Navigator.pop(context, false); - }, - ), - TextButton( - style: TextButton.styleFrom( - textStyle: Theme.of(context).textTheme.labelLarge, - ), - child: const Text('Leave'), - onPressed: () { - Navigator.pop(context, true); - }, - ), - ], - ); - }, - ); - } - - @override - Widget build(BuildContext context) { - return PopScope<FormData>( - canPop: false, - // The result argument contains the pop result that is defined in `_PageTwo`. - onPopInvokedWithResult: (bool didPop, FormData? result) async { - if (didPop) { - return; - } - final bool shouldPop = await _showBackDialog(context) ?? false; - if (context.mounted && shouldPop) { - Navigator.pop(context, result); - } - }, - child: child, - ); - } -} - -// This is a PopScope wrapper over _PageTwoBody -class _PageTwo extends StatelessWidget { - const _PageTwo(); - - @override - Widget build(BuildContext context) { - return const _PopScopeWrapper( - child: _PageTwoBody(), - ); - } - -} - -class _PageTwoBody extends StatefulWidget { - const _PageTwoBody(); - - @override - State<_PageTwoBody> createState() => _PageTwoBodyState(); -} - -class _PageTwoBodyState extends State<_PageTwoBody> { - FormData _formData = const FormData(); - - @override - Widget build(BuildContext context) { - return Scaffold( - body: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: <Widget>[ - const Text('Page Two'), - Form( - child: Column( - children: <Widget>[ - TextFormField( - decoration: const InputDecoration( - hintText: 'Enter your name.', - ), - onChanged: (String value) { - _formData = _formData.copyWith( - name: value, - ); - }, - ), - TextFormField( - decoration: const InputDecoration( - hintText: 'Enter your favorite food.', - ), - onChanged: (String value) { - _formData = _formData.copyWith( - favoriteFood: value, - ); - }, - ), - ], - ), - ), - TextButton( - onPressed: () async { - Navigator.maybePop(context, _formData); - }, - child: const Text('Go back'), - ), - ], - ), - ), - ); - } -} - -@immutable -class FormData { - const FormData({ - this.name = '', - this.favoriteFood = '', - }); - - final String name; - final String favoriteFood; - - FormData copyWith({String? name, String? favoriteFood}) { - return FormData( - name: name ?? this.name, - favoriteFood: favoriteFood ?? this.favoriteFood, - ); - } - - @override - bool operator ==(Object other) { - if (identical(this, other)) { - return true; - } - if (other.runtimeType != runtimeType) { - return false; - } - return other is FormData - && other.name == name - && other.favoriteFood == favoriteFood; - } - - @override - int get hashCode => Object.hash(name, favoriteFood); -}
diff --git a/examples/api/test/widgets/pop_scope/pop_scope.1_test.dart b/examples/api/test/widgets/pop_scope/pop_scope.1_test.dart deleted file mode 100644 index 14266af..0000000 --- a/examples/api/test/widgets/pop_scope/pop_scope.1_test.dart +++ /dev/null
@@ -1,67 +0,0 @@ -// Copyright 2014 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:flutter/material.dart'; -import 'package:flutter_api_samples/widgets/pop_scope/pop_scope.1.dart' as example; -import 'package:flutter_test/flutter_test.dart'; - -import '../navigator_utils.dart'; - -void main() { - testWidgets('Can choose to stay on page', (WidgetTester tester) async { - await tester.pumpWidget( - const example.NavigatorPopHandlerApp(), - ); - - expect(find.text('Page One'), findsOneWidget); - - await tester.tap(find.text('Next page')); - await tester.pumpAndSettle(); - expect(find.text('Page One'), findsNothing); - expect(find.text('Page Two'), findsOneWidget); - - await simulateSystemBack(); - await tester.pumpAndSettle(); - expect(find.text('Page One'), findsNothing); - expect(find.text('Page Two'), findsOneWidget); - expect(find.text('Are you sure?'), findsOneWidget); - - await tester.tap(find.text('Never mind')); - await tester.pumpAndSettle(); - expect(find.text('Page One'), findsNothing); - expect(find.text('Page Two'), findsOneWidget); - }); - - testWidgets('Can choose to go back with pop result', (WidgetTester tester) async { - await tester.pumpWidget( - const example.NavigatorPopHandlerApp(), - ); - - expect(find.text('Page One'), findsOneWidget); - expect(find.text('Page Two'), findsNothing); - - await tester.tap(find.text('Next page')); - await tester.pumpAndSettle(); - expect(find.text('Page One'), findsNothing); - expect(find.text('Page Two'), findsOneWidget); - - await tester.enterText(find.byType(TextFormField).first, 'John'); - await tester.pumpAndSettle(); - await tester.enterText(find.byType(TextFormField).last, 'Apple'); - await tester.pumpAndSettle(); - - await tester.tap(find.text('Go back')); - await tester.pumpAndSettle(); - expect(find.text('Page One'), findsNothing); - expect(find.text('Page Two'), findsOneWidget); - expect(find.text('Are you sure?'), findsOneWidget); - - await tester.tap(find.text('Leave')); - await tester.pumpAndSettle(); - expect(find.text('Page One'), findsOneWidget); - expect(find.text('Page Two'), findsNothing); - expect(find.text('Are you sure?'), findsNothing); - expect(find.text('Hello John, whose favorite food is Apple.'), findsOneWidget); - }); -}
diff --git a/packages/flutter/lib/src/material/about.dart b/packages/flutter/lib/src/material/about.dart index 67c1a1a..a61f3a0 100644 --- a/packages/flutter/lib/src/material/about.dart +++ b/packages/flutter/lib/src/material/about.dart
@@ -1230,9 +1230,9 @@ } MaterialPageRoute<void> _detailPageRoute(Object? arguments) { - return MaterialPageRoute<void>(builder: (BuildContext context) { - return PopScope<void>( - onPopInvokedWithResult: (bool didPop, void result) { + return MaterialPageRoute<dynamic>(builder: (BuildContext context) { + return PopScope( + onPopInvoked: (bool didPop) { // No need for setState() as rebuild happens on navigation pop. focus = _Focus.master; },
diff --git a/packages/flutter/lib/src/widgets/form.dart b/packages/flutter/lib/src/widgets/form.dart index f17529b..5277e8a 100644 --- a/packages/flutter/lib/src/widgets/form.dart +++ b/packages/flutter/lib/src/widgets/form.dart
@@ -55,22 +55,16 @@ super.key, required this.child, this.canPop, - @Deprecated( - 'Use onPopInvokedWithResult instead. ' - 'This feature was deprecated after v3.22.0-12.0.pre.', - ) this.onPopInvoked, - this.onPopInvokedWithResult, @Deprecated( - 'Use canPop and/or onPopInvokedWithResult instead. ' + 'Use canPop and/or onPopInvoked instead. ' 'This feature was deprecated after v3.12.0-1.0.pre.', ) this.onWillPop, this.onChanged, AutovalidateMode? autovalidateMode, }) : autovalidateMode = autovalidateMode ?? AutovalidateMode.disabled, - assert(onPopInvokedWithResult == null || onPopInvoked == null, 'onPopInvoked is deprecated; use onPopInvokedWithResult'), - assert(((onPopInvokedWithResult ?? onPopInvoked ?? canPop) == null) || onWillPop == null, 'onWillPop is deprecated; use canPop and/or onPopInvokedWithResult.'); + assert((onPopInvoked == null && canPop == null) || onWillPop == null, 'onWillPop is deprecated; use canPop and/or onPopInvoked.'); /// Returns the [FormState] of the closest [Form] widget which encloses the /// given context, or null if none is found. @@ -150,7 +144,7 @@ /// * [WillPopScope], another widget that provides a way to intercept the /// back button. @Deprecated( - 'Use canPop and/or onPopInvokedWithResult instead. ' + 'Use canPop and/or onPopInvoked instead. ' 'This feature was deprecated after v3.12.0-1.0.pre.', ) final WillPopCallback? onWillPop; @@ -166,19 +160,12 @@ /// /// See also: /// - /// * [onPopInvokedWithResult], which also comes from [PopScope] and is often used in + /// * [onPopInvoked], which also comes from [PopScope] and is often used in /// conjunction with this parameter. /// * [PopScope.canPop], which is what [Form] delegates to internally. final bool? canPop; /// {@macro flutter.widgets.navigator.onPopInvoked} - @Deprecated( - 'Use onPopInvokedWithResult instead. ' - 'This feature was deprecated after v3.22.0-12.0.pre.', - ) - final PopInvokedCallback? onPopInvoked; - - /// {@macro flutter.widgets.navigator.onPopInvoked} /// /// {@tool dartpad} /// This sample demonstrates how to use this parameter to show a confirmation @@ -191,8 +178,8 @@ /// /// * [canPop], which also comes from [PopScope] and is often used in /// conjunction with this parameter. - /// * [PopScope.onPopInvokedWithResult], which is what [Form] delegates to internally. - final PopInvokedWithResultCallback<Object?>? onPopInvokedWithResult; + /// * [PopScope.onPopInvoked], which is what [Form] delegates to internally. + final PopInvokedCallback? onPopInvoked; /// Called when one of the form fields changes. /// @@ -206,14 +193,6 @@ /// {@macro flutter.widgets.FormField.autovalidateMode} final AutovalidateMode autovalidateMode; - void _callPopInvoked(bool didPop, Object? result) { - if (onPopInvokedWithResult != null) { - onPopInvokedWithResult!(didPop, result); - return; - } - onPopInvoked?.call(didPop); - } - @override FormState createState() => FormState(); } @@ -279,10 +258,10 @@ break; } - if (widget.canPop != null || (widget.onPopInvokedWithResult ?? widget.onPopInvoked) != null) { - return PopScope<Object?>( + if (widget.canPop != null || widget.onPopInvoked != null) { + return PopScope( canPop: widget.canPop ?? true, - onPopInvokedWithResult: widget._callPopInvoked, + onPopInvoked: widget.onPopInvoked, child: _FormScope( formState: this, generation: _generation,
diff --git a/packages/flutter/lib/src/widgets/navigator.dart b/packages/flutter/lib/src/widgets/navigator.dart index bed9a85..662f12d 100644 --- a/packages/flutter/lib/src/widgets/navigator.dart +++ b/packages/flutter/lib/src/widgets/navigator.dart
@@ -355,7 +355,7 @@ /// will still be called. The `didPop` parameter indicates whether or not the /// back navigation actually happened successfully. /// {@endtemplate} - void onPopInvoked(bool didPop, T? result) {} + void onPopInvoked(bool didPop) {} /// Whether calling [didPop] would return false. bool get willHandlePopInternally => false; @@ -3109,7 +3109,7 @@ assert(isPresent); pendingResult = result; currentState = _RouteLifecycle.pop; - route.onPopInvoked(true, result); + route.onPopInvoked(true); } bool _reportRemovalToObserver = true; @@ -5239,7 +5239,7 @@ pop(result); return true; case RoutePopDisposition.doNotPop: - lastEntry.route.onPopInvoked(false, result); + lastEntry.route.onPopInvoked(false); return true; } } @@ -5282,7 +5282,7 @@ assert(entry.route._popCompleter.isCompleted); entry.currentState = _RouteLifecycle.pop; } - entry.route.onPopInvoked(true, result); + entry.route.onPopInvoked(true); } else { entry.pop<T>(result); assert (entry.currentState == _RouteLifecycle.pop);
diff --git a/packages/flutter/lib/src/widgets/navigator_pop_handler.dart b/packages/flutter/lib/src/widgets/navigator_pop_handler.dart index ea2be66..203a85b 100644 --- a/packages/flutter/lib/src/widgets/navigator_pop_handler.dart +++ b/packages/flutter/lib/src/widgets/navigator_pop_handler.dart
@@ -81,9 +81,9 @@ 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( canPop: !widget.enabled || _canPop, - onPopInvokedWithResult: (bool didPop, Object? result) { + onPopInvoked: (bool didPop) { if (didPop) { return; }
diff --git a/packages/flutter/lib/src/widgets/pop_scope.dart b/packages/flutter/lib/src/widgets/pop_scope.dart index 8bbac4c..c8b31f6 100644 --- a/packages/flutter/lib/src/widgets/pop_scope.dart +++ b/packages/flutter/lib/src/widgets/pop_scope.dart
@@ -8,29 +8,12 @@ import 'navigator.dart'; import 'routes.dart'; -/// A callback type for informing that a navigation pop has been invoked, -/// whether or not it was handled successfully. -/// -/// Accepts a didPop boolean indicating whether or not back navigation -/// succeeded. -@Deprecated( - 'Use PopWithResultInvokedCallback instead. ' - 'This feature was deprecated after v3.22.0-12.0.pre.', -) -typedef PopInvokedCallback = void Function(bool didPop); - /// Manages back navigation gestures. /// -/// The generic type should match or be a supertype of the generic type of the -/// enclosing [Route]. For example, if the enclosing Route is a -/// `MaterialPageRoute<int>`, you can define [PopScope] with `int` or any -/// supertype of `int`. -/// /// The [canPop] parameter disables back gestures when set to `false`. /// -/// The [onPopInvokedWithResult] parameter reports when pop navigation was attempted, and -/// `didPop` indicates whether or not the navigation was successful. The -/// `result` contains the pop result. +/// The [onPopInvoked] parameter reports when pop navigation was attempted, and +/// `didPop` indicates whether or not the navigation was successful. /// /// Android has a system back gesture that is a swipe inward from near the edge /// of the screen. It is recognized by Android before being passed to Flutter. @@ -39,15 +22,15 @@ /// back gesture. /// /// If [canPop] is false, then a system back gesture will not pop the route off -/// of the enclosing [Navigator]. [onPopInvokedWithResult] will still be called, and +/// of the enclosing [Navigator]. [onPopInvoked] will still be called, and /// `didPop` will be `false`. On iOS when using [CupertinoRouteTransitionMixin] /// with [canPop] set to false, no gesture will be detected at all, so -/// [onPopInvokedWithResult] will not be called. Programmatically attempting pop -/// navigation will also result in a call to [onPopInvokedWithResult], with `didPop` +/// [onPopInvoked] will not be called. Programmatically attempting pop +/// navigation will also result in a call to [onPopInvoked], with `didPop` /// indicating success or failure. /// /// If [canPop] is true, then a system back gesture will cause the enclosing -/// [Navigator] to receive a pop as usual. [onPopInvokedWithResult] will be called with +/// [Navigator] to receive a pop as usual. [onPopInvoked] will be called with /// `didPop` as true, unless the pop failed for reasons unrelated to /// [PopScope], in which case it will be false. /// @@ -58,42 +41,30 @@ /// ** See code in examples/api/lib/widgets/pop_scope/pop_scope.0.dart ** /// {@end-tool} /// -/// {@tool dartpad} -/// This sample demonstrates showing how to use PopScope to wrap widget that -/// may pop the page with a result. -/// -/// ** See code in examples/api/lib/widgets/pop_scope/pop_scope.1.dart ** -/// {@end-tool} -/// /// See also: /// /// * [NavigatorPopHandler], which is a less verbose way to handle system back /// gestures in simple cases of nested [Navigator]s. -/// * [Form.canPop] and [Form.onPopInvokedWithResult], which can be used to handle system +/// * [Form.canPop] and [Form.onPopInvoked], which can be used to handle system /// back gestures in the case of a form with unsaved data. /// * [ModalRoute.registerPopEntry] and [ModalRoute.unregisterPopEntry], /// which this widget uses to integrate with Flutter's navigation system. -class PopScope<T> extends StatefulWidget { +class PopScope extends StatefulWidget { /// Creates a widget that registers a callback to veto attempts by the user to /// dismiss the enclosing [ModalRoute]. const PopScope({ super.key, required this.child, this.canPop = true, - this.onPopInvokedWithResult, - @Deprecated( - 'Use onPopInvokedWithResult instead. ' - 'This feature was deprecated after v3.22.0-12.0.pre.', - ) this.onPopInvoked, - }) : assert(onPopInvokedWithResult == null || onPopInvoked == null, 'onPopInvoked is deprecated, use onPopInvokedWithResult'); + }); /// The widget below this widget in the tree. /// /// {@macro flutter.widgets.ProxyWidget.child} final Widget child; - /// {@template flutter.widgets.PopScope.onPopInvokedWithResult} + /// {@template flutter.widgets.PopScope.onPopInvoked} /// Called after a route pop was handled. /// {@endtemplate} /// @@ -107,38 +78,11 @@ /// indicates whether or not the back navigation actually happened /// successfully. /// - /// The `result` contains the pop result. - /// /// See also: /// /// * [Route.onPopInvoked], which is similar. - final PopInvokedWithResultCallback<T>? onPopInvokedWithResult; - - /// Called after a route pop was handled. - /// - /// It's not possible to prevent the pop from happening at the time that this - /// method is called; the pop has already happened. Use [canPop] to - /// disable pops in advance. - /// - /// This will still be called even when the pop is canceled. A pop is canceled - /// when the relevant [Route.popDisposition] returns false, such as when - /// [canPop] is set to false on a [PopScope]. The `didPop` parameter - /// indicates whether or not the back navigation actually happened - /// successfully. - @Deprecated( - 'Use onPopInvokedWithResult instead. ' - 'This feature was deprecated after v3.22.0-12.0.pre.', - ) final PopInvokedCallback? onPopInvoked; - void _callPopInvoked(bool didPop, T? result) { - if (onPopInvokedWithResult != null) { - onPopInvokedWithResult!(didPop, result); - return; - } - onPopInvoked?.call(didPop); - } - /// {@template flutter.widgets.PopScope.canPop} /// When false, blocks the current route from being popped. /// @@ -155,16 +99,14 @@ final bool canPop; @override - State<PopScope<T>> createState() => _PopScopeState<T>(); + State<PopScope> createState() => _PopScopeState(); } -class _PopScopeState<T> extends State<PopScope<T>> implements PopEntry<T> { +class _PopScopeState extends State<PopScope> implements PopEntry { ModalRoute<dynamic>? _route; @override - void onPopInvoked(bool didPop, T? result) { - widget._callPopInvoked(didPop, result); - } + PopInvokedCallback? get onPopInvoked => widget.onPopInvoked; @override late final ValueNotifier<bool> canPopNotifier; @@ -187,7 +129,7 @@ } @override - void didUpdateWidget(PopScope<T> oldWidget) { + void didUpdateWidget(PopScope oldWidget) { super.didUpdateWidget(oldWidget); canPopNotifier.value = widget.canPop; }
diff --git a/packages/flutter/lib/src/widgets/routes.dart b/packages/flutter/lib/src/widgets/routes.dart index e6232df..1843a5c 100644 --- a/packages/flutter/lib/src/widgets/routes.dart +++ b/packages/flutter/lib/src/widgets/routes.dart
@@ -1669,9 +1669,7 @@ final List<WillPopCallback> _willPopCallbacks = <WillPopCallback>[]; - // Holding as Object? instead of T so that PopScope in this route can be - // declared with any supertype of T. - final Set<PopEntry<Object?>> _popEntries = <PopEntry<Object?>>{}; + final Set<PopEntry> _popEntries = <PopEntry>{}; /// Returns [RoutePopDisposition.doNotPop] if any of callbacks added with /// [addScopedWillPopCallback] returns either false or null. If they all @@ -1719,14 +1717,14 @@ /// /// See also: /// - /// * [Form], which provides an `onPopInvokedWithResult` callback that is similar. + /// * [Form], which provides an `onPopInvoked` callback that is similar. /// * [registerPopEntry], which adds a [PopEntry] to the list this method /// checks. /// * [unregisterPopEntry], which removes a [PopEntry] from the list this /// method checks. @override RoutePopDisposition get popDisposition { - for (final PopEntry<Object?> popEntry in _popEntries) { + for (final PopEntry popEntry in _popEntries) { if (!popEntry.canPopNotifier.value) { return RoutePopDisposition.doNotPop; } @@ -1736,9 +1734,9 @@ } @override - void onPopInvoked(bool didPop, T? result) { - for (final PopEntry<Object?> popEntry in _popEntries) { - popEntry.onPopInvoked(didPop, result); + void onPopInvoked(bool didPop) { + for (final PopEntry popEntry in _popEntries) { + popEntry.onPopInvoked?.call(didPop); } } @@ -1795,7 +1793,7 @@ /// See also: /// /// * [unregisterPopEntry], which performs the opposite operation. - void registerPopEntry(PopEntry<Object?> popEntry) { + void registerPopEntry(PopEntry popEntry) { _popEntries.add(popEntry); popEntry.canPopNotifier.addListener(_handlePopEntryChange); _handlePopEntryChange(); @@ -1806,7 +1804,7 @@ /// See also: /// /// * [registerPopEntry], which performs the opposite operation. - void unregisterPopEntry(PopEntry<Object?> popEntry) { + void unregisterPopEntry(PopEntry popEntry) { _popEntries.remove(popEntry); popEntry.canPopNotifier.removeListener(_handlePopEntryChange); _handlePopEntryChange(); @@ -2415,9 +2413,7 @@ /// /// Accepts a didPop boolean indicating whether or not back navigation /// succeeded. -/// -/// The `result` contains the pop result. -typedef PopInvokedWithResultCallback<T> = void Function(bool didPop, T? result); +typedef PopInvokedCallback = void Function(bool didPop); /// Allows listening to and preventing pops. /// @@ -2429,9 +2425,9 @@ /// * [PopScope], which provides similar functionality in a widget. /// * [ModalRoute.registerPopEntry], which unregisters instances of this. /// * [ModalRoute.unregisterPopEntry], which unregisters instances of this. -abstract class PopEntry<T> { - /// {@macro flutter.widgets.PopScope.onPopInvokedWithResult} - void onPopInvoked(bool didPop, T? result); +abstract class PopEntry { + /// {@macro flutter.widgets.PopScope.onPopInvoked} + PopInvokedCallback? get onPopInvoked; /// {@macro flutter.widgets.PopScope.canPop} ValueListenable<bool> get canPopNotifier;
diff --git a/packages/flutter/test/cupertino/tab_test.dart b/packages/flutter/test/cupertino/tab_test.dart index 71887775..5acf173 100644 --- a/packages/flutter/test/cupertino/tab_test.dart +++ b/packages/flutter/test/cupertino/tab_test.dart
@@ -305,7 +305,7 @@ BottomNavigationBarItem(label: '', icon: Text('2')) ], ), - tabBuilder: (_, int i) => PopScope<Object?>( + tabBuilder: (_, int i) => PopScope( canPop: false, child: CupertinoTabView( navigatorKey: key,
diff --git a/packages/flutter/test/widgets/navigator_test.dart b/packages/flutter/test/widgets/navigator_test.dart index 3407f61..0bb9ba9 100644 --- a/packages/flutter/test/widgets/navigator_test.dart +++ b/packages/flutter/test/widgets/navigator_test.dart
@@ -2989,7 +2989,7 @@ const List<Page<void>> myPages = <Page<void>>[ MaterialPage<void>(child: Text('page1')), MaterialPage<void>( - child: PopScope<void>( + child: PopScope( canPop: false, child: Text('page2'), ), @@ -4908,9 +4908,9 @@ home: StatefulBuilder( builder: (BuildContext context, StateSetter setState) { builderSetState = setState; - return PopScope<Object?>( + return PopScope( canPop: canPop(), - onPopInvokedWithResult: (bool success, Object? result) { + onPopInvoked: (bool success) { if (success || pages.last == _Page.noPop) { return; } @@ -5024,9 +5024,9 @@ MaterialApp( home: StatefulBuilder( builder: (BuildContext context, StateSetter setState) { - return PopScope<Object?>( + return PopScope( canPop: canPop(), - onPopInvokedWithResult: (bool success, Object? result) { + onPopInvoked: (bool success) { if (success || pages.last == _Page.noPop) { return; } @@ -5117,9 +5117,9 @@ MaterialApp( home: StatefulBuilder( builder: (BuildContext context, StateSetter setState) { - return PopScope<Object?>( + return PopScope( canPop: canPop(), - onPopInvokedWithResult: (bool success, Object? result) { + onPopInvoked: (bool success) { if (success || pages.last == _PageWithYesPop.noPop) { return; } @@ -5189,7 +5189,7 @@ child: _LinksPage( title: 'Can pop page', canPop: true, - onPopInvoked: (bool didPop, void result) { + onPopInvoked: (bool didPop) { onPopInvokedCallCount += 1; }, ), @@ -5556,7 +5556,7 @@ final bool? canPop; final VoidCallback? onBack; final String title; - final PopInvokedWithResultCallback<Object?>? onPopInvoked; + final PopInvokedCallback? onPopInvoked; @override Widget build(BuildContext context) { @@ -5575,9 +5575,9 @@ child: const Text('Go back'), ), if (canPop != null) - PopScope<void>( + PopScope( canPop: canPop!, - onPopInvokedWithResult: onPopInvoked, + onPopInvoked: onPopInvoked, child: const SizedBox.shrink(), ), ],
diff --git a/packages/flutter/test/widgets/pop_scope_test.dart b/packages/flutter/test/widgets/pop_scope_test.dart index 8e6d104..116951c 100644 --- a/packages/flutter/test/widgets/pop_scope_test.dart +++ b/packages/flutter/test/widgets/pop_scope_test.dart
@@ -47,7 +47,7 @@ builder: (BuildContext buildContext, StateSetter stateSetter) { context = buildContext; setState = stateSetter; - return PopScope<Object?>( + return PopScope( canPop: canPop, child: const Center( child: Column( @@ -79,94 +79,6 @@ variant: TargetPlatformVariant.all(), ); - testWidgets('pop scope can receive result', (WidgetTester tester) async { - Object? receivedResult; - final Object poppedResult = Object(); - final GlobalKey<NavigatorState> nav = GlobalKey<NavigatorState>(); - await tester.pumpWidget( - MaterialApp( - initialRoute: '/', - navigatorKey: nav, - home: Scaffold( - body: PopScope<Object?>( - canPop: false, - onPopInvokedWithResult: (bool didPop, Object? result) { - receivedResult = result; - }, - child: const Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: <Widget>[ - Text('Home/PopScope Page'), - ], - ), - ), - ), - ), - ), - ); - - nav.currentState!.maybePop(poppedResult); - await tester.pumpAndSettle(); - expect(receivedResult, poppedResult); - }, - variant: TargetPlatformVariant.all(), - ); - - testWidgets('pop scope can have Object? generic type while route has stricter generic type', (WidgetTester tester) async { - Object? receivedResult; - const int poppedResult = 13; - final GlobalKey<NavigatorState> nav = GlobalKey<NavigatorState>(); - await tester.pumpWidget( - MaterialApp( - initialRoute: '/', - navigatorKey: nav, - home: Scaffold( - body: PopScope<Object?>( - canPop: false, - onPopInvokedWithResult: (bool didPop, Object? result) { - receivedResult = result; - }, - child: const Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: <Widget>[ - Text('Home/PopScope Page'), - ], - ), - ), - ), - ), - ), - ); - - nav.currentState!.push( - MaterialPageRoute<int>( - builder: (BuildContext context) { - return Scaffold( - body: PopScope<Object?>( - canPop: false, - onPopInvokedWithResult: (bool didPop, Object? result) { - receivedResult = result; - }, - child: const Center( - child: Text('new page'), - ), - ), - ); - }, - ), - ); - await tester.pumpAndSettle(); - expect(find.text('new page'), findsOneWidget); - - nav.currentState!.maybePop(poppedResult); - await tester.pumpAndSettle(); - expect(receivedResult, poppedResult); - }, - variant: TargetPlatformVariant.all(), - ); - testWidgets('toggling canPop on secondary route allows/prevents backs', (WidgetTester tester) async { final GlobalKey<NavigatorState> nav = GlobalKey<NavigatorState>(); bool canPop = true; @@ -203,9 +115,9 @@ builder: (BuildContext context, StateSetter stateSetter) { oneContext = context; setState = stateSetter; - return PopScope<Object?>( + return PopScope( canPop: canPop, - onPopInvokedWithResult: (bool didPop, Object? result) { + onPopInvoked: (bool didPop) { lastPopSuccess = didPop; }, child: const Center( @@ -359,7 +271,7 @@ if (!usePopScope) { return child; } - return const PopScope<Object?>( + return const PopScope( canPop: false, child: child, ); @@ -402,12 +314,12 @@ return Column( children: <Widget>[ if (usePopScope1) - const PopScope<Object?>( + const PopScope( canPop: false, child: Text('hello'), ), if (usePopScope2) - const PopScope<Object?>( + const PopScope( canPop: false, child: Text('hello'), ),