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