1// Copyright 2014 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5/// @docImport 'package:flutter/cupertino.dart';
6///
7/// @docImport 'form.dart';
8/// @docImport 'navigator_pop_handler.dart';
9library;
10
11import 'package:flutter/foundation.dart';
12
13import 'framework.dart';
14import 'navigator.dart';
15import 'routes.dart';
16
17/// A callback type for informing that a navigation pop has been invoked,
18/// whether or not it was handled successfully.
19///
20/// Accepts a didPop boolean indicating whether or not back navigation
21/// succeeded.
22@Deprecated(
23 'Use PopInvokedWithResultCallback instead. '
24 'This feature was deprecated after v3.22.0-12.0.pre.',
25)
26typedef PopInvokedCallback = void Function(bool didPop);
27
28/// Manages back navigation gestures.
29///
30/// The generic type should match or be a supertype of the generic type of the
31/// enclosing [Route]. For example, if the enclosing Route is a
32/// `MaterialPageRoute<int>`, you can define [PopScope] with `int` or any
33/// supertype of `int`.
34///
35/// The [canPop] parameter disables back gestures when set to `false`.
36///
37/// The [onPopInvokedWithResult] parameter reports when pop navigation was attempted, and
38/// `didPop` indicates whether or not the navigation was successful. The
39/// `result` contains the pop result.
40///
41/// Android has a system back gesture that is a swipe inward from near the edge
42/// of the screen. It is recognized by Android before being passed to Flutter.
43/// iOS has a similar gesture that is recognized in Flutter by
44/// [CupertinoRouteTransitionMixin], not by iOS, and is therefore not a system
45/// back gesture.
46///
47/// If [canPop] is false, then a system back gesture will not pop the route off
48/// of the enclosing [Navigator]. [onPopInvokedWithResult] will still be called, and
49/// `didPop` will be `false`. On iOS when using [CupertinoRouteTransitionMixin]
50/// with [canPop] set to false, no gesture will be detected at all, so
51/// [onPopInvokedWithResult] will not be called. Programmatically attempting pop
52/// navigation will also result in a call to [onPopInvokedWithResult], with `didPop`
53/// indicating success or failure.
54///
55/// If [canPop] is true, then a system back gesture will cause the enclosing
56/// [Navigator] to receive a pop as usual. [onPopInvokedWithResult] will be called with
57/// `didPop` as true, unless the pop failed for reasons unrelated to
58/// [PopScope], in which case it will be false.
59///
60/// {@tool dartpad}
61/// This sample demonstrates showing a confirmation dialog before navigating
62/// away from a page.
63///
64/// ** See code in examples/api/lib/widgets/pop_scope/pop_scope.0.dart **
65/// {@end-tool}
66///
67/// {@tool dartpad}
68/// This sample demonstrates showing how to use PopScope to wrap widget that
69/// may pop the page with a result.
70///
71/// ** See code in examples/api/lib/widgets/pop_scope/pop_scope.1.dart **
72/// {@end-tool}
73///
74/// See also:
75///
76/// * [NavigatorPopHandler], which is a less verbose way to handle system back
77/// gestures in simple cases of nested [Navigator]s.
78/// * [Form.canPop] and [Form.onPopInvokedWithResult], which can be used to handle system
79/// back gestures in the case of a form with unsaved data.
80/// * [ModalRoute.registerPopEntry] and [ModalRoute.unregisterPopEntry],
81/// which this widget uses to integrate with Flutter's navigation system.
82@optionalTypeArgs
83class PopScope<T> extends StatefulWidget {
84 /// Creates a widget that registers a callback to veto attempts by the user to
85 /// dismiss the enclosing [ModalRoute].
86 const PopScope({
87 super.key,
88 required this.child,
89 this.canPop = true,
90 this.onPopInvokedWithResult,
91 @Deprecated(
92 'Use onPopInvokedWithResult instead. '
93 'This feature was deprecated after v3.22.0-12.0.pre.',
94 )
95 this.onPopInvoked,
96 }) : assert(
97 onPopInvokedWithResult == null || onPopInvoked == null,
98 'onPopInvoked is deprecated, use onPopInvokedWithResult',
99 );
100
101 /// The widget below this widget in the tree.
102 ///
103 /// {@macro flutter.widgets.ProxyWidget.child}
104 final Widget child;
105
106 /// {@template flutter.widgets.PopScope.onPopInvokedWithResult}
107 /// Called after a route pop was handled.
108 /// {@endtemplate}
109 ///
110 /// It's not possible to prevent the pop from happening at the time that this
111 /// method is called; the pop has already happened. Use [canPop] to
112 /// disable pops in advance.
113 ///
114 /// This will still be called even when the pop is canceled. A pop is canceled
115 /// when the relevant [Route.popDisposition] returns false, such as when
116 /// [canPop] is set to false on a [PopScope]. The `didPop` parameter
117 /// indicates whether or not the back navigation actually happened
118 /// successfully.
119 ///
120 /// The `result` contains the pop result.
121 ///
122 /// See also:
123 ///
124 /// * [Route.onPopInvokedWithResult], which is similar.
125 final PopInvokedWithResultCallback<T>? onPopInvokedWithResult;
126
127 /// Called after a route pop was handled.
128 ///
129 /// It's not possible to prevent the pop from happening at the time that this
130 /// method is called; the pop has already happened. Use [canPop] to
131 /// disable pops in advance.
132 ///
133 /// This will still be called even when the pop is canceled. A pop is canceled
134 /// when the relevant [Route.popDisposition] returns false, such as when
135 /// [canPop] is set to false on a [PopScope]. The `didPop` parameter
136 /// indicates whether or not the back navigation actually happened
137 /// successfully.
138 @Deprecated(
139 'Use onPopInvokedWithResult instead. '
140 'This feature was deprecated after v3.22.0-12.0.pre.',
141 )
142 final PopInvokedCallback? onPopInvoked;
143
144 void _callPopInvoked(bool didPop, T? result) {
145 if (onPopInvokedWithResult != null) {
146 onPopInvokedWithResult!(didPop, result);
147 return;
148 }
149 onPopInvoked?.call(didPop);
150 }
151
152 /// {@template flutter.widgets.PopScope.canPop}
153 /// When false, blocks the current route from being popped.
154 ///
155 /// This includes the root route, where upon popping, the Flutter app would
156 /// exit.
157 ///
158 /// If multiple [PopScope] widgets appear in a route's widget subtree, then
159 /// each and every `canPop` must be `true` in order for the route to be
160 /// able to pop.
161 ///
162 /// [Android's predictive back](https://developer.android.com/guide/navigation/predictive-back-gesture)
163 /// feature will not animate when this boolean is false.
164 /// {@endtemplate}
165 final bool canPop;
166
167 @override
168 State<PopScope<T>> createState() => _PopScopeState<T>();
169}
170
171class _PopScopeState<T> extends State<PopScope<T>> implements PopEntry<T> {
172 ModalRoute<dynamic>? _route;
173
174 @override
175 void onPopInvoked(bool didPop) {
176 throw UnimplementedError();
177 }
178
179 @override
180 void onPopInvokedWithResult(bool didPop, T? result) {
181 widget._callPopInvoked(didPop, result);
182 }
183
184 @override
185 late final ValueNotifier<bool> canPopNotifier;
186
187 @override
188 void initState() {
189 super.initState();
190 canPopNotifier = ValueNotifier<bool>(widget.canPop);
191 }
192
193 @override
194 void didChangeDependencies() {
195 super.didChangeDependencies();
196 final ModalRoute<dynamic>? nextRoute = ModalRoute.of(context);
197 if (nextRoute != _route) {
198 _route?.unregisterPopEntry(this);
199 _route = nextRoute;
200 _route?.registerPopEntry(this);
201 }
202 }
203
204 @override
205 void didUpdateWidget(PopScope<T> oldWidget) {
206 super.didUpdateWidget(oldWidget);
207 canPopNotifier.value = widget.canPop;
208 }
209
210 @override
211 void dispose() {
212 _route?.unregisterPopEntry(this);
213 canPopNotifier.dispose();
214 super.dispose();
215 }
216
217 @override
218 Widget build(BuildContext context) => widget.child;
219}
220

Provided by KDAB

Privacy Policy
Learn more about Flutter for embedded and desktop on industrialflutter.com