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/material.dart'; |
6 | /// |
7 | /// @docImport 'container.dart'; |
8 | /// @docImport 'scrollable.dart'; |
9 | library; |
10 | |
11 | import 'package:flutter/foundation.dart'; |
12 | import 'package:flutter/gestures.dart'; |
13 | import 'package:flutter/rendering.dart'; |
14 | |
15 | import 'basic.dart'; |
16 | import 'framework.dart'; |
17 | import 'media_query.dart'; |
18 | import 'scroll_configuration.dart'; |
19 | |
20 | export 'package:flutter/gestures.dart' |
21 | show |
22 | DragDownDetails, |
23 | DragEndDetails, |
24 | DragStartDetails, |
25 | DragUpdateDetails, |
26 | ForcePressDetails, |
27 | GestureDragCancelCallback, |
28 | GestureDragDownCallback, |
29 | GestureDragEndCallback, |
30 | GestureDragStartCallback, |
31 | GestureDragUpdateCallback, |
32 | GestureForcePressEndCallback, |
33 | GestureForcePressPeakCallback, |
34 | GestureForcePressStartCallback, |
35 | GestureForcePressUpdateCallback, |
36 | GestureLongPressCallback, |
37 | GestureLongPressEndCallback, |
38 | GestureLongPressMoveUpdateCallback, |
39 | GestureLongPressStartCallback, |
40 | GestureLongPressUpCallback, |
41 | GestureScaleEndCallback, |
42 | GestureScaleStartCallback, |
43 | GestureScaleUpdateCallback, |
44 | GestureTapCallback, |
45 | GestureTapCancelCallback, |
46 | GestureTapDownCallback, |
47 | GestureTapUpCallback, |
48 | LongPressEndDetails, |
49 | LongPressMoveUpdateDetails, |
50 | LongPressStartDetails, |
51 | ScaleEndDetails, |
52 | ScaleStartDetails, |
53 | ScaleUpdateDetails, |
54 | TapDownDetails, |
55 | TapUpDetails, |
56 | Velocity; |
57 | export 'package:flutter/rendering.dart' show RenderSemanticsGestureHandler; |
58 | |
59 | // Examples can assume: |
60 | // late bool _lights; |
61 | // void setState(VoidCallback fn) { } |
62 | // late String _last; |
63 | // late Color _color; |
64 | |
65 | /// Factory for creating gesture recognizers. |
66 | /// |
67 | /// `T` is the type of gesture recognizer this class manages. |
68 | /// |
69 | /// Used by [RawGestureDetector.gestures]. |
70 | @optionalTypeArgs |
71 | abstract class GestureRecognizerFactory<T extends GestureRecognizer> { |
72 | /// Abstract const constructor. This constructor enables subclasses to provide |
73 | /// const constructors so that they can be used in const expressions. |
74 | const GestureRecognizerFactory(); |
75 | |
76 | /// Must return an instance of T. |
77 | T constructor(); |
78 | |
79 | /// Must configure the given instance (which will have been created by |
80 | /// `constructor`). |
81 | /// |
82 | /// This normally means setting the callbacks. |
83 | void initializer(T instance); |
84 | |
85 | bool _debugAssertTypeMatches(Type type) { |
86 | assert( |
87 | type == T, |
88 | 'GestureRecognizerFactory of type$T was used where type$type was specified.', |
89 | ); |
90 | return true; |
91 | } |
92 | } |
93 | |
94 | /// Signature for closures that implement [GestureRecognizerFactory.constructor]. |
95 | typedef GestureRecognizerFactoryConstructor<T extends GestureRecognizer> = T Function(); |
96 | |
97 | /// Signature for closures that implement [GestureRecognizerFactory.initializer]. |
98 | typedef GestureRecognizerFactoryInitializer<T extends GestureRecognizer> = |
99 | void Function(T instance); |
100 | |
101 | /// Factory for creating gesture recognizers that delegates to callbacks. |
102 | /// |
103 | /// Used by [RawGestureDetector.gestures]. |
104 | class GestureRecognizerFactoryWithHandlers<T extends GestureRecognizer> |
105 | extends GestureRecognizerFactory<T> { |
106 | /// Creates a gesture recognizer factory with the given callbacks. |
107 | const GestureRecognizerFactoryWithHandlers(this._constructor, this._initializer); |
108 | |
109 | final GestureRecognizerFactoryConstructor<T> _constructor; |
110 | |
111 | final GestureRecognizerFactoryInitializer<T> _initializer; |
112 | |
113 | @override |
114 | T constructor() => _constructor(); |
115 | |
116 | @override |
117 | void initializer(T instance) => _initializer(instance); |
118 | } |
119 | |
120 | /// A widget that detects gestures. |
121 | /// |
122 | /// Attempts to recognize gestures that correspond to its non-null callbacks. |
123 | /// |
124 | /// If this widget has a child, it defers to that child for its sizing behavior. |
125 | /// If it does not have a child, it grows to fit the parent instead. |
126 | /// |
127 | /// {@youtube 560 315 https://www.youtube.com/watch?v=WhVXkCFPmK4} |
128 | /// |
129 | /// By default a GestureDetector with an invisible child ignores touches; |
130 | /// this behavior can be controlled with [behavior]. |
131 | /// |
132 | /// GestureDetector also listens for accessibility events and maps |
133 | /// them to the callbacks. To ignore accessibility events, set |
134 | /// [excludeFromSemantics] to true. |
135 | /// |
136 | /// See <http://flutter.dev/to/gestures> for additional information. |
137 | /// |
138 | /// Material design applications typically react to touches with ink splash |
139 | /// effects. The [InkWell] class implements this effect and can be used in place |
140 | /// of a [GestureDetector] for handling taps. |
141 | /// |
142 | /// {@tool dartpad} |
143 | /// This example contains a black light bulb wrapped in a [GestureDetector]. It |
144 | /// turns the light bulb yellow when the "TURN LIGHT ON" button is tapped by |
145 | /// setting the `_lights` field, and off again when "TURN LIGHT OFF" is tapped. |
146 | /// |
147 | /// ** See code in examples/api/lib/widgets/gesture_detector/gesture_detector.0.dart ** |
148 | /// {@end-tool} |
149 | /// |
150 | /// {@tool dartpad} |
151 | /// This example uses a [Container] that wraps a [GestureDetector] widget which |
152 | /// detects a tap. |
153 | /// |
154 | /// Since the [GestureDetector] does not have a child, it takes on the size of its |
155 | /// parent, making the entire area of the surrounding [Container] clickable. When |
156 | /// tapped, the [Container] turns yellow by setting the `_color` field. When |
157 | /// tapped again, it goes back to white. |
158 | /// |
159 | /// ** See code in examples/api/lib/widgets/gesture_detector/gesture_detector.1.dart ** |
160 | /// {@end-tool} |
161 | /// |
162 | /// ### Troubleshooting |
163 | /// |
164 | /// Why isn't my parent [GestureDetector.onTap] method called? |
165 | /// |
166 | /// Given a parent [GestureDetector] with an onTap callback, and a child |
167 | /// GestureDetector that also defines an onTap callback, when the inner |
168 | /// GestureDetector is tapped, both GestureDetectors send a [GestureRecognizer] |
169 | /// into the gesture arena. This is because the pointer coordinates are within the |
170 | /// bounds of both GestureDetectors. The child GestureDetector wins in this |
171 | /// scenario because it was the first to enter the arena, resolving as first come, |
172 | /// first served. The child onTap is called, and the parent's is not as the gesture has |
173 | /// been consumed. |
174 | /// For more information on gesture disambiguation see: |
175 | /// [Gesture disambiguation](https://flutter.dev/to/gesture-disambiguation). |
176 | /// |
177 | /// Setting [GestureDetector.behavior] to [HitTestBehavior.opaque] |
178 | /// or [HitTestBehavior.translucent] has no impact on parent-child relationships: |
179 | /// both GestureDetectors send a GestureRecognizer into the gesture arena, only one wins. |
180 | /// |
181 | /// Some callbacks (e.g. onTapDown) can fire before a recognizer wins the arena, |
182 | /// and others (e.g. onTapCancel) fire even when it loses the arena. Therefore, |
183 | /// the parent detector in the example above may call some of its callbacks even |
184 | /// though it loses in the arena. |
185 | /// |
186 | /// {@tool dartpad} |
187 | /// This example uses a [GestureDetector] that wraps a green [Container] and a second |
188 | /// GestureDetector that wraps a yellow Container. The second GestureDetector is |
189 | /// a child of the green Container. |
190 | /// Both GestureDetectors define an onTap callback. When the callback is called it |
191 | /// adds a red border to the corresponding Container. |
192 | /// |
193 | /// When the green Container is tapped, it's parent GestureDetector enters |
194 | /// the gesture arena. It wins because there is no competing GestureDetector and |
195 | /// the green Container shows a red border. |
196 | /// When the yellow Container is tapped, it's parent GestureDetector enters |
197 | /// the gesture arena. The GestureDetector that wraps the green Container also |
198 | /// enters the gesture arena (the pointer events coordinates are inside both |
199 | /// GestureDetectors bounds). The GestureDetector that wraps the yellow Container |
200 | /// wins because it was the first detector to enter the arena. |
201 | /// |
202 | /// This example sets [debugPrintGestureArenaDiagnostics] to true. |
203 | /// This flag prints useful information about gesture arenas. |
204 | /// |
205 | /// Changing the [GestureDetector.behavior] property to [HitTestBehavior.translucent] |
206 | /// or [HitTestBehavior.opaque] has no impact: both GestureDetectors send a [GestureRecognizer] |
207 | /// into the gesture arena, only one wins. |
208 | /// |
209 | /// ** See code in examples/api/lib/widgets/gesture_detector/gesture_detector.2.dart ** |
210 | /// {@end-tool} |
211 | /// |
212 | /// ## Debugging |
213 | /// |
214 | /// To see how large the hit test box of a [GestureDetector] is for debugging |
215 | /// purposes, set [debugPaintPointersEnabled] to true. |
216 | /// |
217 | /// See also: |
218 | /// |
219 | /// * [Listener], a widget for listening to lower-level raw pointer events. |
220 | /// * [MouseRegion], a widget that tracks the movement of mice, even when no |
221 | /// button is pressed. |
222 | /// * [RawGestureDetector], a widget that is used to detect custom gestures. |
223 | class GestureDetector extends StatelessWidget { |
224 | /// Creates a widget that detects gestures. |
225 | /// |
226 | /// Pan and scale callbacks cannot be used simultaneously because scale is a |
227 | /// superset of pan. Use the scale callbacks instead. |
228 | /// |
229 | /// Horizontal and vertical drag callbacks cannot be used simultaneously |
230 | /// because a combination of a horizontal and vertical drag is a pan. |
231 | /// Use the pan callbacks instead. |
232 | /// |
233 | /// {@youtube 560 315 https://www.youtube.com/watch?v=WhVXkCFPmK4} |
234 | /// |
235 | /// By default, gesture detectors contribute semantic information to the tree |
236 | /// that is used by assistive technology. |
237 | GestureDetector({ |
238 | super.key, |
239 | this.child, |
240 | this.onTapDown, |
241 | this.onTapUp, |
242 | this.onTap, |
243 | this.onTapMove, |
244 | this.onTapCancel, |
245 | this.onSecondaryTap, |
246 | this.onSecondaryTapDown, |
247 | this.onSecondaryTapUp, |
248 | this.onSecondaryTapCancel, |
249 | this.onTertiaryTapDown, |
250 | this.onTertiaryTapUp, |
251 | this.onTertiaryTapCancel, |
252 | this.onDoubleTapDown, |
253 | this.onDoubleTap, |
254 | this.onDoubleTapCancel, |
255 | this.onLongPressDown, |
256 | this.onLongPressCancel, |
257 | this.onLongPress, |
258 | this.onLongPressStart, |
259 | this.onLongPressMoveUpdate, |
260 | this.onLongPressUp, |
261 | this.onLongPressEnd, |
262 | this.onSecondaryLongPressDown, |
263 | this.onSecondaryLongPressCancel, |
264 | this.onSecondaryLongPress, |
265 | this.onSecondaryLongPressStart, |
266 | this.onSecondaryLongPressMoveUpdate, |
267 | this.onSecondaryLongPressUp, |
268 | this.onSecondaryLongPressEnd, |
269 | this.onTertiaryLongPressDown, |
270 | this.onTertiaryLongPressCancel, |
271 | this.onTertiaryLongPress, |
272 | this.onTertiaryLongPressStart, |
273 | this.onTertiaryLongPressMoveUpdate, |
274 | this.onTertiaryLongPressUp, |
275 | this.onTertiaryLongPressEnd, |
276 | this.onVerticalDragDown, |
277 | this.onVerticalDragStart, |
278 | this.onVerticalDragUpdate, |
279 | this.onVerticalDragEnd, |
280 | this.onVerticalDragCancel, |
281 | this.onHorizontalDragDown, |
282 | this.onHorizontalDragStart, |
283 | this.onHorizontalDragUpdate, |
284 | this.onHorizontalDragEnd, |
285 | this.onHorizontalDragCancel, |
286 | this.onForcePressStart, |
287 | this.onForcePressPeak, |
288 | this.onForcePressUpdate, |
289 | this.onForcePressEnd, |
290 | this.onPanDown, |
291 | this.onPanStart, |
292 | this.onPanUpdate, |
293 | this.onPanEnd, |
294 | this.onPanCancel, |
295 | this.onScaleStart, |
296 | this.onScaleUpdate, |
297 | this.onScaleEnd, |
298 | this.behavior, |
299 | this.excludeFromSemantics = false, |
300 | this.dragStartBehavior = DragStartBehavior.start, |
301 | this.trackpadScrollCausesScale = false, |
302 | this.trackpadScrollToScaleFactor = kDefaultTrackpadScrollToScaleFactor, |
303 | this.supportedDevices, |
304 | }) : assert(() { |
305 | final bool haveVerticalDrag = |
306 | onVerticalDragStart != null || |
307 | onVerticalDragUpdate != null || |
308 | onVerticalDragEnd != null; |
309 | final bool haveHorizontalDrag = |
310 | onHorizontalDragStart != null || |
311 | onHorizontalDragUpdate != null || |
312 | onHorizontalDragEnd != null; |
313 | final bool havePan = onPanStart != null || onPanUpdate != null || onPanEnd != null; |
314 | final bool haveScale = onScaleStart != null || onScaleUpdate != null || onScaleEnd != null; |
315 | if (havePan || haveScale) { |
316 | if (havePan && haveScale) { |
317 | throw FlutterError.fromParts(<DiagnosticsNode>[ |
318 | ErrorSummary('Incorrect GestureDetector arguments.'), |
319 | ErrorDescription( |
320 | 'Having both a pan gesture recognizer and a scale gesture recognizer is redundant; scale is a superset of pan.', |
321 | ), |
322 | ErrorHint('Just use the scale gesture recognizer.'), |
323 | ]); |
324 | } |
325 | final String recognizer = havePan ? 'pan': 'scale'; |
326 | if (haveVerticalDrag && haveHorizontalDrag) { |
327 | throw FlutterError( |
328 | 'Incorrect GestureDetector arguments.\n' |
329 | 'Simultaneously having a vertical drag gesture recognizer, a horizontal drag gesture recognizer, and a$recognizer gesture recognizer ' |
330 | 'will result in the$recognizer gesture recognizer being ignored, since the other two will catch all drags.', |
331 | ); |
332 | } |
333 | } |
334 | return true; |
335 | }()); |
336 | |
337 | /// The widget below this widget in the tree. |
338 | /// |
339 | /// {@macro flutter.widgets.ProxyWidget.child} |
340 | final Widget? child; |
341 | |
342 | /// A pointer that might cause a tap with a primary button has contacted the |
343 | /// screen at a particular location. |
344 | /// |
345 | /// This is called after a short timeout, even if the winning gesture has not |
346 | /// yet been selected. If the tap gesture wins, [onTapUp] will be called, |
347 | /// otherwise [onTapCancel] will be called. |
348 | /// |
349 | /// See also: |
350 | /// |
351 | /// * [kPrimaryButton], the button this callback responds to. |
352 | final GestureTapDownCallback? onTapDown; |
353 | |
354 | /// A pointer that will trigger a tap with a primary button has stopped |
355 | /// contacting the screen at a particular location. |
356 | /// |
357 | /// This triggers immediately before [onTap] in the case of the tap gesture |
358 | /// winning. If the tap gesture did not win, [onTapCancel] is called instead. |
359 | /// |
360 | /// See also: |
361 | /// |
362 | /// * [kPrimaryButton], the button this callback responds to. |
363 | final GestureTapUpCallback? onTapUp; |
364 | |
365 | /// A tap with a primary button has occurred. |
366 | /// |
367 | /// This triggers when the tap gesture wins. If the tap gesture did not win, |
368 | /// [onTapCancel] is called instead. |
369 | /// |
370 | /// See also: |
371 | /// |
372 | /// * [kPrimaryButton], the button this callback responds to. |
373 | /// * [onTapUp], which is called at the same time but includes details |
374 | /// regarding the pointer position. |
375 | final GestureTapCallback? onTap; |
376 | |
377 | /// A pointer that triggered a tap has moved. |
378 | /// |
379 | /// This triggers when the pointer moves after the tap gesture has been recognized. |
380 | /// |
381 | /// See also: |
382 | /// |
383 | /// * [kPrimaryButton], the button this callback responds to. |
384 | final GestureTapMoveCallback? onTapMove; |
385 | |
386 | /// The pointer that previously triggered [onTapDown] will not end up causing |
387 | /// a tap. |
388 | /// |
389 | /// This is called after [onTapDown], and instead of [onTapUp] and [onTap], if |
390 | /// the tap gesture did not win. |
391 | /// |
392 | /// See also: |
393 | /// |
394 | /// * [kPrimaryButton], the button this callback responds to. |
395 | final GestureTapCancelCallback? onTapCancel; |
396 | |
397 | /// A tap with a secondary button has occurred. |
398 | /// |
399 | /// This triggers when the tap gesture wins. If the tap gesture did not win, |
400 | /// [onSecondaryTapCancel] is called instead. |
401 | /// |
402 | /// See also: |
403 | /// |
404 | /// * [kSecondaryButton], the button this callback responds to. |
405 | /// * [onSecondaryTapUp], which is called at the same time but includes details |
406 | /// regarding the pointer position. |
407 | final GestureTapCallback? onSecondaryTap; |
408 | |
409 | /// A pointer that might cause a tap with a secondary button has contacted the |
410 | /// screen at a particular location. |
411 | /// |
412 | /// This is called after a short timeout, even if the winning gesture has not |
413 | /// yet been selected. If the tap gesture wins, [onSecondaryTapUp] will be |
414 | /// called, otherwise [onSecondaryTapCancel] will be called. |
415 | /// |
416 | /// See also: |
417 | /// |
418 | /// * [kSecondaryButton], the button this callback responds to. |
419 | final GestureTapDownCallback? onSecondaryTapDown; |
420 | |
421 | /// A pointer that will trigger a tap with a secondary button has stopped |
422 | /// contacting the screen at a particular location. |
423 | /// |
424 | /// This triggers in the case of the tap gesture winning. If the tap gesture |
425 | /// did not win, [onSecondaryTapCancel] is called instead. |
426 | /// |
427 | /// See also: |
428 | /// |
429 | /// * [onSecondaryTap], a handler triggered right after this one that doesn't |
430 | /// pass any details about the tap. |
431 | /// * [kSecondaryButton], the button this callback responds to. |
432 | final GestureTapUpCallback? onSecondaryTapUp; |
433 | |
434 | /// The pointer that previously triggered [onSecondaryTapDown] will not end up |
435 | /// causing a tap. |
436 | /// |
437 | /// This is called after [onSecondaryTapDown], and instead of |
438 | /// [onSecondaryTapUp], if the tap gesture did not win. |
439 | /// |
440 | /// See also: |
441 | /// |
442 | /// * [kSecondaryButton], the button this callback responds to. |
443 | final GestureTapCancelCallback? onSecondaryTapCancel; |
444 | |
445 | /// A pointer that might cause a tap with a tertiary button has contacted the |
446 | /// screen at a particular location. |
447 | /// |
448 | /// This is called after a short timeout, even if the winning gesture has not |
449 | /// yet been selected. If the tap gesture wins, [onTertiaryTapUp] will be |
450 | /// called, otherwise [onTertiaryTapCancel] will be called. |
451 | /// |
452 | /// See also: |
453 | /// |
454 | /// * [kTertiaryButton], the button this callback responds to. |
455 | final GestureTapDownCallback? onTertiaryTapDown; |
456 | |
457 | /// A pointer that will trigger a tap with a tertiary button has stopped |
458 | /// contacting the screen at a particular location. |
459 | /// |
460 | /// This triggers in the case of the tap gesture winning. If the tap gesture |
461 | /// did not win, [onTertiaryTapCancel] is called instead. |
462 | /// |
463 | /// See also: |
464 | /// |
465 | /// * [kTertiaryButton], the button this callback responds to. |
466 | final GestureTapUpCallback? onTertiaryTapUp; |
467 | |
468 | /// The pointer that previously triggered [onTertiaryTapDown] will not end up |
469 | /// causing a tap. |
470 | /// |
471 | /// This is called after [onTertiaryTapDown], and instead of |
472 | /// [onTertiaryTapUp], if the tap gesture did not win. |
473 | /// |
474 | /// See also: |
475 | /// |
476 | /// * [kTertiaryButton], the button this callback responds to. |
477 | final GestureTapCancelCallback? onTertiaryTapCancel; |
478 | |
479 | /// A pointer that might cause a double tap has contacted the screen at a |
480 | /// particular location. |
481 | /// |
482 | /// Triggered immediately after the down event of the second tap. |
483 | /// |
484 | /// If the user completes the double tap and the gesture wins, [onDoubleTap] |
485 | /// will be called after this callback. Otherwise, [onDoubleTapCancel] will |
486 | /// be called after this callback. |
487 | /// |
488 | /// See also: |
489 | /// |
490 | /// * [kPrimaryButton], the button this callback responds to. |
491 | final GestureTapDownCallback? onDoubleTapDown; |
492 | |
493 | /// The user has tapped the screen with a primary button at the same location |
494 | /// twice in quick succession. |
495 | /// |
496 | /// See also: |
497 | /// |
498 | /// * [kPrimaryButton], the button this callback responds to. |
499 | final GestureTapCallback? onDoubleTap; |
500 | |
501 | /// The pointer that previously triggered [onDoubleTapDown] will not end up |
502 | /// causing a double tap. |
503 | /// |
504 | /// See also: |
505 | /// |
506 | /// * [kPrimaryButton], the button this callback responds to. |
507 | final GestureTapCancelCallback? onDoubleTapCancel; |
508 | |
509 | /// The pointer has contacted the screen with a primary button, which might |
510 | /// be the start of a long-press. |
511 | /// |
512 | /// This triggers after the pointer down event. |
513 | /// |
514 | /// If the user completes the long-press, and this gesture wins, |
515 | /// [onLongPressStart] will be called after this callback. Otherwise, |
516 | /// [onLongPressCancel] will be called after this callback. |
517 | /// |
518 | /// See also: |
519 | /// |
520 | /// * [kPrimaryButton], the button this callback responds to. |
521 | /// * [onSecondaryLongPressDown], a similar callback but for a secondary button. |
522 | /// * [onTertiaryLongPressDown], a similar callback but for a tertiary button. |
523 | /// * [LongPressGestureRecognizer.onLongPressDown], which exposes this |
524 | /// callback at the gesture layer. |
525 | final GestureLongPressDownCallback? onLongPressDown; |
526 | |
527 | /// A pointer that previously triggered [onLongPressDown] will not end up |
528 | /// causing a long-press. |
529 | /// |
530 | /// This triggers once the gesture loses if [onLongPressDown] has previously |
531 | /// been triggered. |
532 | /// |
533 | /// If the user completed the long-press, and the gesture won, then |
534 | /// [onLongPressStart] and [onLongPress] are called instead. |
535 | /// |
536 | /// See also: |
537 | /// |
538 | /// * [kPrimaryButton], the button this callback responds to. |
539 | /// * [LongPressGestureRecognizer.onLongPressCancel], which exposes this |
540 | /// callback at the gesture layer. |
541 | final GestureLongPressCancelCallback? onLongPressCancel; |
542 | |
543 | /// Called when a long press gesture with a primary button has been recognized. |
544 | /// |
545 | /// Triggered when a pointer has remained in contact with the screen at the |
546 | /// same location for a long period of time. |
547 | /// |
548 | /// This is equivalent to (and is called immediately after) [onLongPressStart]. |
549 | /// The only difference between the two is that this callback does not |
550 | /// contain details of the position at which the pointer initially contacted |
551 | /// the screen. |
552 | /// |
553 | /// See also: |
554 | /// |
555 | /// * [kPrimaryButton], the button this callback responds to. |
556 | /// * [LongPressGestureRecognizer.onLongPress], which exposes this |
557 | /// callback at the gesture layer. |
558 | final GestureLongPressCallback? onLongPress; |
559 | |
560 | /// Called when a long press gesture with a primary button has been recognized. |
561 | /// |
562 | /// Triggered when a pointer has remained in contact with the screen at the |
563 | /// same location for a long period of time. |
564 | /// |
565 | /// This is equivalent to (and is called immediately before) [onLongPress]. |
566 | /// The only difference between the two is that this callback contains |
567 | /// details of the position at which the pointer initially contacted the |
568 | /// screen, whereas [onLongPress] does not. |
569 | /// |
570 | /// See also: |
571 | /// |
572 | /// * [kPrimaryButton], the button this callback responds to. |
573 | /// * [LongPressGestureRecognizer.onLongPressStart], which exposes this |
574 | /// callback at the gesture layer. |
575 | final GestureLongPressStartCallback? onLongPressStart; |
576 | |
577 | /// A pointer has been drag-moved after a long-press with a primary button. |
578 | /// |
579 | /// See also: |
580 | /// |
581 | /// * [kPrimaryButton], the button this callback responds to. |
582 | /// * [LongPressGestureRecognizer.onLongPressMoveUpdate], which exposes this |
583 | /// callback at the gesture layer. |
584 | final GestureLongPressMoveUpdateCallback? onLongPressMoveUpdate; |
585 | |
586 | /// A pointer that has triggered a long-press with a primary button has |
587 | /// stopped contacting the screen. |
588 | /// |
589 | /// This is equivalent to (and is called immediately after) [onLongPressEnd]. |
590 | /// The only difference between the two is that this callback does not |
591 | /// contain details of the state of the pointer when it stopped contacting |
592 | /// the screen. |
593 | /// |
594 | /// See also: |
595 | /// |
596 | /// * [kPrimaryButton], the button this callback responds to. |
597 | /// * [LongPressGestureRecognizer.onLongPressUp], which exposes this |
598 | /// callback at the gesture layer. |
599 | final GestureLongPressUpCallback? onLongPressUp; |
600 | |
601 | /// A pointer that has triggered a long-press with a primary button has |
602 | /// stopped contacting the screen. |
603 | /// |
604 | /// This is equivalent to (and is called immediately before) [onLongPressUp]. |
605 | /// The only difference between the two is that this callback contains |
606 | /// details of the state of the pointer when it stopped contacting the |
607 | /// screen, whereas [onLongPressUp] does not. |
608 | /// |
609 | /// See also: |
610 | /// |
611 | /// * [kPrimaryButton], the button this callback responds to. |
612 | /// * [LongPressGestureRecognizer.onLongPressEnd], which exposes this |
613 | /// callback at the gesture layer. |
614 | final GestureLongPressEndCallback? onLongPressEnd; |
615 | |
616 | /// The pointer has contacted the screen with a secondary button, which might |
617 | /// be the start of a long-press. |
618 | /// |
619 | /// This triggers after the pointer down event. |
620 | /// |
621 | /// If the user completes the long-press, and this gesture wins, |
622 | /// [onSecondaryLongPressStart] will be called after this callback. Otherwise, |
623 | /// [onSecondaryLongPressCancel] will be called after this callback. |
624 | /// |
625 | /// See also: |
626 | /// |
627 | /// * [kSecondaryButton], the button this callback responds to. |
628 | /// * [onLongPressDown], a similar callback but for a secondary button. |
629 | /// * [onTertiaryLongPressDown], a similar callback but for a tertiary button. |
630 | /// * [LongPressGestureRecognizer.onSecondaryLongPressDown], which exposes |
631 | /// this callback at the gesture layer. |
632 | final GestureLongPressDownCallback? onSecondaryLongPressDown; |
633 | |
634 | /// A pointer that previously triggered [onSecondaryLongPressDown] will not |
635 | /// end up causing a long-press. |
636 | /// |
637 | /// This triggers once the gesture loses if [onSecondaryLongPressDown] has |
638 | /// previously been triggered. |
639 | /// |
640 | /// If the user completed the long-press, and the gesture won, then |
641 | /// [onSecondaryLongPressStart] and [onSecondaryLongPress] are called instead. |
642 | /// |
643 | /// See also: |
644 | /// |
645 | /// * [kSecondaryButton], the button this callback responds to. |
646 | /// * [LongPressGestureRecognizer.onSecondaryLongPressCancel], which exposes |
647 | /// this callback at the gesture layer. |
648 | final GestureLongPressCancelCallback? onSecondaryLongPressCancel; |
649 | |
650 | /// Called when a long press gesture with a secondary button has been |
651 | /// recognized. |
652 | /// |
653 | /// Triggered when a pointer has remained in contact with the screen at the |
654 | /// same location for a long period of time. |
655 | /// |
656 | /// This is equivalent to (and is called immediately after) |
657 | /// [onSecondaryLongPressStart]. The only difference between the two is that |
658 | /// this callback does not contain details of the position at which the |
659 | /// pointer initially contacted the screen. |
660 | /// |
661 | /// See also: |
662 | /// |
663 | /// * [kSecondaryButton], the button this callback responds to. |
664 | /// * [LongPressGestureRecognizer.onSecondaryLongPress], which exposes |
665 | /// this callback at the gesture layer. |
666 | final GestureLongPressCallback? onSecondaryLongPress; |
667 | |
668 | /// Called when a long press gesture with a secondary button has been |
669 | /// recognized. |
670 | /// |
671 | /// Triggered when a pointer has remained in contact with the screen at the |
672 | /// same location for a long period of time. |
673 | /// |
674 | /// This is equivalent to (and is called immediately before) |
675 | /// [onSecondaryLongPress]. The only difference between the two is that this |
676 | /// callback contains details of the position at which the pointer initially |
677 | /// contacted the screen, whereas [onSecondaryLongPress] does not. |
678 | /// |
679 | /// See also: |
680 | /// |
681 | /// * [kSecondaryButton], the button this callback responds to. |
682 | /// * [LongPressGestureRecognizer.onSecondaryLongPressStart], which exposes |
683 | /// this callback at the gesture layer. |
684 | final GestureLongPressStartCallback? onSecondaryLongPressStart; |
685 | |
686 | /// A pointer has been drag-moved after a long press with a secondary button. |
687 | /// |
688 | /// See also: |
689 | /// |
690 | /// * [kSecondaryButton], the button this callback responds to. |
691 | /// * [LongPressGestureRecognizer.onSecondaryLongPressMoveUpdate], which exposes |
692 | /// this callback at the gesture layer. |
693 | final GestureLongPressMoveUpdateCallback? onSecondaryLongPressMoveUpdate; |
694 | |
695 | /// A pointer that has triggered a long-press with a secondary button has |
696 | /// stopped contacting the screen. |
697 | /// |
698 | /// This is equivalent to (and is called immediately after) |
699 | /// [onSecondaryLongPressEnd]. The only difference between the two is that |
700 | /// this callback does not contain details of the state of the pointer when |
701 | /// it stopped contacting the screen. |
702 | /// |
703 | /// See also: |
704 | /// |
705 | /// * [kSecondaryButton], the button this callback responds to. |
706 | /// * [LongPressGestureRecognizer.onSecondaryLongPressUp], which exposes |
707 | /// this callback at the gesture layer. |
708 | final GestureLongPressUpCallback? onSecondaryLongPressUp; |
709 | |
710 | /// A pointer that has triggered a long-press with a secondary button has |
711 | /// stopped contacting the screen. |
712 | /// |
713 | /// This is equivalent to (and is called immediately before) |
714 | /// [onSecondaryLongPressUp]. The only difference between the two is that |
715 | /// this callback contains details of the state of the pointer when it |
716 | /// stopped contacting the screen, whereas [onSecondaryLongPressUp] does not. |
717 | /// |
718 | /// See also: |
719 | /// |
720 | /// * [kSecondaryButton], the button this callback responds to. |
721 | /// * [LongPressGestureRecognizer.onSecondaryLongPressEnd], which exposes |
722 | /// this callback at the gesture layer. |
723 | final GestureLongPressEndCallback? onSecondaryLongPressEnd; |
724 | |
725 | /// The pointer has contacted the screen with a tertiary button, which might |
726 | /// be the start of a long-press. |
727 | /// |
728 | /// This triggers after the pointer down event. |
729 | /// |
730 | /// If the user completes the long-press, and this gesture wins, |
731 | /// [onTertiaryLongPressStart] will be called after this callback. Otherwise, |
732 | /// [onTertiaryLongPressCancel] will be called after this callback. |
733 | /// |
734 | /// See also: |
735 | /// |
736 | /// * [kTertiaryButton], the button this callback responds to. |
737 | /// * [onLongPressDown], a similar callback but for a primary button. |
738 | /// * [onSecondaryLongPressDown], a similar callback but for a secondary button. |
739 | /// * [LongPressGestureRecognizer.onTertiaryLongPressDown], which exposes |
740 | /// this callback at the gesture layer. |
741 | final GestureLongPressDownCallback? onTertiaryLongPressDown; |
742 | |
743 | /// A pointer that previously triggered [onTertiaryLongPressDown] will not |
744 | /// end up causing a long-press. |
745 | /// |
746 | /// This triggers once the gesture loses if [onTertiaryLongPressDown] has |
747 | /// previously been triggered. |
748 | /// |
749 | /// If the user completed the long-press, and the gesture won, then |
750 | /// [onTertiaryLongPressStart] and [onTertiaryLongPress] are called instead. |
751 | /// |
752 | /// See also: |
753 | /// |
754 | /// * [kTertiaryButton], the button this callback responds to. |
755 | /// * [LongPressGestureRecognizer.onTertiaryLongPressCancel], which exposes |
756 | /// this callback at the gesture layer. |
757 | final GestureLongPressCancelCallback? onTertiaryLongPressCancel; |
758 | |
759 | /// Called when a long press gesture with a tertiary button has been |
760 | /// recognized. |
761 | /// |
762 | /// Triggered when a pointer has remained in contact with the screen at the |
763 | /// same location for a long period of time. |
764 | /// |
765 | /// This is equivalent to (and is called immediately after) |
766 | /// [onTertiaryLongPressStart]. The only difference between the two is that |
767 | /// this callback does not contain details of the position at which the |
768 | /// pointer initially contacted the screen. |
769 | /// |
770 | /// See also: |
771 | /// |
772 | /// * [kTertiaryButton], the button this callback responds to. |
773 | /// * [LongPressGestureRecognizer.onTertiaryLongPress], which exposes |
774 | /// this callback at the gesture layer. |
775 | final GestureLongPressCallback? onTertiaryLongPress; |
776 | |
777 | /// Called when a long press gesture with a tertiary button has been |
778 | /// recognized. |
779 | /// |
780 | /// Triggered when a pointer has remained in contact with the screen at the |
781 | /// same location for a long period of time. |
782 | /// |
783 | /// This is equivalent to (and is called immediately before) |
784 | /// [onTertiaryLongPress]. The only difference between the two is that this |
785 | /// callback contains details of the position at which the pointer initially |
786 | /// contacted the screen, whereas [onTertiaryLongPress] does not. |
787 | /// |
788 | /// See also: |
789 | /// |
790 | /// * [kTertiaryButton], the button this callback responds to. |
791 | /// * [LongPressGestureRecognizer.onTertiaryLongPressStart], which exposes |
792 | /// this callback at the gesture layer. |
793 | final GestureLongPressStartCallback? onTertiaryLongPressStart; |
794 | |
795 | /// A pointer has been drag-moved after a long press with a tertiary button. |
796 | /// |
797 | /// See also: |
798 | /// |
799 | /// * [kTertiaryButton], the button this callback responds to. |
800 | /// * [LongPressGestureRecognizer.onTertiaryLongPressMoveUpdate], which exposes |
801 | /// this callback at the gesture layer. |
802 | final GestureLongPressMoveUpdateCallback? onTertiaryLongPressMoveUpdate; |
803 | |
804 | /// A pointer that has triggered a long-press with a tertiary button has |
805 | /// stopped contacting the screen. |
806 | /// |
807 | /// This is equivalent to (and is called immediately after) |
808 | /// [onTertiaryLongPressEnd]. The only difference between the two is that |
809 | /// this callback does not contain details of the state of the pointer when |
810 | /// it stopped contacting the screen. |
811 | /// |
812 | /// See also: |
813 | /// |
814 | /// * [kTertiaryButton], the button this callback responds to. |
815 | /// * [LongPressGestureRecognizer.onTertiaryLongPressUp], which exposes |
816 | /// this callback at the gesture layer. |
817 | final GestureLongPressUpCallback? onTertiaryLongPressUp; |
818 | |
819 | /// A pointer that has triggered a long-press with a tertiary button has |
820 | /// stopped contacting the screen. |
821 | /// |
822 | /// This is equivalent to (and is called immediately before) |
823 | /// [onTertiaryLongPressUp]. The only difference between the two is that |
824 | /// this callback contains details of the state of the pointer when it |
825 | /// stopped contacting the screen, whereas [onTertiaryLongPressUp] does not. |
826 | /// |
827 | /// See also: |
828 | /// |
829 | /// * [kTertiaryButton], the button this callback responds to. |
830 | /// * [LongPressGestureRecognizer.onTertiaryLongPressEnd], which exposes |
831 | /// this callback at the gesture layer. |
832 | final GestureLongPressEndCallback? onTertiaryLongPressEnd; |
833 | |
834 | /// A pointer has contacted the screen with a primary button and might begin |
835 | /// to move vertically. |
836 | /// |
837 | /// See also: |
838 | /// |
839 | /// * [kPrimaryButton], the button this callback responds to. |
840 | final GestureDragDownCallback? onVerticalDragDown; |
841 | |
842 | /// A pointer has contacted the screen with a primary button and has begun to |
843 | /// move vertically. |
844 | /// |
845 | /// See also: |
846 | /// |
847 | /// * [kPrimaryButton], the button this callback responds to. |
848 | final GestureDragStartCallback? onVerticalDragStart; |
849 | |
850 | /// A pointer that is in contact with the screen with a primary button and |
851 | /// moving vertically has moved in the vertical direction. |
852 | /// |
853 | /// See also: |
854 | /// |
855 | /// * [kPrimaryButton], the button this callback responds to. |
856 | final GestureDragUpdateCallback? onVerticalDragUpdate; |
857 | |
858 | /// A pointer that was previously in contact with the screen with a primary |
859 | /// button and moving vertically is no longer in contact with the screen and |
860 | /// was moving at a specific velocity when it stopped contacting the screen. |
861 | /// |
862 | /// See also: |
863 | /// |
864 | /// * [kPrimaryButton], the button this callback responds to. |
865 | final GestureDragEndCallback? onVerticalDragEnd; |
866 | |
867 | /// The pointer that previously triggered [onVerticalDragDown] did not |
868 | /// complete. |
869 | /// |
870 | /// See also: |
871 | /// |
872 | /// * [kPrimaryButton], the button this callback responds to. |
873 | final GestureDragCancelCallback? onVerticalDragCancel; |
874 | |
875 | /// A pointer has contacted the screen with a primary button and might begin |
876 | /// to move horizontally. |
877 | /// |
878 | /// See also: |
879 | /// |
880 | /// * [kPrimaryButton], the button this callback responds to. |
881 | final GestureDragDownCallback? onHorizontalDragDown; |
882 | |
883 | /// A pointer has contacted the screen with a primary button and has begun to |
884 | /// move horizontally. |
885 | /// |
886 | /// See also: |
887 | /// |
888 | /// * [kPrimaryButton], the button this callback responds to. |
889 | final GestureDragStartCallback? onHorizontalDragStart; |
890 | |
891 | /// A pointer that is in contact with the screen with a primary button and |
892 | /// moving horizontally has moved in the horizontal direction. |
893 | /// |
894 | /// See also: |
895 | /// |
896 | /// * [kPrimaryButton], the button this callback responds to. |
897 | final GestureDragUpdateCallback? onHorizontalDragUpdate; |
898 | |
899 | /// A pointer that was previously in contact with the screen with a primary |
900 | /// button and moving horizontally is no longer in contact with the screen and |
901 | /// was moving at a specific velocity when it stopped contacting the screen. |
902 | /// |
903 | /// See also: |
904 | /// |
905 | /// * [kPrimaryButton], the button this callback responds to. |
906 | final GestureDragEndCallback? onHorizontalDragEnd; |
907 | |
908 | /// The pointer that previously triggered [onHorizontalDragDown] did not |
909 | /// complete. |
910 | /// |
911 | /// See also: |
912 | /// |
913 | /// * [kPrimaryButton], the button this callback responds to. |
914 | final GestureDragCancelCallback? onHorizontalDragCancel; |
915 | |
916 | /// A pointer has contacted the screen with a primary button and might begin |
917 | /// to move. |
918 | /// |
919 | /// See also: |
920 | /// |
921 | /// * [kPrimaryButton], the button this callback responds to. |
922 | final GestureDragDownCallback? onPanDown; |
923 | |
924 | /// A pointer has contacted the screen with a primary button and has begun to |
925 | /// move. |
926 | /// |
927 | /// See also: |
928 | /// |
929 | /// * [kPrimaryButton], the button this callback responds to. |
930 | final GestureDragStartCallback? onPanStart; |
931 | |
932 | /// A pointer that is in contact with the screen with a primary button and |
933 | /// moving has moved again. |
934 | /// |
935 | /// See also: |
936 | /// |
937 | /// * [kPrimaryButton], the button this callback responds to. |
938 | final GestureDragUpdateCallback? onPanUpdate; |
939 | |
940 | /// A pointer that was previously in contact with the screen with a primary |
941 | /// button and moving is no longer in contact with the screen and was moving |
942 | /// at a specific velocity when it stopped contacting the screen. |
943 | /// |
944 | /// See also: |
945 | /// |
946 | /// * [kPrimaryButton], the button this callback responds to. |
947 | final GestureDragEndCallback? onPanEnd; |
948 | |
949 | /// The pointer that previously triggered [onPanDown] did not complete. |
950 | /// |
951 | /// See also: |
952 | /// |
953 | /// * [kPrimaryButton], the button this callback responds to. |
954 | final GestureDragCancelCallback? onPanCancel; |
955 | |
956 | /// The pointers in contact with the screen have established a focal point and |
957 | /// initial scale of 1.0. |
958 | final GestureScaleStartCallback? onScaleStart; |
959 | |
960 | /// The pointers in contact with the screen have indicated a new focal point |
961 | /// and/or scale. |
962 | final GestureScaleUpdateCallback? onScaleUpdate; |
963 | |
964 | /// The pointers are no longer in contact with the screen. |
965 | final GestureScaleEndCallback? onScaleEnd; |
966 | |
967 | /// The pointer is in contact with the screen and has pressed with sufficient |
968 | /// force to initiate a force press. The amount of force is at least |
969 | /// [ForcePressGestureRecognizer.startPressure]. |
970 | /// |
971 | /// This callback will only be fired on devices with pressure |
972 | /// detecting screens. |
973 | final GestureForcePressStartCallback? onForcePressStart; |
974 | |
975 | /// The pointer is in contact with the screen and has pressed with the maximum |
976 | /// force. The amount of force is at least |
977 | /// [ForcePressGestureRecognizer.peakPressure]. |
978 | /// |
979 | /// This callback will only be fired on devices with pressure |
980 | /// detecting screens. |
981 | final GestureForcePressPeakCallback? onForcePressPeak; |
982 | |
983 | /// A pointer is in contact with the screen, has previously passed the |
984 | /// [ForcePressGestureRecognizer.startPressure] and is either moving on the |
985 | /// plane of the screen, pressing the screen with varying forces or both |
986 | /// simultaneously. |
987 | /// |
988 | /// This callback will only be fired on devices with pressure |
989 | /// detecting screens. |
990 | final GestureForcePressUpdateCallback? onForcePressUpdate; |
991 | |
992 | /// The pointer tracked by [onForcePressStart] is no longer in contact with the screen. |
993 | /// |
994 | /// This callback will only be fired on devices with pressure |
995 | /// detecting screens. |
996 | final GestureForcePressEndCallback? onForcePressEnd; |
997 | |
998 | /// How this gesture detector should behave during hit testing when deciding |
999 | /// how the hit test propagates to children and whether to consider targets |
1000 | /// behind this one. |
1001 | /// |
1002 | /// This defaults to [HitTestBehavior.deferToChild] if [child] is not null and |
1003 | /// [HitTestBehavior.translucent] if child is null. |
1004 | /// |
1005 | /// See [HitTestBehavior] for the allowed values and their meanings. |
1006 | final HitTestBehavior? behavior; |
1007 | |
1008 | /// Whether to exclude these gestures from the semantics tree. For |
1009 | /// example, the long-press gesture for showing a tooltip is |
1010 | /// excluded because the tooltip itself is included in the semantics |
1011 | /// tree directly and so having a gesture to show it would result in |
1012 | /// duplication of information. |
1013 | final bool excludeFromSemantics; |
1014 | |
1015 | /// Determines the way that drag start behavior is handled. |
1016 | /// |
1017 | /// If set to [DragStartBehavior.start], gesture drag behavior will |
1018 | /// begin at the position where the drag gesture won the arena. If set to |
1019 | /// [DragStartBehavior.down] it will begin at the position where a down event |
1020 | /// is first detected. |
1021 | /// |
1022 | /// In general, setting this to [DragStartBehavior.start] will make drag |
1023 | /// animation smoother and setting it to [DragStartBehavior.down] will make |
1024 | /// drag behavior feel slightly more reactive. |
1025 | /// |
1026 | /// By default, the drag start behavior is [DragStartBehavior.start]. |
1027 | /// |
1028 | /// Only the [DragGestureRecognizer.onStart] callbacks for the |
1029 | /// [VerticalDragGestureRecognizer], [HorizontalDragGestureRecognizer] and |
1030 | /// [PanGestureRecognizer] are affected by this setting. |
1031 | /// |
1032 | /// See also: |
1033 | /// |
1034 | /// * [DragGestureRecognizer.dragStartBehavior], which gives an example for the different behaviors. |
1035 | final DragStartBehavior dragStartBehavior; |
1036 | |
1037 | /// The kind of devices that are allowed to be recognized. |
1038 | /// |
1039 | /// If set to null, events from all device types will be recognized. Defaults to null. |
1040 | final Set<PointerDeviceKind>? supportedDevices; |
1041 | |
1042 | /// {@macro flutter.gestures.scale.trackpadScrollCausesScale} |
1043 | final bool trackpadScrollCausesScale; |
1044 | |
1045 | /// {@macro flutter.gestures.scale.trackpadScrollToScaleFactor} |
1046 | final Offset trackpadScrollToScaleFactor; |
1047 | |
1048 | @override |
1049 | Widget build(BuildContext context) { |
1050 | final Map<Type, GestureRecognizerFactory> gestures = <Type, GestureRecognizerFactory>{}; |
1051 | final DeviceGestureSettings? gestureSettings = MediaQuery.maybeGestureSettingsOf(context); |
1052 | final ScrollBehavior configuration = ScrollConfiguration.of(context); |
1053 | |
1054 | if (onTapDown != null || |
1055 | onTapUp != null || |
1056 | onTap != null || |
1057 | onTapCancel != null || |
1058 | onSecondaryTap != null || |
1059 | onSecondaryTapDown != null || |
1060 | onSecondaryTapUp != null || |
1061 | onSecondaryTapCancel != null || |
1062 | onTertiaryTapDown != null || |
1063 | onTertiaryTapUp != null || |
1064 | onTertiaryTapCancel != null) { |
1065 | gestures[TapGestureRecognizer] = GestureRecognizerFactoryWithHandlers<TapGestureRecognizer>( |
1066 | () => TapGestureRecognizer(debugOwner: this, supportedDevices: supportedDevices), |
1067 | (TapGestureRecognizer instance) { |
1068 | instance |
1069 | ..onTapDown = onTapDown |
1070 | ..onTapUp = onTapUp |
1071 | ..onTap = onTap |
1072 | ..onTapCancel = onTapCancel |
1073 | ..onSecondaryTap = onSecondaryTap |
1074 | ..onSecondaryTapDown = onSecondaryTapDown |
1075 | ..onSecondaryTapUp = onSecondaryTapUp |
1076 | ..onSecondaryTapCancel = onSecondaryTapCancel |
1077 | ..onTertiaryTapDown = onTertiaryTapDown |
1078 | ..onTertiaryTapUp = onTertiaryTapUp |
1079 | ..onTertiaryTapCancel = onTertiaryTapCancel |
1080 | ..gestureSettings = gestureSettings |
1081 | ..supportedDevices = supportedDevices; |
1082 | }, |
1083 | ); |
1084 | } |
1085 | |
1086 | if (onDoubleTap != null || onDoubleTapDown != null || onDoubleTapCancel != null) { |
1087 | gestures[DoubleTapGestureRecognizer] = |
1088 | GestureRecognizerFactoryWithHandlers<DoubleTapGestureRecognizer>( |
1089 | () => DoubleTapGestureRecognizer(debugOwner: this, supportedDevices: supportedDevices), |
1090 | (DoubleTapGestureRecognizer instance) { |
1091 | instance |
1092 | ..onDoubleTapDown = onDoubleTapDown |
1093 | ..onDoubleTap = onDoubleTap |
1094 | ..onDoubleTapCancel = onDoubleTapCancel |
1095 | ..gestureSettings = gestureSettings |
1096 | ..supportedDevices = supportedDevices; |
1097 | }, |
1098 | ); |
1099 | } |
1100 | |
1101 | if (onLongPressDown != null || |
1102 | onLongPressCancel != null || |
1103 | onLongPress != null || |
1104 | onLongPressStart != null || |
1105 | onLongPressMoveUpdate != null || |
1106 | onLongPressUp != null || |
1107 | onLongPressEnd != null || |
1108 | onSecondaryLongPressDown != null || |
1109 | onSecondaryLongPressCancel != null || |
1110 | onSecondaryLongPress != null || |
1111 | onSecondaryLongPressStart != null || |
1112 | onSecondaryLongPressMoveUpdate != null || |
1113 | onSecondaryLongPressUp != null || |
1114 | onSecondaryLongPressEnd != null || |
1115 | onTertiaryLongPressDown != null || |
1116 | onTertiaryLongPressCancel != null || |
1117 | onTertiaryLongPress != null || |
1118 | onTertiaryLongPressStart != null || |
1119 | onTertiaryLongPressMoveUpdate != null || |
1120 | onTertiaryLongPressUp != null || |
1121 | onTertiaryLongPressEnd != null) { |
1122 | gestures[LongPressGestureRecognizer] = |
1123 | GestureRecognizerFactoryWithHandlers<LongPressGestureRecognizer>( |
1124 | () => LongPressGestureRecognizer(debugOwner: this, supportedDevices: supportedDevices), |
1125 | (LongPressGestureRecognizer instance) { |
1126 | instance |
1127 | ..onLongPressDown = onLongPressDown |
1128 | ..onLongPressCancel = onLongPressCancel |
1129 | ..onLongPress = onLongPress |
1130 | ..onLongPressStart = onLongPressStart |
1131 | ..onLongPressMoveUpdate = onLongPressMoveUpdate |
1132 | ..onLongPressUp = onLongPressUp |
1133 | ..onLongPressEnd = onLongPressEnd |
1134 | ..onSecondaryLongPressDown = onSecondaryLongPressDown |
1135 | ..onSecondaryLongPressCancel = onSecondaryLongPressCancel |
1136 | ..onSecondaryLongPress = onSecondaryLongPress |
1137 | ..onSecondaryLongPressStart = onSecondaryLongPressStart |
1138 | ..onSecondaryLongPressMoveUpdate = onSecondaryLongPressMoveUpdate |
1139 | ..onSecondaryLongPressUp = onSecondaryLongPressUp |
1140 | ..onSecondaryLongPressEnd = onSecondaryLongPressEnd |
1141 | ..onTertiaryLongPressDown = onTertiaryLongPressDown |
1142 | ..onTertiaryLongPressCancel = onTertiaryLongPressCancel |
1143 | ..onTertiaryLongPress = onTertiaryLongPress |
1144 | ..onTertiaryLongPressStart = onTertiaryLongPressStart |
1145 | ..onTertiaryLongPressMoveUpdate = onTertiaryLongPressMoveUpdate |
1146 | ..onTertiaryLongPressUp = onTertiaryLongPressUp |
1147 | ..onTertiaryLongPressEnd = onTertiaryLongPressEnd |
1148 | ..gestureSettings = gestureSettings |
1149 | ..supportedDevices = supportedDevices; |
1150 | }, |
1151 | ); |
1152 | } |
1153 | |
1154 | if (onVerticalDragDown != null || |
1155 | onVerticalDragStart != null || |
1156 | onVerticalDragUpdate != null || |
1157 | onVerticalDragEnd != null || |
1158 | onVerticalDragCancel != null) { |
1159 | gestures[VerticalDragGestureRecognizer] = |
1160 | GestureRecognizerFactoryWithHandlers<VerticalDragGestureRecognizer>( |
1161 | () => |
1162 | VerticalDragGestureRecognizer(debugOwner: this, supportedDevices: supportedDevices), |
1163 | (VerticalDragGestureRecognizer instance) { |
1164 | instance |
1165 | ..onDown = onVerticalDragDown |
1166 | ..onStart = onVerticalDragStart |
1167 | ..onUpdate = onVerticalDragUpdate |
1168 | ..onEnd = onVerticalDragEnd |
1169 | ..onCancel = onVerticalDragCancel |
1170 | ..dragStartBehavior = dragStartBehavior |
1171 | ..multitouchDragStrategy = configuration.getMultitouchDragStrategy(context) |
1172 | ..gestureSettings = gestureSettings |
1173 | ..supportedDevices = supportedDevices; |
1174 | }, |
1175 | ); |
1176 | } |
1177 | |
1178 | if (onHorizontalDragDown != null || |
1179 | onHorizontalDragStart != null || |
1180 | onHorizontalDragUpdate != null || |
1181 | onHorizontalDragEnd != null || |
1182 | onHorizontalDragCancel != null) { |
1183 | gestures[HorizontalDragGestureRecognizer] = GestureRecognizerFactoryWithHandlers< |
1184 | HorizontalDragGestureRecognizer |
1185 | >( |
1186 | () => HorizontalDragGestureRecognizer(debugOwner: this, supportedDevices: supportedDevices), |
1187 | (HorizontalDragGestureRecognizer instance) { |
1188 | instance |
1189 | ..onDown = onHorizontalDragDown |
1190 | ..onStart = onHorizontalDragStart |
1191 | ..onUpdate = onHorizontalDragUpdate |
1192 | ..onEnd = onHorizontalDragEnd |
1193 | ..onCancel = onHorizontalDragCancel |
1194 | ..dragStartBehavior = dragStartBehavior |
1195 | ..multitouchDragStrategy = configuration.getMultitouchDragStrategy(context) |
1196 | ..gestureSettings = gestureSettings |
1197 | ..supportedDevices = supportedDevices; |
1198 | }, |
1199 | ); |
1200 | } |
1201 | |
1202 | if (onPanDown != null || |
1203 | onPanStart != null || |
1204 | onPanUpdate != null || |
1205 | onPanEnd != null || |
1206 | onPanCancel != null) { |
1207 | gestures[PanGestureRecognizer] = GestureRecognizerFactoryWithHandlers<PanGestureRecognizer>( |
1208 | () => PanGestureRecognizer(debugOwner: this, supportedDevices: supportedDevices), |
1209 | (PanGestureRecognizer instance) { |
1210 | instance |
1211 | ..onDown = onPanDown |
1212 | ..onStart = onPanStart |
1213 | ..onUpdate = onPanUpdate |
1214 | ..onEnd = onPanEnd |
1215 | ..onCancel = onPanCancel |
1216 | ..dragStartBehavior = dragStartBehavior |
1217 | ..multitouchDragStrategy = configuration.getMultitouchDragStrategy(context) |
1218 | ..gestureSettings = gestureSettings |
1219 | ..supportedDevices = supportedDevices; |
1220 | }, |
1221 | ); |
1222 | } |
1223 | |
1224 | if (onScaleStart != null || onScaleUpdate != null || onScaleEnd != null) { |
1225 | gestures[ScaleGestureRecognizer] = |
1226 | GestureRecognizerFactoryWithHandlers<ScaleGestureRecognizer>( |
1227 | () => ScaleGestureRecognizer(debugOwner: this, supportedDevices: supportedDevices), |
1228 | (ScaleGestureRecognizer instance) { |
1229 | instance |
1230 | ..onStart = onScaleStart |
1231 | ..onUpdate = onScaleUpdate |
1232 | ..onEnd = onScaleEnd |
1233 | ..dragStartBehavior = dragStartBehavior |
1234 | ..gestureSettings = gestureSettings |
1235 | ..trackpadScrollCausesScale = trackpadScrollCausesScale |
1236 | ..trackpadScrollToScaleFactor = trackpadScrollToScaleFactor |
1237 | ..supportedDevices = supportedDevices; |
1238 | }, |
1239 | ); |
1240 | } |
1241 | |
1242 | if (onForcePressStart != null || |
1243 | onForcePressPeak != null || |
1244 | onForcePressUpdate != null || |
1245 | onForcePressEnd != null) { |
1246 | gestures[ForcePressGestureRecognizer] = |
1247 | GestureRecognizerFactoryWithHandlers<ForcePressGestureRecognizer>( |
1248 | () => ForcePressGestureRecognizer(debugOwner: this, supportedDevices: supportedDevices), |
1249 | (ForcePressGestureRecognizer instance) { |
1250 | instance |
1251 | ..onStart = onForcePressStart |
1252 | ..onPeak = onForcePressPeak |
1253 | ..onUpdate = onForcePressUpdate |
1254 | ..onEnd = onForcePressEnd |
1255 | ..gestureSettings = gestureSettings |
1256 | ..supportedDevices = supportedDevices; |
1257 | }, |
1258 | ); |
1259 | } |
1260 | |
1261 | return RawGestureDetector( |
1262 | gestures: gestures, |
1263 | behavior: behavior, |
1264 | excludeFromSemantics: excludeFromSemantics, |
1265 | child: child, |
1266 | ); |
1267 | } |
1268 | |
1269 | @override |
1270 | void debugFillProperties(DiagnosticPropertiesBuilder properties) { |
1271 | super.debugFillProperties(properties); |
1272 | properties.add(EnumProperty<DragStartBehavior>('startBehavior', dragStartBehavior)); |
1273 | } |
1274 | } |
1275 | |
1276 | /// A widget that detects gestures described by the given gesture |
1277 | /// factories. |
1278 | /// |
1279 | /// For common gestures, use a [GestureDetector]. |
1280 | /// [RawGestureDetector] is useful primarily when developing your |
1281 | /// own gesture recognizers. |
1282 | /// |
1283 | /// Configuring the gesture recognizers requires a carefully constructed map, as |
1284 | /// described in [gestures] and as shown in the example below. |
1285 | /// |
1286 | /// {@tool snippet} |
1287 | /// |
1288 | /// This example shows how to hook up a [TapGestureRecognizer]. It assumes that |
1289 | /// the code is being used inside a [State] object with a `_last` field that is |
1290 | /// then displayed as the child of the gesture detector. |
1291 | /// |
1292 | /// ```dart |
1293 | /// RawGestureDetector( |
1294 | /// gestures: <Type, GestureRecognizerFactory>{ |
1295 | /// TapGestureRecognizer: GestureRecognizerFactoryWithHandlers<TapGestureRecognizer>( |
1296 | /// () => TapGestureRecognizer(), |
1297 | /// (TapGestureRecognizer instance) { |
1298 | /// instance |
1299 | /// ..onTapDown = (TapDownDetails details) { setState(() { _last = 'down'; }); } |
1300 | /// ..onTapUp = (TapUpDetails details) { setState(() { _last = 'up'; }); } |
1301 | /// ..onTap = () { setState(() { _last = 'tap'; }); } |
1302 | /// ..onTapCancel = () { setState(() { _last = 'cancel'; }); }; |
1303 | /// }, |
1304 | /// ), |
1305 | /// }, |
1306 | /// child: Container(width: 300.0, height: 300.0, color: Colors.yellow, child: Text(_last)), |
1307 | /// ) |
1308 | /// ``` |
1309 | /// {@end-tool} |
1310 | /// |
1311 | /// See also: |
1312 | /// |
1313 | /// * [GestureDetector], a less flexible but much simpler widget that does the same thing. |
1314 | /// * [Listener], a widget that reports raw pointer events. |
1315 | /// * [GestureRecognizer], the class that you extend to create a custom gesture recognizer. |
1316 | class RawGestureDetector extends StatefulWidget { |
1317 | /// Creates a widget that detects gestures. |
1318 | /// |
1319 | /// Gesture detectors can contribute semantic information to the tree that is |
1320 | /// used by assistive technology. The behavior can be configured by |
1321 | /// [semantics], or disabled with [excludeFromSemantics]. |
1322 | const RawGestureDetector({ |
1323 | super.key, |
1324 | this.child, |
1325 | this.gestures = const <Type, GestureRecognizerFactory>{}, |
1326 | this.behavior, |
1327 | this.excludeFromSemantics = false, |
1328 | this.semantics, |
1329 | }); |
1330 | |
1331 | /// The widget below this widget in the tree. |
1332 | /// |
1333 | /// {@macro flutter.widgets.ProxyWidget.child} |
1334 | final Widget? child; |
1335 | |
1336 | /// The gestures that this widget will attempt to recognize. |
1337 | /// |
1338 | /// This should be a map from [GestureRecognizer] subclasses to |
1339 | /// [GestureRecognizerFactory] subclasses specialized with the same type. |
1340 | /// |
1341 | /// This value can be late-bound at layout time using |
1342 | /// [RawGestureDetectorState.replaceGestureRecognizers]. |
1343 | final Map<Type, GestureRecognizerFactory> gestures; |
1344 | |
1345 | /// How this gesture detector should behave during hit testing. |
1346 | /// |
1347 | /// This defaults to [HitTestBehavior.deferToChild] if [child] is not null and |
1348 | /// [HitTestBehavior.translucent] if child is null. |
1349 | final HitTestBehavior? behavior; |
1350 | |
1351 | /// Whether to exclude these gestures from the semantics tree. For |
1352 | /// example, the long-press gesture for showing a tooltip is |
1353 | /// excluded because the tooltip itself is included in the semantics |
1354 | /// tree directly and so having a gesture to show it would result in |
1355 | /// duplication of information. |
1356 | final bool excludeFromSemantics; |
1357 | |
1358 | /// Describes the semantics notations that should be added to the underlying |
1359 | /// render object [RenderSemanticsGestureHandler]. |
1360 | /// |
1361 | /// It has no effect if [excludeFromSemantics] is true. |
1362 | /// |
1363 | /// When [semantics] is null, [RawGestureDetector] will fall back to a |
1364 | /// default delegate which checks if the detector owns certain gesture |
1365 | /// recognizers and calls their callbacks if they exist: |
1366 | /// |
1367 | /// * During a semantic tap, it calls [TapGestureRecognizer]'s |
1368 | /// `onTapDown`, `onTapUp`, and `onTap`. |
1369 | /// * During a semantic long press, it calls [LongPressGestureRecognizer]'s |
1370 | /// `onLongPressDown`, `onLongPressStart`, `onLongPress`, `onLongPressEnd` |
1371 | /// and `onLongPressUp`. |
1372 | /// * During a semantic horizontal drag, it calls [HorizontalDragGestureRecognizer]'s |
1373 | /// `onDown`, `onStart`, `onUpdate` and `onEnd`, then |
1374 | /// [PanGestureRecognizer]'s `onDown`, `onStart`, `onUpdate` and `onEnd`. |
1375 | /// * During a semantic vertical drag, it calls [VerticalDragGestureRecognizer]'s |
1376 | /// `onDown`, `onStart`, `onUpdate` and `onEnd`, then |
1377 | /// [PanGestureRecognizer]'s `onDown`, `onStart`, `onUpdate` and `onEnd`. |
1378 | /// |
1379 | /// {@tool snippet} |
1380 | /// This custom gesture detector listens to force presses, while also allows |
1381 | /// the same callback to be triggered by semantic long presses. |
1382 | /// |
1383 | /// ```dart |
1384 | /// class ForcePressGestureDetectorWithSemantics extends StatelessWidget { |
1385 | /// const ForcePressGestureDetectorWithSemantics({ |
1386 | /// super.key, |
1387 | /// required this.child, |
1388 | /// required this.onForcePress, |
1389 | /// }); |
1390 | /// |
1391 | /// final Widget child; |
1392 | /// final VoidCallback onForcePress; |
1393 | /// |
1394 | /// @override |
1395 | /// Widget build(BuildContext context) { |
1396 | /// return RawGestureDetector( |
1397 | /// gestures: <Type, GestureRecognizerFactory>{ |
1398 | /// ForcePressGestureRecognizer: GestureRecognizerFactoryWithHandlers<ForcePressGestureRecognizer>( |
1399 | /// () => ForcePressGestureRecognizer(debugOwner: this), |
1400 | /// (ForcePressGestureRecognizer instance) { |
1401 | /// instance.onStart = (_) => onForcePress(); |
1402 | /// } |
1403 | /// ), |
1404 | /// }, |
1405 | /// behavior: HitTestBehavior.opaque, |
1406 | /// semantics: _LongPressSemanticsDelegate(onForcePress), |
1407 | /// child: child, |
1408 | /// ); |
1409 | /// } |
1410 | /// } |
1411 | /// |
1412 | /// class _LongPressSemanticsDelegate extends SemanticsGestureDelegate { |
1413 | /// _LongPressSemanticsDelegate(this.onLongPress); |
1414 | /// |
1415 | /// VoidCallback onLongPress; |
1416 | /// |
1417 | /// @override |
1418 | /// void assignSemantics(RenderSemanticsGestureHandler renderObject) { |
1419 | /// renderObject.onLongPress = onLongPress; |
1420 | /// } |
1421 | /// } |
1422 | /// ``` |
1423 | /// {@end-tool} |
1424 | final SemanticsGestureDelegate? semantics; |
1425 | |
1426 | @override |
1427 | RawGestureDetectorState createState() => RawGestureDetectorState(); |
1428 | } |
1429 | |
1430 | /// State for a [RawGestureDetector]. |
1431 | class RawGestureDetectorState extends State<RawGestureDetector> { |
1432 | Map<Type, GestureRecognizer>? _recognizers = const <Type, GestureRecognizer>{}; |
1433 | SemanticsGestureDelegate? _semantics; |
1434 | |
1435 | @protected |
1436 | @override |
1437 | void initState() { |
1438 | super.initState(); |
1439 | _semantics = widget.semantics ?? _DefaultSemanticsGestureDelegate(this); |
1440 | _syncAll(widget.gestures); |
1441 | } |
1442 | |
1443 | @protected |
1444 | @override |
1445 | void didUpdateWidget(RawGestureDetector oldWidget) { |
1446 | super.didUpdateWidget(oldWidget); |
1447 | if (!(oldWidget.semantics == null && widget.semantics == null)) { |
1448 | _semantics = widget.semantics ?? _DefaultSemanticsGestureDelegate(this); |
1449 | } |
1450 | _syncAll(widget.gestures); |
1451 | } |
1452 | |
1453 | /// This method can be called after the build phase, during the |
1454 | /// layout of the nearest descendant [RenderObjectWidget] of the |
1455 | /// gesture detector, to update the list of active gesture |
1456 | /// recognizers. |
1457 | /// |
1458 | /// The typical use case is [Scrollable]s, which put their viewport |
1459 | /// in their gesture detector, and then need to know the dimensions |
1460 | /// of the viewport and the viewport's child to determine whether |
1461 | /// the gesture detector should be enabled. |
1462 | /// |
1463 | /// The argument should follow the same conventions as |
1464 | /// [RawGestureDetector.gestures]. It acts like a temporary replacement for |
1465 | /// that value until the next build. |
1466 | void replaceGestureRecognizers(Map<Type, GestureRecognizerFactory> gestures) { |
1467 | assert(() { |
1468 | if (!context.findRenderObject()!.owner!.debugDoingLayout) { |
1469 | throw FlutterError.fromParts(<DiagnosticsNode>[ |
1470 | ErrorSummary( |
1471 | 'Unexpected call to replaceGestureRecognizers() method of RawGestureDetectorState.', |
1472 | ), |
1473 | ErrorDescription( |
1474 | 'The replaceGestureRecognizers() method can only be called during the layout phase.', |
1475 | ), |
1476 | ErrorHint( |
1477 | 'To set the gesture recognizers at other times, trigger a new build using setState() ' |
1478 | 'and provide the new gesture recognizers as constructor arguments to the corresponding ' |
1479 | 'RawGestureDetector or GestureDetector object.', |
1480 | ), |
1481 | ]); |
1482 | } |
1483 | return true; |
1484 | }()); |
1485 | _syncAll(gestures); |
1486 | if (!widget.excludeFromSemantics) { |
1487 | final RenderSemanticsGestureHandler semanticsGestureHandler = |
1488 | context.findRenderObject()! as RenderSemanticsGestureHandler; |
1489 | _updateSemanticsForRenderObject(semanticsGestureHandler); |
1490 | } |
1491 | } |
1492 | |
1493 | /// This method can be called to filter the list of available semantic actions, |
1494 | /// after the render object was created. |
1495 | /// |
1496 | /// The actual filtering is happening in the next frame and a frame will be |
1497 | /// scheduled if non is pending. |
1498 | /// |
1499 | /// This is used by [Scrollable] to configure system accessibility tools so |
1500 | /// that they know in which direction a particular list can be scrolled. |
1501 | /// |
1502 | /// If this is never called, then the actions are not filtered. If the list of |
1503 | /// actions to filter changes, it must be called again. |
1504 | void replaceSemanticsActions(Set<SemanticsAction> actions) { |
1505 | if (widget.excludeFromSemantics) { |
1506 | return; |
1507 | } |
1508 | |
1509 | final RenderSemanticsGestureHandler? semanticsGestureHandler = |
1510 | context.findRenderObject() as RenderSemanticsGestureHandler?; |
1511 | assert(() { |
1512 | if (semanticsGestureHandler == null) { |
1513 | throw FlutterError( |
1514 | 'Unexpected call to replaceSemanticsActions() method of RawGestureDetectorState.\n' |
1515 | 'The replaceSemanticsActions() method can only be called after the RenderSemanticsGestureHandler has been created.', |
1516 | ); |
1517 | } |
1518 | return true; |
1519 | }()); |
1520 | |
1521 | semanticsGestureHandler!.validActions = |
1522 | actions; // will call _markNeedsSemanticsUpdate(), if required. |
1523 | } |
1524 | |
1525 | @protected |
1526 | @override |
1527 | void dispose() { |
1528 | for (final GestureRecognizer recognizer in _recognizers!.values) { |
1529 | recognizer.dispose(); |
1530 | } |
1531 | _recognizers = null; |
1532 | super.dispose(); |
1533 | } |
1534 | |
1535 | void _syncAll(Map<Type, GestureRecognizerFactory> gestures) { |
1536 | assert(_recognizers != null); |
1537 | final Map<Type, GestureRecognizer> oldRecognizers = _recognizers!; |
1538 | _recognizers = <Type, GestureRecognizer>{}; |
1539 | for (final Type type in gestures.keys) { |
1540 | assert(gestures[type] != null); |
1541 | assert(gestures[type]!._debugAssertTypeMatches(type)); |
1542 | assert(!_recognizers!.containsKey(type)); |
1543 | _recognizers![type] = oldRecognizers[type] ?? gestures[type]!.constructor(); |
1544 | assert( |
1545 | _recognizers![type].runtimeType == type, |
1546 | 'GestureRecognizerFactory of type$type created a GestureRecognizer of type${_recognizers![type].runtimeType} . The GestureRecognizerFactory must be specialized with the type of the class that it returns from its constructor method.', |
1547 | ); |
1548 | gestures[type]!.initializer(_recognizers![type]!); |
1549 | } |
1550 | for (final Type type in oldRecognizers.keys) { |
1551 | if (!_recognizers!.containsKey(type)) { |
1552 | oldRecognizers[type]!.dispose(); |
1553 | } |
1554 | } |
1555 | } |
1556 | |
1557 | void _handlePointerDown(PointerDownEvent event) { |
1558 | assert(_recognizers != null); |
1559 | for (final GestureRecognizer recognizer in _recognizers!.values) { |
1560 | recognizer.addPointer(event); |
1561 | } |
1562 | } |
1563 | |
1564 | void _handlePointerPanZoomStart(PointerPanZoomStartEvent event) { |
1565 | assert(_recognizers != null); |
1566 | for (final GestureRecognizer recognizer in _recognizers!.values) { |
1567 | recognizer.addPointerPanZoom(event); |
1568 | } |
1569 | } |
1570 | |
1571 | HitTestBehavior get _defaultBehavior { |
1572 | return widget.child == null ? HitTestBehavior.translucent : HitTestBehavior.deferToChild; |
1573 | } |
1574 | |
1575 | void _updateSemanticsForRenderObject(RenderSemanticsGestureHandler renderObject) { |
1576 | assert(!widget.excludeFromSemantics); |
1577 | assert(_semantics != null); |
1578 | _semantics!.assignSemantics(renderObject); |
1579 | } |
1580 | |
1581 | @protected |
1582 | @override |
1583 | Widget build(BuildContext context) { |
1584 | Widget result = Listener( |
1585 | onPointerDown: _handlePointerDown, |
1586 | onPointerPanZoomStart: _handlePointerPanZoomStart, |
1587 | behavior: widget.behavior ?? _defaultBehavior, |
1588 | child: widget.child, |
1589 | ); |
1590 | if (!widget.excludeFromSemantics) { |
1591 | result = _GestureSemantics( |
1592 | behavior: widget.behavior ?? _defaultBehavior, |
1593 | assignSemantics: _updateSemanticsForRenderObject, |
1594 | child: result, |
1595 | ); |
1596 | } |
1597 | return result; |
1598 | } |
1599 | |
1600 | @protected |
1601 | @override |
1602 | void debugFillProperties(DiagnosticPropertiesBuilder properties) { |
1603 | super.debugFillProperties(properties); |
1604 | if (_recognizers == null) { |
1605 | properties.add(DiagnosticsNode.message('DISPOSED')); |
1606 | } else { |
1607 | final List<String> gestures = |
1608 | _recognizers!.values |
1609 | .map<String>((GestureRecognizer recognizer) => recognizer.debugDescription) |
1610 | .toList(); |
1611 | properties.add(IterableProperty<String>('gestures', gestures, ifEmpty: '<none>')); |
1612 | properties.add( |
1613 | IterableProperty<GestureRecognizer>( |
1614 | 'recognizers', |
1615 | _recognizers!.values, |
1616 | level: DiagnosticLevel.fine, |
1617 | ), |
1618 | ); |
1619 | properties.add( |
1620 | DiagnosticsProperty<bool>( |
1621 | 'excludeFromSemantics', |
1622 | widget.excludeFromSemantics, |
1623 | defaultValue: false, |
1624 | ), |
1625 | ); |
1626 | if (!widget.excludeFromSemantics) { |
1627 | properties.add( |
1628 | DiagnosticsProperty<SemanticsGestureDelegate>( |
1629 | 'semantics', |
1630 | widget.semantics, |
1631 | defaultValue: null, |
1632 | ), |
1633 | ); |
1634 | } |
1635 | } |
1636 | properties.add(EnumProperty<HitTestBehavior>('behavior', widget.behavior, defaultValue: null)); |
1637 | } |
1638 | } |
1639 | |
1640 | typedef _AssignSemantics = void Function(RenderSemanticsGestureHandler); |
1641 | |
1642 | class _GestureSemantics extends SingleChildRenderObjectWidget { |
1643 | const _GestureSemantics({super.child, required this.behavior, required this.assignSemantics}); |
1644 | |
1645 | final HitTestBehavior behavior; |
1646 | final _AssignSemantics assignSemantics; |
1647 | |
1648 | @override |
1649 | RenderSemanticsGestureHandler createRenderObject(BuildContext context) { |
1650 | final RenderSemanticsGestureHandler renderObject = |
1651 | RenderSemanticsGestureHandler()..behavior = behavior; |
1652 | assignSemantics(renderObject); |
1653 | return renderObject; |
1654 | } |
1655 | |
1656 | @override |
1657 | void updateRenderObject(BuildContext context, RenderSemanticsGestureHandler renderObject) { |
1658 | renderObject.behavior = behavior; |
1659 | assignSemantics(renderObject); |
1660 | } |
1661 | } |
1662 | |
1663 | /// A base class that describes what semantics notations a [RawGestureDetector] |
1664 | /// should add to the render object [RenderSemanticsGestureHandler]. |
1665 | /// |
1666 | /// It is used to allow custom [GestureDetector]s to add semantics notations. |
1667 | abstract class SemanticsGestureDelegate { |
1668 | /// Create a delegate of gesture semantics. |
1669 | const SemanticsGestureDelegate(); |
1670 | |
1671 | /// Assigns semantics notations to the [RenderSemanticsGestureHandler] render |
1672 | /// object of the gesture detector. |
1673 | /// |
1674 | /// This method is called when the widget is created, updated, or during |
1675 | /// [RawGestureDetectorState.replaceGestureRecognizers]. |
1676 | void assignSemantics(RenderSemanticsGestureHandler renderObject); |
1677 | |
1678 | @override |
1679 | String toString() => '${objectRuntimeType(this, 'SemanticsGestureDelegate')} ()'; |
1680 | } |
1681 | |
1682 | // The default semantics delegate of [RawGestureDetector]. Its behavior is |
1683 | // described in [RawGestureDetector.semantics]. |
1684 | // |
1685 | // For readers who come here to learn how to write custom semantics delegates: |
1686 | // this is not a proper sample code. It has access to the detector state as well |
1687 | // as its private properties, which are inaccessible normally. It is designed |
1688 | // this way in order to work independently in a [RawGestureRecognizer] to |
1689 | // preserve existing behavior. |
1690 | // |
1691 | // Instead, a normal delegate will store callbacks as properties, and use them |
1692 | // in `assignSemantics`. |
1693 | class _DefaultSemanticsGestureDelegate extends SemanticsGestureDelegate { |
1694 | _DefaultSemanticsGestureDelegate(this.detectorState); |
1695 | |
1696 | final RawGestureDetectorState detectorState; |
1697 | |
1698 | @override |
1699 | void assignSemantics(RenderSemanticsGestureHandler renderObject) { |
1700 | assert(!detectorState.widget.excludeFromSemantics); |
1701 | final Map<Type, GestureRecognizer> recognizers = detectorState._recognizers!; |
1702 | renderObject |
1703 | ..onTap = _getTapHandler(recognizers) |
1704 | ..onLongPress = _getLongPressHandler(recognizers) |
1705 | ..onHorizontalDragUpdate = _getHorizontalDragUpdateHandler(recognizers) |
1706 | ..onVerticalDragUpdate = _getVerticalDragUpdateHandler(recognizers); |
1707 | } |
1708 | |
1709 | GestureTapCallback? _getTapHandler(Map<Type, GestureRecognizer> recognizers) { |
1710 | final TapGestureRecognizer? tap = recognizers[TapGestureRecognizer] as TapGestureRecognizer?; |
1711 | if (tap == null) { |
1712 | return null; |
1713 | } |
1714 | |
1715 | return () { |
1716 | tap.onTapDown?.call(TapDownDetails()); |
1717 | tap.onTapUp?.call(TapUpDetails(kind: PointerDeviceKind.unknown)); |
1718 | tap.onTap?.call(); |
1719 | }; |
1720 | } |
1721 | |
1722 | GestureLongPressCallback? _getLongPressHandler(Map<Type, GestureRecognizer> recognizers) { |
1723 | final LongPressGestureRecognizer? longPress = |
1724 | recognizers[LongPressGestureRecognizer] as LongPressGestureRecognizer?; |
1725 | if (longPress == null) { |
1726 | return null; |
1727 | } |
1728 | |
1729 | return () { |
1730 | longPress.onLongPressDown?.call(const LongPressDownDetails()); |
1731 | longPress.onLongPressStart?.call(const LongPressStartDetails()); |
1732 | longPress.onLongPress?.call(); |
1733 | longPress.onLongPressEnd?.call(const LongPressEndDetails()); |
1734 | longPress.onLongPressUp?.call(); |
1735 | }; |
1736 | } |
1737 | |
1738 | GestureDragUpdateCallback? _getHorizontalDragUpdateHandler( |
1739 | Map<Type, GestureRecognizer> recognizers, |
1740 | ) { |
1741 | final HorizontalDragGestureRecognizer? horizontal = |
1742 | recognizers[HorizontalDragGestureRecognizer] as HorizontalDragGestureRecognizer?; |
1743 | final PanGestureRecognizer? pan = recognizers[PanGestureRecognizer] as PanGestureRecognizer?; |
1744 | |
1745 | final GestureDragUpdateCallback? horizontalHandler = |
1746 | horizontal == null |
1747 | ? null |
1748 | : (DragUpdateDetails details) { |
1749 | horizontal.onDown?.call(DragDownDetails()); |
1750 | horizontal.onStart?.call(DragStartDetails()); |
1751 | horizontal.onUpdate?.call(details); |
1752 | horizontal.onEnd?.call(DragEndDetails(primaryVelocity: 0.0)); |
1753 | }; |
1754 | |
1755 | final GestureDragUpdateCallback? panHandler = |
1756 | pan == null |
1757 | ? null |
1758 | : (DragUpdateDetails details) { |
1759 | pan.onDown?.call(DragDownDetails()); |
1760 | pan.onStart?.call(DragStartDetails()); |
1761 | pan.onUpdate?.call(details); |
1762 | pan.onEnd?.call(DragEndDetails()); |
1763 | }; |
1764 | |
1765 | if (horizontalHandler == null && panHandler == null) { |
1766 | return null; |
1767 | } |
1768 | return (DragUpdateDetails details) { |
1769 | horizontalHandler?.call(details); |
1770 | panHandler?.call(details); |
1771 | }; |
1772 | } |
1773 | |
1774 | GestureDragUpdateCallback? _getVerticalDragUpdateHandler( |
1775 | Map<Type, GestureRecognizer> recognizers, |
1776 | ) { |
1777 | final VerticalDragGestureRecognizer? vertical = |
1778 | recognizers[VerticalDragGestureRecognizer] as VerticalDragGestureRecognizer?; |
1779 | final PanGestureRecognizer? pan = recognizers[PanGestureRecognizer] as PanGestureRecognizer?; |
1780 | |
1781 | final GestureDragUpdateCallback? verticalHandler = |
1782 | vertical == null |
1783 | ? null |
1784 | : (DragUpdateDetails details) { |
1785 | vertical.onDown?.call(DragDownDetails()); |
1786 | vertical.onStart?.call(DragStartDetails()); |
1787 | vertical.onUpdate?.call(details); |
1788 | vertical.onEnd?.call(DragEndDetails(primaryVelocity: 0.0)); |
1789 | }; |
1790 | |
1791 | final GestureDragUpdateCallback? panHandler = |
1792 | pan == null |
1793 | ? null |
1794 | : (DragUpdateDetails details) { |
1795 | pan.onDown?.call(DragDownDetails()); |
1796 | pan.onStart?.call(DragStartDetails()); |
1797 | pan.onUpdate?.call(details); |
1798 | pan.onEnd?.call(DragEndDetails()); |
1799 | }; |
1800 | |
1801 | if (verticalHandler == null && panHandler == null) { |
1802 | return null; |
1803 | } |
1804 | return (DragUpdateDetails details) { |
1805 | verticalHandler?.call(details); |
1806 | panHandler?.call(details); |
1807 | }; |
1808 | } |
1809 | } |
1810 |
Definitions
- GestureRecognizerFactory
- GestureRecognizerFactory
- constructor
- initializer
- _debugAssertTypeMatches
- GestureRecognizerFactoryWithHandlers
- GestureRecognizerFactoryWithHandlers
- constructor
- initializer
- GestureDetector
- GestureDetector
- build
- debugFillProperties
- RawGestureDetector
- RawGestureDetector
- createState
- RawGestureDetectorState
- initState
- didUpdateWidget
- replaceGestureRecognizers
- replaceSemanticsActions
- dispose
- _syncAll
- _handlePointerDown
- _handlePointerPanZoomStart
- _defaultBehavior
- _updateSemanticsForRenderObject
- build
- debugFillProperties
- _GestureSemantics
- _GestureSemantics
- createRenderObject
- updateRenderObject
- SemanticsGestureDelegate
- SemanticsGestureDelegate
- assignSemantics
- toString
- _DefaultSemanticsGestureDelegate
- _DefaultSemanticsGestureDelegate
- assignSemantics
- _getTapHandler
- _getLongPressHandler
- _getHorizontalDragUpdateHandler
Learn more about Flutter for embedded and desktop on industrialflutter.com