mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
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
This commit is contained in:
parent
e8fc89e690
commit
bb4c9b381e
@ -48,7 +48,7 @@ class CupertinoNavigationDemo extends StatelessWidget {
|
||||
|
||||
@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(
|
||||
|
@ -110,7 +110,7 @@ class FullScreenDialogDemoState extends State<FullScreenDialogDemo> {
|
||||
bool _hasName = false;
|
||||
late String _eventName;
|
||||
|
||||
Future<void> _handlePopInvoked(bool didPop, Object? result) async {
|
||||
Future<void> _handlePopInvoked(bool didPop) async {
|
||||
if (didPop) {
|
||||
return;
|
||||
}
|
||||
|
@ -143,7 +143,7 @@ class TextFormFieldDemoState extends State<TextFormFieldDemo> {
|
||||
return null;
|
||||
}
|
||||
|
||||
Future<void> _handlePopInvoked(bool didPop, Object? result) async {
|
||||
Future<void> _handlePopInvoked(bool didPop) async {
|
||||
if (didPop) {
|
||||
return;
|
||||
}
|
||||
|
@ -355,7 +355,7 @@ class ExpandingBottomSheetState extends State<ExpandingBottomSheet> 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<ExpandingBottomSheet> with TickerP
|
||||
duration: const Duration(milliseconds: 225),
|
||||
curve: Curves.easeInOut,
|
||||
alignment: FractionalOffset.topLeft,
|
||||
child: PopScope<Object?>(
|
||||
child: PopScope(
|
||||
canPop: !_isOpen,
|
||||
onPopInvoked: _handlePopInvoked,
|
||||
child: AnimatedBuilder(
|
||||
|
@ -326,9 +326,9 @@ class _GalleryHomeState extends State<GalleryHome> with SingleTickerProviderStat
|
||||
backgroundColor: isDark ? _kFlutterBlue : theme.primaryColor,
|
||||
body: SafeArea(
|
||||
bottom: false,
|
||||
child: PopScope<Object?>(
|
||||
child: PopScope(
|
||||
canPop: _category == null,
|
||||
onPopInvoked: (bool didPop, Object? result) {
|
||||
onPopInvoked: (bool didPop) {
|
||||
if (didPop) {
|
||||
return;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -109,9 +109,9 @@ class _PageTwoState extends State<_PageTwo> {
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
const Text('Page Two'),
|
||||
PopScope<Object?>(
|
||||
PopScope(
|
||||
canPop: false,
|
||||
onPopInvoked: (bool didPop, Object? result) async {
|
||||
onPopInvoked: (bool didPop) async {
|
||||
if (didPop) {
|
||||
return;
|
||||
}
|
||||
|
@ -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<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 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: <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);
|
||||
}
|
@ -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);
|
||||
});
|
||||
}
|
@ -1230,9 +1230,9 @@ class _MasterDetailFlowState extends State<_MasterDetailFlow> implements _PageOp
|
||||
}
|
||||
|
||||
MaterialPageRoute<void> _detailPageRoute(Object? arguments) {
|
||||
return MaterialPageRoute<void>(builder: (BuildContext context) {
|
||||
return PopScope<void>(
|
||||
onPopInvoked: (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;
|
||||
},
|
||||
|
@ -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<Object?>? onPopInvoked;
|
||||
final PopInvokedCallback? onPopInvoked;
|
||||
|
||||
/// Called when one of the form fields changes.
|
||||
///
|
||||
@ -244,7 +244,7 @@ class FormState extends State<Form> {
|
||||
}
|
||||
|
||||
if (widget.canPop != null || widget.onPopInvoked != null) {
|
||||
return PopScope<Object?>(
|
||||
return PopScope(
|
||||
canPop: widget.canPop ?? true,
|
||||
onPopInvoked: widget.onPopInvoked,
|
||||
child: _FormScope(
|
||||
|
@ -355,7 +355,7 @@ abstract class Route<T> 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<Navigator> 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<Navigator> with TickerProviderStateMixin, Res
|
||||
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);
|
||||
|
@ -81,9 +81,9 @@ class _NavigatorPopHandlerState extends State<NavigatorPopHandler> {
|
||||
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,
|
||||
onPopInvoked: (bool didPop, Object? result) {
|
||||
onPopInvoked: (bool didPop) {
|
||||
if (didPop) {
|
||||
return;
|
||||
}
|
||||
|
@ -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<int>`,
|
||||
/// 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<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({
|
||||
@ -90,12 +78,10 @@ class PopScope<T> 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<T>? onPopInvoked;
|
||||
final PopInvokedCallback? onPopInvoked;
|
||||
|
||||
/// {@template flutter.widgets.PopScope.canPop}
|
||||
/// When false, blocks the current route from being popped.
|
||||
@ -113,16 +99,14 @@ class PopScope<T> extends StatefulWidget {
|
||||
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.onPopInvoked?.call(didPop, result);
|
||||
}
|
||||
PopInvokedCallback? get onPopInvoked => widget.onPopInvoked;
|
||||
|
||||
@override
|
||||
late final ValueNotifier<bool> canPopNotifier;
|
||||
@ -145,7 +129,7 @@ class _PopScopeState<T> extends State<PopScope<T>> implements PopEntry<T> {
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(PopScope<T> oldWidget) {
|
||||
void didUpdateWidget(PopScope oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
canPopNotifier.value = widget.canPop;
|
||||
}
|
||||
|
@ -1669,9 +1669,7 @@ abstract class ModalRoute<T> extends TransitionRoute<T> with LocalHistoryRoute<T
|
||||
|
||||
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
|
||||
@ -1726,7 +1724,7 @@ abstract class ModalRoute<T> extends TransitionRoute<T> with LocalHistoryRoute<T
|
||||
/// 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 @@ abstract class ModalRoute<T> extends TransitionRoute<T> with LocalHistoryRoute<T
|
||||
}
|
||||
|
||||
@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 @@ abstract class ModalRoute<T> extends TransitionRoute<T> with LocalHistoryRoute<T
|
||||
/// 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 @@ abstract class ModalRoute<T> extends TransitionRoute<T> with LocalHistoryRoute<T
|
||||
/// 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 @@ 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<T> = void Function(bool didPop, T? result);
|
||||
typedef PopInvokedCallback = void Function(bool didPop);
|
||||
|
||||
/// Allows listening to and preventing pops.
|
||||
///
|
||||
@ -2429,13 +2425,9 @@ typedef PopInvokedCallback<T> = 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<T> {
|
||||
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<bool> get canPopNotifier;
|
||||
|
@ -305,7 +305,7 @@ void main() {
|
||||
BottomNavigationBarItem(label: '', icon: Text('2'))
|
||||
],
|
||||
),
|
||||
tabBuilder: (_, int i) => PopScope<Object?>(
|
||||
tabBuilder: (_, int i) => PopScope(
|
||||
canPop: false,
|
||||
child: CupertinoTabView(
|
||||
navigatorKey: key,
|
||||
|
@ -2989,7 +2989,7 @@ void main() {
|
||||
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 @@ void main() {
|
||||
home: StatefulBuilder(
|
||||
builder: (BuildContext context, StateSetter setState) {
|
||||
builderSetState = setState;
|
||||
return PopScope<Object?>(
|
||||
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<Object?>(
|
||||
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<Object?>(
|
||||
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<Object?>? 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<void>(
|
||||
PopScope(
|
||||
canPop: canPop!,
|
||||
onPopInvoked: onPopInvoked,
|
||||
child: const SizedBox.shrink(),
|
||||
|
@ -47,7 +47,7 @@ void main() {
|
||||
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 @@ void main() {
|
||||
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,
|
||||
onPopInvoked: (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,
|
||||
onPopInvoked: (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,
|
||||
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<NavigatorState> nav = GlobalKey<NavigatorState>();
|
||||
bool canPop = true;
|
||||
@ -203,9 +115,9 @@ void main() {
|
||||
builder: (BuildContext context, StateSetter stateSetter) {
|
||||
oneContext = context;
|
||||
setState = stateSetter;
|
||||
return PopScope<Object?>(
|
||||
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<Object?>(
|
||||
return const PopScope(
|
||||
canPop: false,
|
||||
child: child,
|
||||
);
|
||||
@ -402,12 +314,12 @@ void main() {
|
||||
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'),
|
||||
),
|
||||
|
Loading…
Reference in New Issue
Block a user