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';
8library;
9
10import 'package:flutter/foundation.dart';
11
12import 'arena.dart';
13import 'constants.dart';
14import 'events.dart';
15import 'recognizer.dart';
16
17export 'dart:ui' show Offset, PointerDeviceKind;
18
19export 'package:flutter/foundation.dart' show DiagnosticPropertiesBuilder;
20export 'package:vector_math/vector_math_64.dart' show Matrix4;
21
22export 'arena.dart' show GestureDisposition;
23export '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.
31class 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.
58typedef 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.
66class 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.
87class 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.
122typedef 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.
130typedef 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.
140typedef 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.
149typedef 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.
181abstract 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]
440class 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

Provided by KDAB

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