From bb4c9b381e3cc23510d1c03cff77021db0b4b54c Mon Sep 17 00:00:00 2001 From: "auto-submit[bot]" <98614782+auto-submit[bot]@users.noreply.github.com> Date: Thu, 18 Apr 2024 22:36:26 +0000 Subject: [PATCH] Reverts "Add generic type for result in PopScope (#139164)" (#147015) Reverts: flutter/flutter#139164 Initiated by: chunhtai Reason for reverting: hard breaking change Original PR Author: chunhtai Reviewed By: {justinmc} This change reverts the following previous change: Adds a generic type and pop result to popscope and its friend. The use cases are to be able to capture the result when the pop is called. migration guide: https://github.com/flutter/website/pull/9872 --- .../cupertino/cupertino_navigation_demo.dart | 2 +- .../material/full_screen_dialog_demo.dart | 2 +- .../demo/material/text_form_field_demo.dart | 2 +- .../demo/shrine/expanding_bottom_sheet.dart | 4 +- .../flutter_gallery/lib/gallery/home.dart | 4 +- examples/api/lib/widgets/form/form.1.dart | 2 +- .../lib/widgets/pop_scope/pop_scope.0.dart | 4 +- .../lib/widgets/pop_scope/pop_scope.1.dart | 232 ------------------ .../widgets/pop_scope/pop_scope.1_test.dart | 67 ----- packages/flutter/lib/src/material/about.dart | 6 +- packages/flutter/lib/src/widgets/form.dart | 4 +- .../flutter/lib/src/widgets/navigator.dart | 8 +- .../src/widgets/navigator_pop_handler.dart | 4 +- .../flutter/lib/src/widgets/pop_scope.dart | 30 +-- packages/flutter/lib/src/widgets/routes.dart | 28 +-- packages/flutter/test/cupertino/tab_test.dart | 2 +- .../flutter/test/widgets/navigator_test.dart | 20 +- .../flutter/test/widgets/pop_scope_test.dart | 100 +------- 18 files changed, 55 insertions(+), 466 deletions(-) delete mode 100644 examples/api/lib/widgets/pop_scope/pop_scope.1.dart delete mode 100644 examples/api/test/widgets/pop_scope/pop_scope.1_test.dart 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 a063c7cfe4d..a68c2a194c9 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 @@ class CupertinoNavigationDemo extends StatelessWidget { @override Widget build(BuildContext context) { - return PopScope( + 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 86c57731112..81ee4dacd86 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 @@ class FullScreenDialogDemoState extends State { bool _hasName = false; late String _eventName; - Future _handlePopInvoked(bool didPop, Object? result) async { + Future _handlePopInvoked(bool didPop) async { if (didPop) { return; } 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 dd479d5222c..c6f644ee74c 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 @@ class TextFormFieldDemoState extends State { return null; } - Future _handlePopInvoked(bool didPop, Object? result) async { + Future _handlePopInvoked(bool didPop) async { if (didPop) { return; } 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 1a90c145847..5e7a9516685 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 @@ class ExpandingBottomSheetState extends State with TickerP // 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,7 +370,7 @@ class ExpandingBottomSheetState extends State with TickerP duration: const Duration(milliseconds: 225), curve: Curves.easeInOut, alignment: FractionalOffset.topLeft, - child: PopScope( + child: PopScope( canPop: !_isOpen, onPopInvoked: _handlePopInvoked, child: AnimatedBuilder( diff --git a/dev/integration_tests/flutter_gallery/lib/gallery/home.dart b/dev/integration_tests/flutter_gallery/lib/gallery/home.dart index 1e57e39e387..a6ceab8850b 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 @@ class _GalleryHomeState extends State with SingleTickerProviderStat backgroundColor: isDark ? _kFlutterBlue : theme.primaryColor, body: SafeArea( bottom: false, - child: PopScope( + child: PopScope( canPop: _category == null, - onPopInvoked: (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 c6d602dc9f2..e008f5aaa4d 100644 --- a/examples/api/lib/widgets/form/form.1.dart +++ b/examples/api/lib/widgets/form/form.1.dart @@ -111,7 +111,7 @@ class _SaveableFormState extends State<_SaveableForm> { const SizedBox(height: 20.0), Form( canPop: !_isDirty, - onPopInvoked: (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 c86640c38a6..2400b0905e0 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 @@ class _PageTwoState extends State<_PageTwo> { mainAxisAlignment: MainAxisAlignment.center, children: [ const Text('Page Two'), - PopScope( + PopScope( canPop: false, - onPopInvoked: (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 cb3b9f2574e..00000000000 --- a/examples/api/lib/widgets/pop_scope/pop_scope.1.dart +++ /dev/null @@ -1,232 +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 showing how to use PopScope to wrap 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( - builder: (BuildContext context) => const _PageTwo(), - ), - _ => MaterialPageRoute( - 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: [ - 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('/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 _showBackDialog(BuildContext context) { - return showDialog( - 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: [ - 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( - canPop: false, - // The result contains pop result in `_PageTwo`. - onPopInvoked: (bool didPop, FormData? result) async { - if (didPop) { - return; - } - final bool shouldPop = await _showBackDialog(context) ?? false; - if (context.mounted && shouldPop) { - Navigator.pop(context, result); - } - }, - child: const _PageTwoBody(), - ); - } -} - -// 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: [ - const Text('Page Two'), - Form( - child: Column( - children: [ - 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 14266af5214..00000000000 --- 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 308a9d8c7df..a61f3a0d797 100644 --- a/packages/flutter/lib/src/material/about.dart +++ b/packages/flutter/lib/src/material/about.dart @@ -1230,9 +1230,9 @@ class _MasterDetailFlowState extends State<_MasterDetailFlow> implements _PageOp } MaterialPageRoute _detailPageRoute(Object? arguments) { - return MaterialPageRoute(builder: (BuildContext context) { - return PopScope( - onPopInvoked: (bool didPop, void result) { + return MaterialPageRoute(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 823235b96ff..2211523a4a6 100644 --- a/packages/flutter/lib/src/widgets/form.dart +++ b/packages/flutter/lib/src/widgets/form.dart @@ -177,7 +177,7 @@ class Form extends StatefulWidget { /// * [canPop], which also comes from [PopScope] and is often used in /// conjunction with this parameter. /// * [PopScope.onPopInvoked], which is what [Form] delegates to internally. - final PopInvokedCallback? onPopInvoked; + final PopInvokedCallback? onPopInvoked; /// Called when one of the form fields changes. /// @@ -244,7 +244,7 @@ class FormState extends State
{ } if (widget.canPop != null || widget.onPopInvoked != null) { - return PopScope( + return PopScope( canPop: widget.canPop ?? true, onPopInvoked: widget.onPopInvoked, child: _FormScope( diff --git a/packages/flutter/lib/src/widgets/navigator.dart b/packages/flutter/lib/src/widgets/navigator.dart index edf3eb1140c..88b869baf59 100644 --- a/packages/flutter/lib/src/widgets/navigator.dart +++ b/packages/flutter/lib/src/widgets/navigator.dart @@ -355,7 +355,7 @@ abstract class Route extends _RoutePlaceholder { /// 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 @@ class _RouteEntry extends RouteTransitionRecord { assert(isPresent); pendingResult = result; currentState = _RouteLifecycle.pop; - route.onPopInvoked(true, result); + route.onPopInvoked(true); } bool _reportRemovalToObserver = true; @@ -5239,7 +5239,7 @@ class NavigatorState extends State with TickerProviderStateMixin, Res pop(result); return true; case RoutePopDisposition.doNotPop: - lastEntry.route.onPopInvoked(false, result); + lastEntry.route.onPopInvoked(false); return true; } } @@ -5282,7 +5282,7 @@ class NavigatorState extends State with TickerProviderStateMixin, Res assert(entry.route._popCompleter.isCompleted); entry.currentState = _RouteLifecycle.pop; } - entry.route.onPopInvoked(true, result); + entry.route.onPopInvoked(true); } else { entry.pop(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 627a9cf53c9..203a85beded 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 @@ class _NavigatorPopHandlerState extends State { 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( + return PopScope( canPop: !widget.enabled || _canPop, - onPopInvoked: (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 675f701c88a..c8b31f60c8d 100644 --- a/packages/flutter/lib/src/widgets/pop_scope.dart +++ b/packages/flutter/lib/src/widgets/pop_scope.dart @@ -10,15 +10,10 @@ import 'routes.dart'; /// Manages back navigation gestures. /// -/// The generic type should match or be supertype of the generic type of the -/// enclosing [Route]. If the enclosing Route is a `MaterialPageRoute`, -/// you can define [PopScope] with int or any supertype of int. -/// /// The [canPop] parameter disables back gestures when set to `false`. /// /// The [onPopInvoked] parameter reports when pop navigation was attempted, and -/// `didPop` indicates whether or not the navigation was successful. The -/// `result` contains the pop result. +/// `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. @@ -46,13 +41,6 @@ import 'routes.dart'; /// ** 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 @@ -61,7 +49,7 @@ import 'routes.dart'; /// 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 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({ @@ -90,12 +78,10 @@ class PopScope extends StatefulWidget { /// indicates whether or not the back navigation actually happened /// successfully. /// - /// The `result` contains the pop result. - /// /// See also: /// /// * [Route.onPopInvoked], which is similar. - final PopInvokedCallback? onPopInvoked; + final PopInvokedCallback? onPopInvoked; /// {@template flutter.widgets.PopScope.canPop} /// When false, blocks the current route from being popped. @@ -113,16 +99,14 @@ class PopScope extends StatefulWidget { final bool canPop; @override - State> createState() => _PopScopeState(); + State createState() => _PopScopeState(); } -class _PopScopeState extends State> implements PopEntry { +class _PopScopeState extends State implements PopEntry { ModalRoute? _route; @override - void onPopInvoked(bool didPop, T? result) { - widget.onPopInvoked?.call(didPop, result); - } + PopInvokedCallback? get onPopInvoked => widget.onPopInvoked; @override late final ValueNotifier canPopNotifier; @@ -145,7 +129,7 @@ class _PopScopeState extends State> implements PopEntry { } @override - void didUpdateWidget(PopScope 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 eb1976c5d63..277a5fb6c55 100644 --- a/packages/flutter/lib/src/widgets/routes.dart +++ b/packages/flutter/lib/src/widgets/routes.dart @@ -1669,9 +1669,7 @@ abstract class ModalRoute extends TransitionRoute with LocalHistoryRoute _willPopCallbacks = []; - // Holding as Object? instead of T so that PopScope in this route can be - // declared with any supertype of T. - final Set> _popEntries = >{}; + final Set _popEntries = {}; /// Returns [RoutePopDisposition.doNotPop] if any of callbacks added with /// [addScopedWillPopCallback] returns either false or null. If they all @@ -1726,7 +1724,7 @@ abstract class ModalRoute extends TransitionRoute with LocalHistoryRoute popEntry in _popEntries) { + for (final PopEntry popEntry in _popEntries) { if (!popEntry.canPopNotifier.value) { return RoutePopDisposition.doNotPop; } @@ -1736,9 +1734,9 @@ abstract class ModalRoute extends TransitionRoute with LocalHistoryRoute 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 @@ abstract class ModalRoute extends TransitionRoute with LocalHistoryRoute popEntry) { + void registerPopEntry(PopEntry popEntry) { _popEntries.add(popEntry); popEntry.canPopNotifier.addListener(_handlePopEntryChange); _handlePopEntryChange(); @@ -1806,7 +1804,7 @@ abstract class ModalRoute extends TransitionRoute with LocalHistoryRoute popEntry) { + void unregisterPopEntry(PopEntry popEntry) { _popEntries.remove(popEntry); popEntry.canPopNotifier.removeListener(_handlePopEntryChange); _handlePopEntryChange(); @@ -2415,9 +2413,7 @@ typedef RouteTransitionsBuilder = Widget Function(BuildContext context, Animatio /// /// Accepts a didPop boolean indicating whether or not back navigation /// succeeded. -/// -/// The `result` contains the pop result. -typedef PopInvokedCallback = void Function(bool didPop, T? result); +typedef PopInvokedCallback = void Function(bool didPop); /// Allows listening to and preventing pops. /// @@ -2429,13 +2425,9 @@ typedef PopInvokedCallback = void Function(bool didPop, T? result); /// * [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 { +abstract class PopEntry { /// {@macro flutter.widgets.PopScope.onPopInvoked} - // This can't be a function getter since dart vm doesn't allow upcasting - // generic type of the function getter. This prevents customers from declaring - // PopScope with any generic type that is subtype of ModalRoute._popEntries. - // See https://github.com/dart-lang/sdk/issues/55427. - void onPopInvoked(bool didPop, T? result); + PopInvokedCallback? get onPopInvoked; /// {@macro flutter.widgets.PopScope.canPop} ValueListenable get canPopNotifier; diff --git a/packages/flutter/test/cupertino/tab_test.dart b/packages/flutter/test/cupertino/tab_test.dart index 7188777509a..5acf173cd63 100644 --- a/packages/flutter/test/cupertino/tab_test.dart +++ b/packages/flutter/test/cupertino/tab_test.dart @@ -305,7 +305,7 @@ void main() { BottomNavigationBarItem(label: '', icon: Text('2')) ], ), - tabBuilder: (_, int i) => PopScope( + 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 4cc9a7f49b4..0bb9ba9d5d7 100644 --- a/packages/flutter/test/widgets/navigator_test.dart +++ b/packages/flutter/test/widgets/navigator_test.dart @@ -2989,7 +2989,7 @@ void main() { const List> myPages = >[ MaterialPage(child: Text('page1')), MaterialPage( - child: PopScope( + child: PopScope( canPop: false, child: Text('page2'), ), @@ -4908,9 +4908,9 @@ void main() { home: StatefulBuilder( builder: (BuildContext context, StateSetter setState) { builderSetState = setState; - return PopScope( + return PopScope( canPop: canPop(), - onPopInvoked: (bool success, Object? result) { + onPopInvoked: (bool success) { if (success || pages.last == _Page.noPop) { return; } @@ -5024,9 +5024,9 @@ void main() { MaterialApp( home: StatefulBuilder( builder: (BuildContext context, StateSetter setState) { - return PopScope( + return PopScope( canPop: canPop(), - onPopInvoked: (bool success, Object? result) { + onPopInvoked: (bool success) { if (success || pages.last == _Page.noPop) { return; } @@ -5117,9 +5117,9 @@ void main() { MaterialApp( home: StatefulBuilder( builder: (BuildContext context, StateSetter setState) { - return PopScope( + return PopScope( canPop: canPop(), - onPopInvoked: (bool success, Object? result) { + onPopInvoked: (bool success) { if (success || pages.last == _PageWithYesPop.noPop) { return; } @@ -5189,7 +5189,7 @@ void main() { child: _LinksPage( title: 'Can pop page', canPop: true, - onPopInvoked: (bool didPop, void result) { + onPopInvoked: (bool didPop) { onPopInvokedCallCount += 1; }, ), @@ -5556,7 +5556,7 @@ class _LinksPage extends StatelessWidget { final bool? canPop; final VoidCallback? onBack; final String title; - final PopInvokedCallback? onPopInvoked; + final PopInvokedCallback? onPopInvoked; @override Widget build(BuildContext context) { @@ -5575,7 +5575,7 @@ class _LinksPage extends StatelessWidget { child: const Text('Go back'), ), if (canPop != null) - PopScope( + PopScope( canPop: canPop!, 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 7da2019ddda..116951ce787 100644 --- a/packages/flutter/test/widgets/pop_scope_test.dart +++ b/packages/flutter/test/widgets/pop_scope_test.dart @@ -47,7 +47,7 @@ void main() { builder: (BuildContext buildContext, StateSetter stateSetter) { context = buildContext; setState = stateSetter; - return PopScope( + return PopScope( canPop: canPop, child: const Center( child: Column( @@ -79,94 +79,6 @@ void main() { variant: TargetPlatformVariant.all(), ); - testWidgets('pop scope can receive result', (WidgetTester tester) async { - Object? receivedResult; - final Object poppedResult = Object(); - final GlobalKey nav = GlobalKey(); - await tester.pumpWidget( - MaterialApp( - initialRoute: '/', - navigatorKey: nav, - home: Scaffold( - body: PopScope( - canPop: false, - onPopInvoked: (bool didPop, Object? result) { - receivedResult = result; - }, - child: const Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - 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 nav = GlobalKey(); - await tester.pumpWidget( - MaterialApp( - initialRoute: '/', - navigatorKey: nav, - home: Scaffold( - body: PopScope( - canPop: false, - onPopInvoked: (bool didPop, Object? result) { - receivedResult = result; - }, - child: const Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text('Home/PopScope Page'), - ], - ), - ), - ), - ), - ), - ); - - nav.currentState!.push( - MaterialPageRoute( - builder: (BuildContext context) { - return Scaffold( - body: PopScope( - canPop: false, - onPopInvoked: (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 nav = GlobalKey(); bool canPop = true; @@ -203,9 +115,9 @@ void main() { builder: (BuildContext context, StateSetter stateSetter) { oneContext = context; setState = stateSetter; - return PopScope( + return PopScope( canPop: canPop, - onPopInvoked: (bool didPop, Object? result) { + onPopInvoked: (bool didPop) { lastPopSuccess = didPop; }, child: const Center( @@ -359,7 +271,7 @@ void main() { if (!usePopScope) { return child; } - return const PopScope( + return const PopScope( canPop: false, child: child, ); @@ -402,12 +314,12 @@ void main() { return Column( children: [ if (usePopScope1) - const PopScope( + const PopScope( canPop: false, child: Text('hello'), ), if (usePopScope2) - const PopScope( + const PopScope( canPop: false, child: Text('hello'), ),