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/widgets.dart'; |
6 | /// |
7 | /// @docImport 'multitap.dart'; |
8 | library; |
9 | |
10 | import 'package:flutter/foundation.dart'; |
11 | |
12 | import 'arena.dart'; |
13 | import 'constants.dart'; |
14 | import 'events.dart'; |
15 | import 'recognizer.dart'; |
16 | |
17 | export 'dart:ui' show Offset, PointerDeviceKind; |
18 | |
19 | export 'package:flutter/foundation.dart' show DiagnosticPropertiesBuilder; |
20 | export 'package:vector_math/vector_math_64.dart'show Matrix4; |
21 | |
22 | export 'arena.dart' show GestureDisposition; |
23 | export 'events.dart' show PointerCancelEvent, PointerDownEvent, PointerEvent, PointerUpEvent; |
24 | |
25 | /// Details for [GestureTapDownCallback], such as position. |
26 | /// |
27 | /// See also: |
28 | /// |
29 | /// * [GestureDetector.onTapDown], which receives this information. |
30 | /// * [TapGestureRecognizer], which passes this information to one of its callbacks. |
31 | class TapDownDetails { |
32 | /// Creates details for a [GestureTapDownCallback]. |
33 | TapDownDetails({this.globalPosition = Offset.zero, Offset? localPosition, this.kind}) |
34 | : localPosition = localPosition ?? globalPosition; |
35 | |
36 | /// The global position at which the pointer contacted the screen. |
37 | final Offset globalPosition; |
38 | |
39 | /// The kind of the device that initiated the event. |
40 | final PointerDeviceKind? kind; |
41 | |
42 | /// The local position at which the pointer contacted the screen. |
43 | final Offset localPosition; |
44 | } |
45 | |
46 | /// {@template flutter.gestures.tap.GestureTapDownCallback} |
47 | /// Signature for when a pointer that might cause a tap has contacted the |
48 | /// screen. |
49 | /// |
50 | /// The position at which the pointer contacted the screen is available in the |
51 | /// `details`. |
52 | /// {@endtemplate} |
53 | /// |
54 | /// See also: |
55 | /// |
56 | /// * [GestureDetector.onTapDown], which matches this signature. |
57 | /// * [TapGestureRecognizer], which uses this signature in one of its callbacks. |
58 | typedef GestureTapDownCallback = void Function(TapDownDetails details); |
59 | |
60 | /// Details for [GestureTapUpCallback], such as position. |
61 | /// |
62 | /// See also: |
63 | /// |
64 | /// * [GestureDetector.onTapUp], which receives this information. |
65 | /// * [TapGestureRecognizer], which passes this information to one of its callbacks. |
66 | class TapUpDetails { |
67 | /// Creates a [TapUpDetails] data object. |
68 | TapUpDetails({required this.kind, this.globalPosition = Offset.zero, Offset? localPosition}) |
69 | : localPosition = localPosition ?? globalPosition; |
70 | |
71 | /// The global position at which the pointer contacted the screen. |
72 | final Offset globalPosition; |
73 | |
74 | /// The local position at which the pointer contacted the screen. |
75 | final Offset localPosition; |
76 | |
77 | /// The kind of the device that initiated the event. |
78 | final PointerDeviceKind kind; |
79 | } |
80 | |
81 | /// Details object for callbacks that use [GestureTapMoveCallback]. |
82 | /// |
83 | /// See also: |
84 | /// |
85 | /// * [GestureDetector.onTapMove], which receives this information. |
86 | /// * [TapGestureRecognizer], which passes this information to one of its callbacks. |
87 | class TapMoveDetails { |
88 | /// Creates a [TapMoveDetails] data object. |
89 | TapMoveDetails({ |
90 | required this.kind, |
91 | this.globalPosition = Offset.zero, |
92 | this.delta = Offset.zero, |
93 | Offset? localPosition, |
94 | }) : localPosition = localPosition ?? globalPosition; |
95 | |
96 | /// The global position at which the pointer contacted the screen. |
97 | final Offset globalPosition; |
98 | |
99 | /// The local position at which the pointer contacted the screen. |
100 | final Offset localPosition; |
101 | |
102 | /// The kind of the device that initiated the event. |
103 | final PointerDeviceKind kind; |
104 | |
105 | /// The amount the pointer has moved in the coordinate space of the |
106 | /// event receiver since the previous update. |
107 | final Offset delta; |
108 | } |
109 | |
110 | /// {@template flutter.gestures.tap.GestureTapUpCallback} |
111 | /// Signature for when a pointer that will trigger a tap has stopped contacting |
112 | /// the screen. |
113 | /// |
114 | /// The position at which the pointer stopped contacting the screen is available |
115 | /// in the `details`. |
116 | /// {@endtemplate} |
117 | /// |
118 | /// See also: |
119 | /// |
120 | /// * [GestureDetector.onTapUp], which matches this signature. |
121 | /// * [TapGestureRecognizer], which uses this signature in one of its callbacks. |
122 | typedef GestureTapUpCallback = void Function(TapUpDetails details); |
123 | |
124 | /// Signature for when a tap has occurred. |
125 | /// |
126 | /// See also: |
127 | /// |
128 | /// * [GestureDetector.onTap], which matches this signature. |
129 | /// * [TapGestureRecognizer], which uses this signature in one of its callbacks. |
130 | typedef GestureTapCallback = void Function(); |
131 | |
132 | /// Signature for when a pointer that triggered a tap has moved. |
133 | /// |
134 | /// The position at which the pointer moved is available in the `details`. |
135 | /// |
136 | /// See also: |
137 | /// |
138 | /// * [GestureDetector.onTapMove], which matches this signature. |
139 | /// * [TapGestureRecognizer], which uses this signature in one of its callbacks. |
140 | typedef GestureTapMoveCallback = void Function(TapMoveDetails details); |
141 | |
142 | /// Signature for when the pointer that previously triggered a |
143 | /// [GestureTapDownCallback] will not end up causing a tap. |
144 | /// |
145 | /// See also: |
146 | /// |
147 | /// * [GestureDetector.onTapCancel], which matches this signature. |
148 | /// * [TapGestureRecognizer], which uses this signature in one of its callbacks. |
149 | typedef GestureTapCancelCallback = void Function(); |
150 | |
151 | /// A base class for gesture recognizers that recognize taps. |
152 | /// |
153 | /// Gesture recognizers take part in gesture arenas to enable potential gestures |
154 | /// to be disambiguated from each other. This process is managed by a |
155 | /// [GestureArenaManager]. |
156 | /// |
157 | /// A tap is defined as a sequence of events that starts with a down, followed |
158 | /// by optional moves, then ends with an up. All move events must contain the |
159 | /// same `buttons` as the down event, and must not be too far from the initial |
160 | /// position. The gesture is rejected on any violation, a cancel event, or |
161 | /// if any other recognizers wins the arena. It is accepted only when it is the |
162 | /// last member of the arena. |
163 | /// |
164 | /// The [BaseTapGestureRecognizer] considers all the pointers involved in the |
165 | /// pointer event sequence as contributing to one gesture. For this reason, |
166 | /// extra pointer interactions during a tap sequence are not recognized as |
167 | /// additional taps. For example, down-1, down-2, up-1, up-2 produces only one |
168 | /// tap on up-1. |
169 | /// |
170 | /// The [BaseTapGestureRecognizer] can not be directly used, since it does not |
171 | /// define which buttons to accept, or what to do when a tap happens. If you |
172 | /// want to build a custom tap recognizer, extend this class by overriding |
173 | /// [isPointerAllowed] and the handler methods. |
174 | /// |
175 | /// See also: |
176 | /// |
177 | /// * [TapGestureRecognizer], a ready-to-use tap recognizer that recognizes |
178 | /// taps of the primary button and taps of the secondary button. |
179 | /// * [ModalBarrier], a widget that uses a custom tap recognizer that accepts |
180 | /// any buttons. |
181 | abstract class BaseTapGestureRecognizer extends PrimaryPointerGestureRecognizer { |
182 | /// Creates a tap gesture recognizer. |
183 | /// |
184 | /// {@macro flutter.gestures.GestureRecognizer.supportedDevices} |
185 | BaseTapGestureRecognizer({ |
186 | super.debugOwner, |
187 | super.supportedDevices, |
188 | super.allowedButtonsFilter, |
189 | super.preAcceptSlopTolerance, |
190 | super.postAcceptSlopTolerance, |
191 | }) : super(deadline: kPressTimeout); |
192 | |
193 | bool _sentTapDown = false; |
194 | bool _wonArenaForPrimaryPointer = false; |
195 | |
196 | PointerDownEvent? _down; |
197 | PointerUpEvent? _up; |
198 | |
199 | /// A pointer has contacted the screen, which might be the start of a tap. |
200 | /// |
201 | /// This triggers after the down event, once a short timeout ([deadline]) has |
202 | /// elapsed, or once the gesture has won the arena, whichever comes first. |
203 | /// |
204 | /// The parameter `down` is the down event of the primary pointer that started |
205 | /// the tap sequence. |
206 | /// |
207 | /// If this recognizer doesn't win the arena, [handleTapCancel] is called next. |
208 | /// Otherwise, [handleTapUp] is called next. |
209 | @protected |
210 | void handleTapDown({required PointerDownEvent down}); |
211 | |
212 | /// A pointer has stopped contacting the screen, which is recognized as a tap. |
213 | /// |
214 | /// This triggers on the up event if the recognizer wins the arena with it |
215 | /// or has previously won. |
216 | /// |
217 | /// The parameter `down` is the down event of the primary pointer that started |
218 | /// the tap sequence, and `up` is the up event that ended the tap sequence. |
219 | /// |
220 | /// If this recognizer doesn't win the arena, [handleTapCancel] is called |
221 | /// instead. |
222 | @protected |
223 | void handleTapUp({required PointerDownEvent down, required PointerUpEvent up}); |
224 | |
225 | /// A pointer that triggered a tap has moved. |
226 | /// |
227 | /// This triggers on the move event if the recognizer has recognized the tap gesture. |
228 | /// |
229 | /// The parameter `move` is the move event of the primary pointer that started |
230 | /// the tap sequence. |
231 | @protected |
232 | void handleTapMove({required PointerMoveEvent move}) {} |
233 | |
234 | /// A pointer that previously triggered [handleTapDown] will not end up |
235 | /// causing a tap. |
236 | /// |
237 | /// This triggers once the gesture loses the arena if [handleTapDown] has |
238 | /// been previously triggered. |
239 | /// |
240 | /// The parameter `down` is the down event of the primary pointer that started |
241 | /// the tap sequence; `cancel` is the cancel event, which might be null; |
242 | /// `reason` is a short description of the cause if `cancel` is null, which |
243 | /// can be "forced" if other gestures won the arena, or "spontaneous" |
244 | /// otherwise. |
245 | /// |
246 | /// If this recognizer wins the arena, [handleTapUp] is called instead. |
247 | @protected |
248 | void handleTapCancel({ |
249 | required PointerDownEvent down, |
250 | PointerCancelEvent? cancel, |
251 | required String reason, |
252 | }); |
253 | |
254 | @override |
255 | void addAllowedPointer(PointerDownEvent event) { |
256 | if (state == GestureRecognizerState.ready) { |
257 | // If there is no result in the previous gesture arena, |
258 | // we ignore them and prepare to accept a new pointer. |
259 | if (_down != null && _up != null) { |
260 | assert(_down!.pointer == _up!.pointer); |
261 | _reset(); |
262 | } |
263 | |
264 | assert(_down == null && _up == null); |
265 | // `_down` must be assigned in this method instead of `handlePrimaryPointer`, |
266 | // because `acceptGesture` might be called before `handlePrimaryPointer`, |
267 | // which relies on `_down` to call `handleTapDown`. |
268 | _down = event; |
269 | } |
270 | if (_down != null) { |
271 | // This happens when this tap gesture has been rejected while the pointer |
272 | // is down (i.e. due to movement), when another allowed pointer is added, |
273 | // in which case all pointers are ignored. The `_down` being null |
274 | // means that _reset() has been called, since it is always set at the |
275 | // first allowed down event and will not be cleared except for reset(), |
276 | super.addAllowedPointer(event); |
277 | } |
278 | } |
279 | |
280 | @override |
281 | @protected |
282 | void startTrackingPointer(int pointer, [Matrix4? transform]) { |
283 | // The recognizer should never track any pointers when `_down` is null, |
284 | // because calling `_checkDown` in this state will throw exception. |
285 | assert(_down != null); |
286 | super.startTrackingPointer(pointer, transform); |
287 | } |
288 | |
289 | @override |
290 | void handlePrimaryPointer(PointerEvent event) { |
291 | if (event is PointerUpEvent) { |
292 | _up = event; |
293 | _checkUp(); |
294 | } else if (event is PointerCancelEvent) { |
295 | resolve(GestureDisposition.rejected); |
296 | if (_sentTapDown) { |
297 | _checkCancel(event, ''); |
298 | } |
299 | _reset(); |
300 | } else if (event.buttons != _down!.buttons) { |
301 | resolve(GestureDisposition.rejected); |
302 | stopTrackingPointer(primaryPointer!); |
303 | } else if (event is PointerMoveEvent) { |
304 | _checkMove(event); |
305 | } |
306 | } |
307 | |
308 | @override |
309 | void resolve(GestureDisposition disposition) { |
310 | if (_wonArenaForPrimaryPointer && disposition == GestureDisposition.rejected) { |
311 | // This can happen if the gesture has been canceled. For example, when |
312 | // the pointer has exceeded the touch slop, the buttons have been changed, |
313 | // or if the recognizer is disposed. |
314 | assert(_sentTapDown); |
315 | _checkCancel(null, 'spontaneous'); |
316 | _reset(); |
317 | } |
318 | super.resolve(disposition); |
319 | } |
320 | |
321 | @override |
322 | void didExceedDeadline() { |
323 | _checkDown(); |
324 | } |
325 | |
326 | @override |
327 | void acceptGesture(int pointer) { |
328 | super.acceptGesture(pointer); |
329 | if (pointer == primaryPointer) { |
330 | _checkDown(); |
331 | _wonArenaForPrimaryPointer = true; |
332 | _checkUp(); |
333 | } |
334 | } |
335 | |
336 | @override |
337 | void rejectGesture(int pointer) { |
338 | super.rejectGesture(pointer); |
339 | if (pointer == primaryPointer) { |
340 | // Another gesture won the arena. |
341 | assert(state != GestureRecognizerState.possible); |
342 | if (_sentTapDown) { |
343 | _checkCancel(null, 'forced'); |
344 | } |
345 | _reset(); |
346 | } |
347 | } |
348 | |
349 | void _checkDown() { |
350 | if (_sentTapDown) { |
351 | return; |
352 | } |
353 | handleTapDown(down: _down!); |
354 | _sentTapDown = true; |
355 | } |
356 | |
357 | void _checkUp() { |
358 | if (!_wonArenaForPrimaryPointer || _up == null) { |
359 | return; |
360 | } |
361 | assert(_up!.pointer == _down!.pointer); |
362 | handleTapUp(down: _down!, up: _up!); |
363 | _reset(); |
364 | } |
365 | |
366 | void _checkCancel(PointerCancelEvent? event, String note) { |
367 | handleTapCancel(down: _down!, cancel: event, reason: note); |
368 | } |
369 | |
370 | void _checkMove(PointerMoveEvent event) { |
371 | assert(event.pointer == _down!.pointer); |
372 | handleTapMove(move: event); |
373 | } |
374 | |
375 | void _reset() { |
376 | _sentTapDown = false; |
377 | _wonArenaForPrimaryPointer = false; |
378 | _up = null; |
379 | _down = null; |
380 | } |
381 | |
382 | @override |
383 | String get debugDescription => 'base tap'; |
384 | |
385 | @override |
386 | void debugFillProperties(DiagnosticPropertiesBuilder properties) { |
387 | super.debugFillProperties(properties); |
388 | properties.add( |
389 | FlagProperty( |
390 | 'wonArenaForPrimaryPointer', |
391 | value: _wonArenaForPrimaryPointer, |
392 | ifTrue: 'won arena', |
393 | ), |
394 | ); |
395 | properties.add(DiagnosticsProperty<Offset>('finalPosition', _up?.position, defaultValue: null)); |
396 | properties.add( |
397 | DiagnosticsProperty<Offset>( |
398 | 'finalLocalPosition', |
399 | _up?.localPosition, |
400 | defaultValue: _up?.position, |
401 | ), |
402 | ); |
403 | properties.add(DiagnosticsProperty<int>('button', _down?.buttons, defaultValue: null)); |
404 | properties.add(FlagProperty('sentTapDown', value: _sentTapDown, ifTrue: 'sent tap down')); |
405 | } |
406 | } |
407 | |
408 | /// Recognizes taps. |
409 | /// |
410 | /// Gesture recognizers take part in gesture arenas to enable potential gestures |
411 | /// to be disambiguated from each other. This process is managed by a |
412 | /// [GestureArenaManager]. |
413 | /// |
414 | /// [TapGestureRecognizer] considers all the pointers involved in the pointer |
415 | /// event sequence as contributing to one gesture. For this reason, extra |
416 | /// pointer interactions during a tap sequence are not recognized as additional |
417 | /// taps. For example, down-1, down-2, up-1, up-2 produces only one tap on up-1. |
418 | /// |
419 | /// [TapGestureRecognizer] competes on pointer events of [kPrimaryButton] only |
420 | /// when it has at least one non-null `onTap*` callback, on events of |
421 | /// [kSecondaryButton] only when it has at least one non-null `onSecondaryTap*` |
422 | /// callback, and on events of [kTertiaryButton] only when it has at least |
423 | /// one non-null `onTertiaryTap*` callback. If it has no callbacks, it is a |
424 | /// no-op. |
425 | /// |
426 | /// {@template flutter.gestures.tap.TapGestureRecognizer.allowedButtonsFilter} |
427 | /// The [allowedButtonsFilter] argument only gives this recognizer the |
428 | /// ability to limit the buttons it accepts. It does not provide the |
429 | /// ability to recognize any buttons beyond the ones it already accepts: |
430 | /// kPrimaryButton, kSecondaryButton or kTertiaryButton. Therefore, a |
431 | /// combined value of `kPrimaryButton & kSecondaryButton` would be ignored, |
432 | /// but `kPrimaryButton | kSecondaryButton` would be allowed, as long as |
433 | /// only one of them is selected at a time. |
434 | /// {@endtemplate} |
435 | /// |
436 | /// See also: |
437 | /// |
438 | /// * [GestureDetector.onTap], which uses this recognizer. |
439 | /// * [MultiTapGestureRecognizer] |
440 | class TapGestureRecognizer extends BaseTapGestureRecognizer { |
441 | /// Creates a tap gesture recognizer. |
442 | /// |
443 | /// {@macro flutter.gestures.GestureRecognizer.supportedDevices} |
444 | TapGestureRecognizer({ |
445 | super.debugOwner, |
446 | super.supportedDevices, |
447 | super.allowedButtonsFilter, |
448 | super.preAcceptSlopTolerance, |
449 | super.postAcceptSlopTolerance, |
450 | }); |
451 | |
452 | /// {@template flutter.gestures.tap.TapGestureRecognizer.onTapDown} |
453 | /// A pointer has contacted the screen at a particular location with a primary |
454 | /// button, which might be the start of a tap. |
455 | /// {@endtemplate} |
456 | /// |
457 | /// This triggers after the down event, once a short timeout ([deadline]) has |
458 | /// elapsed, or once the gestures has won the arena, whichever comes first. |
459 | /// |
460 | /// If this recognizer doesn't win the arena, [onTapCancel] is called next. |
461 | /// Otherwise, [onTapUp] is called next. |
462 | /// |
463 | /// See also: |
464 | /// |
465 | /// * [kPrimaryButton], the button this callback responds to. |
466 | /// * [onSecondaryTapDown], a similar callback but for a secondary button. |
467 | /// * [onTertiaryTapDown], a similar callback but for a tertiary button. |
468 | /// * [TapDownDetails], which is passed as an argument to this callback. |
469 | /// * [GestureDetector.onTapDown], which exposes this callback. |
470 | GestureTapDownCallback? onTapDown; |
471 | |
472 | /// {@template flutter.gestures.tap.TapGestureRecognizer.onTapUp} |
473 | /// A pointer has stopped contacting the screen at a particular location, |
474 | /// which is recognized as a tap of a primary button. |
475 | /// {@endtemplate} |
476 | /// |
477 | /// This triggers on the up event, if the recognizer wins the arena with it |
478 | /// or has previously won, immediately followed by [onTap]. |
479 | /// |
480 | /// If this recognizer doesn't win the arena, [onTapCancel] is called instead. |
481 | /// |
482 | /// See also: |
483 | /// |
484 | /// * [kPrimaryButton], the button this callback responds to. |
485 | /// * [onSecondaryTapUp], a similar callback but for a secondary button. |
486 | /// * [onTertiaryTapUp], a similar callback but for a tertiary button. |
487 | /// * [TapUpDetails], which is passed as an argument to this callback. |
488 | /// * [GestureDetector.onTapUp], which exposes this callback. |
489 | GestureTapUpCallback? onTapUp; |
490 | |
491 | /// A pointer has stopped contacting the screen, which is recognized as a tap |
492 | /// of a primary button. |
493 | /// |
494 | /// This triggers on the up event, if the recognizer wins the arena with it |
495 | /// or has previously won, immediately following [onTapUp]. |
496 | /// |
497 | /// If this recognizer doesn't win the arena, [onTapCancel] is called instead. |
498 | /// |
499 | /// See also: |
500 | /// |
501 | /// * [kPrimaryButton], the button this callback responds to. |
502 | /// * [onSecondaryTap], a similar callback but for a secondary button. |
503 | /// * [onTapUp], which has the same timing but with details. |
504 | /// * [GestureDetector.onTap], which exposes this callback. |
505 | GestureTapCallback? onTap; |
506 | |
507 | /// A pointer that triggered a tap has moved. |
508 | /// |
509 | /// This callback is triggered after the tap gesture has been recognized and the pointer starts to move. |
510 | /// |
511 | /// If the pointer moves beyond the `postAcceptSlopTolerance` distance, the tap will be canceled. |
512 | /// To make `onTapMove` more useful, consider setting `postAcceptSlopTolerance` to a larger value, |
513 | /// or to `null` for no limit on movement. |
514 | /// |
515 | /// See also: |
516 | /// |
517 | /// * [kPrimaryButton], the button this callback responds to. |
518 | /// * [GestureDetector.onTapMove], which exposes this callback. |
519 | GestureTapMoveCallback? onTapMove; |
520 | |
521 | /// {@template flutter.gestures.tap.TapGestureRecognizer.onTapCancel} |
522 | /// A pointer that previously triggered [onTapDown] will not end up causing |
523 | /// a tap. |
524 | /// {@endtemplate} |
525 | /// |
526 | /// This triggers once the gesture loses the arena if [onTapDown] has |
527 | /// previously been triggered. |
528 | /// |
529 | /// If this recognizer wins the arena, [onTapUp] and [onTap] are called |
530 | /// instead. |
531 | /// |
532 | /// See also: |
533 | /// |
534 | /// * [kPrimaryButton], the button this callback responds to. |
535 | /// * [onSecondaryTapCancel], a similar callback but for a secondary button. |
536 | /// * [onTertiaryTapCancel], a similar callback but for a tertiary button. |
537 | /// * [GestureDetector.onTapCancel], which exposes this callback. |
538 | GestureTapCancelCallback? onTapCancel; |
539 | |
540 | /// {@template flutter.gestures.tap.TapGestureRecognizer.onSecondaryTap} |
541 | /// A pointer has stopped contacting the screen, which is recognized as a tap |
542 | /// of a secondary button. |
543 | /// {@endtemplate} |
544 | /// |
545 | /// This triggers on the up event, if the recognizer wins the arena with it or |
546 | /// has previously won, immediately following [onSecondaryTapUp]. |
547 | /// |
548 | /// If this recognizer doesn't win the arena, [onSecondaryTapCancel] is called |
549 | /// instead. |
550 | /// |
551 | /// See also: |
552 | /// |
553 | /// * [kSecondaryButton], the button this callback responds to. |
554 | /// * [onSecondaryTapUp], which has the same timing but with details. |
555 | /// * [GestureDetector.onSecondaryTap], which exposes this callback. |
556 | GestureTapCallback? onSecondaryTap; |
557 | |
558 | /// {@template flutter.gestures.tap.TapGestureRecognizer.onSecondaryTapDown} |
559 | /// A pointer has contacted the screen at a particular location with a |
560 | /// secondary button, which might be the start of a secondary tap. |
561 | /// {@endtemplate} |
562 | /// |
563 | /// This triggers after the down event, once a short timeout ([deadline]) has |
564 | /// elapsed, or once the gestures has won the arena, whichever comes first. |
565 | /// |
566 | /// If this recognizer doesn't win the arena, [onSecondaryTapCancel] is called |
567 | /// next. Otherwise, [onSecondaryTapUp] is called next. |
568 | /// |
569 | /// See also: |
570 | /// |
571 | /// * [kSecondaryButton], the button this callback responds to. |
572 | /// * [onTapDown], a similar callback but for a primary button. |
573 | /// * [onTertiaryTapDown], a similar callback but for a tertiary button. |
574 | /// * [TapDownDetails], which is passed as an argument to this callback. |
575 | /// * [GestureDetector.onSecondaryTapDown], which exposes this callback. |
576 | GestureTapDownCallback? onSecondaryTapDown; |
577 | |
578 | /// {@template flutter.gestures.tap.TapGestureRecognizer.onSecondaryTapUp} |
579 | /// A pointer has stopped contacting the screen at a particular location, |
580 | /// which is recognized as a tap of a secondary button. |
581 | /// {@endtemplate} |
582 | /// |
583 | /// This triggers on the up event if the recognizer wins the arena with it |
584 | /// or has previously won. |
585 | /// |
586 | /// If this recognizer doesn't win the arena, [onSecondaryTapCancel] is called |
587 | /// instead. |
588 | /// |
589 | /// See also: |
590 | /// |
591 | /// * [onSecondaryTap], a handler triggered right after this one that doesn't |
592 | /// pass any details about the tap. |
593 | /// * [kSecondaryButton], the button this callback responds to. |
594 | /// * [onTapUp], a similar callback but for a primary button. |
595 | /// * [onTertiaryTapUp], a similar callback but for a tertiary button. |
596 | /// * [TapUpDetails], which is passed as an argument to this callback. |
597 | /// * [GestureDetector.onSecondaryTapUp], which exposes this callback. |
598 | GestureTapUpCallback? onSecondaryTapUp; |
599 | |
600 | /// {@template flutter.gestures.tap.TapGestureRecognizer.onSecondaryTapCancel} |
601 | /// A pointer that previously triggered [onSecondaryTapDown] will not end up |
602 | /// causing a tap. |
603 | /// {@endtemplate} |
604 | /// |
605 | /// This triggers once the gesture loses the arena if [onSecondaryTapDown] |
606 | /// has previously been triggered. |
607 | /// |
608 | /// If this recognizer wins the arena, [onSecondaryTapUp] is called instead. |
609 | /// |
610 | /// See also: |
611 | /// |
612 | /// * [kSecondaryButton], the button this callback responds to. |
613 | /// * [onTapCancel], a similar callback but for a primary button. |
614 | /// * [onTertiaryTapCancel], a similar callback but for a tertiary button. |
615 | /// * [GestureDetector.onSecondaryTapCancel], which exposes this callback. |
616 | GestureTapCancelCallback? onSecondaryTapCancel; |
617 | |
618 | /// A pointer has contacted the screen at a particular location with a |
619 | /// tertiary button, which might be the start of a tertiary tap. |
620 | /// |
621 | /// This triggers after the down event, once a short timeout ([deadline]) has |
622 | /// elapsed, or once the gestures has won the arena, whichever comes first. |
623 | /// |
624 | /// If this recognizer doesn't win the arena, [onTertiaryTapCancel] is called |
625 | /// next. Otherwise, [onTertiaryTapUp] is called next. |
626 | /// |
627 | /// See also: |
628 | /// |
629 | /// * [kTertiaryButton], the button this callback responds to. |
630 | /// * [onTapDown], a similar callback but for a primary button. |
631 | /// * [onSecondaryTapDown], a similar callback but for a secondary button. |
632 | /// * [TapDownDetails], which is passed as an argument to this callback. |
633 | /// * [GestureDetector.onTertiaryTapDown], which exposes this callback. |
634 | GestureTapDownCallback? onTertiaryTapDown; |
635 | |
636 | /// A pointer has stopped contacting the screen at a particular location, |
637 | /// which is recognized as a tap of a tertiary button. |
638 | /// |
639 | /// This triggers on the up event if the recognizer wins the arena with it |
640 | /// or has previously won. |
641 | /// |
642 | /// If this recognizer doesn't win the arena, [onTertiaryTapCancel] is called |
643 | /// instead. |
644 | /// |
645 | /// See also: |
646 | /// |
647 | /// * [kTertiaryButton], the button this callback responds to. |
648 | /// * [onTapUp], a similar callback but for a primary button. |
649 | /// * [onSecondaryTapUp], a similar callback but for a secondary button. |
650 | /// * [TapUpDetails], which is passed as an argument to this callback. |
651 | /// * [GestureDetector.onTertiaryTapUp], which exposes this callback. |
652 | GestureTapUpCallback? onTertiaryTapUp; |
653 | |
654 | /// A pointer that previously triggered [onTertiaryTapDown] will not end up |
655 | /// causing a tap. |
656 | /// |
657 | /// This triggers once the gesture loses the arena if [onTertiaryTapDown] |
658 | /// has previously been triggered. |
659 | /// |
660 | /// If this recognizer wins the arena, [onTertiaryTapUp] is called instead. |
661 | /// |
662 | /// See also: |
663 | /// |
664 | /// * [kSecondaryButton], the button this callback responds to. |
665 | /// * [onTapCancel], a similar callback but for a primary button. |
666 | /// * [onSecondaryTapCancel], a similar callback but for a secondary button. |
667 | /// * [GestureDetector.onTertiaryTapCancel], which exposes this callback. |
668 | GestureTapCancelCallback? onTertiaryTapCancel; |
669 | |
670 | @override |
671 | bool isPointerAllowed(PointerDownEvent event) { |
672 | switch (event.buttons) { |
673 | case kPrimaryButton: |
674 | if (onTapDown == null && |
675 | onTap == null && |
676 | onTapUp == null && |
677 | onTapCancel == null && |
678 | onTapMove == null) { |
679 | return false; |
680 | } |
681 | case kSecondaryButton: |
682 | if (onSecondaryTap == null && |
683 | onSecondaryTapDown == null && |
684 | onSecondaryTapUp == null && |
685 | onSecondaryTapCancel == null) { |
686 | return false; |
687 | } |
688 | case kTertiaryButton: |
689 | if (onTertiaryTapDown == null && onTertiaryTapUp == null && onTertiaryTapCancel == null) { |
690 | return false; |
691 | } |
692 | default: |
693 | return false; |
694 | } |
695 | return super.isPointerAllowed(event); |
696 | } |
697 | |
698 | @protected |
699 | @override |
700 | void handleTapDown({required PointerDownEvent down}) { |
701 | final TapDownDetails details = TapDownDetails( |
702 | globalPosition: down.position, |
703 | localPosition: down.localPosition, |
704 | kind: getKindForPointer(down.pointer), |
705 | ); |
706 | switch (down.buttons) { |
707 | case kPrimaryButton: |
708 | if (onTapDown != null) { |
709 | invokeCallback<void>('onTapDown', () => onTapDown!(details)); |
710 | } |
711 | case kSecondaryButton: |
712 | if (onSecondaryTapDown != null) { |
713 | invokeCallback<void>('onSecondaryTapDown', () => onSecondaryTapDown!(details)); |
714 | } |
715 | case kTertiaryButton: |
716 | if (onTertiaryTapDown != null) { |
717 | invokeCallback<void>('onTertiaryTapDown', () => onTertiaryTapDown!(details)); |
718 | } |
719 | default: |
720 | } |
721 | } |
722 | |
723 | @protected |
724 | @override |
725 | void handleTapUp({required PointerDownEvent down, required PointerUpEvent up}) { |
726 | final TapUpDetails details = TapUpDetails( |
727 | kind: up.kind, |
728 | globalPosition: up.position, |
729 | localPosition: up.localPosition, |
730 | ); |
731 | switch (down.buttons) { |
732 | case kPrimaryButton: |
733 | if (onTapUp != null) { |
734 | invokeCallback<void>('onTapUp', () => onTapUp!(details)); |
735 | } |
736 | if (onTap != null) { |
737 | invokeCallback<void>('onTap', onTap!); |
738 | } |
739 | case kSecondaryButton: |
740 | if (onSecondaryTapUp != null) { |
741 | invokeCallback<void>('onSecondaryTapUp', () => onSecondaryTapUp!(details)); |
742 | } |
743 | if (onSecondaryTap != null) { |
744 | invokeCallback<void>('onSecondaryTap', () => onSecondaryTap!()); |
745 | } |
746 | case kTertiaryButton: |
747 | if (onTertiaryTapUp != null) { |
748 | invokeCallback<void>('onTertiaryTapUp', () => onTertiaryTapUp!(details)); |
749 | } |
750 | default: |
751 | } |
752 | } |
753 | |
754 | @protected |
755 | @override |
756 | void handleTapMove({required PointerMoveEvent move}) { |
757 | if (onTapMove != null && move.buttons == kPrimaryButton) { |
758 | final TapMoveDetails details = TapMoveDetails( |
759 | globalPosition: move.position, |
760 | localPosition: move.localPosition, |
761 | kind: getKindForPointer(move.pointer), |
762 | delta: move.delta, |
763 | ); |
764 | invokeCallback<void>('onTapMove', () => onTapMove!(details)); |
765 | } |
766 | } |
767 | |
768 | @protected |
769 | @override |
770 | void handleTapCancel({ |
771 | required PointerDownEvent down, |
772 | PointerCancelEvent? cancel, |
773 | required String reason, |
774 | }) { |
775 | final String note = reason == ''? reason : '$reason '; |
776 | switch (down.buttons) { |
777 | case kPrimaryButton: |
778 | if (onTapCancel != null) { |
779 | invokeCallback<void>('${note} onTapCancel', onTapCancel!); |
780 | } |
781 | case kSecondaryButton: |
782 | if (onSecondaryTapCancel != null) { |
783 | invokeCallback<void>('${note} onSecondaryTapCancel', onSecondaryTapCancel!); |
784 | } |
785 | case kTertiaryButton: |
786 | if (onTertiaryTapCancel != null) { |
787 | invokeCallback<void>('${note} onTertiaryTapCancel', onTertiaryTapCancel!); |
788 | } |
789 | default: |
790 | } |
791 | } |
792 | |
793 | @override |
794 | String get debugDescription => 'tap'; |
795 | } |
796 |
Definitions
- TapDownDetails
- TapDownDetails
- TapUpDetails
- TapUpDetails
- TapMoveDetails
- TapMoveDetails
- BaseTapGestureRecognizer
- BaseTapGestureRecognizer
- handleTapDown
- handleTapUp
- handleTapMove
- handleTapCancel
- addAllowedPointer
- startTrackingPointer
- handlePrimaryPointer
- resolve
- didExceedDeadline
- acceptGesture
- rejectGesture
- _checkDown
- _checkUp
- _checkCancel
- _checkMove
- _reset
- debugDescription
- debugFillProperties
- TapGestureRecognizer
- TapGestureRecognizer
- isPointerAllowed
- handleTapDown
- handleTapUp
- handleTapMove
- handleTapCancel
Learn more about Flutter for embedded and desktop on industrialflutter.com