• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2015 The Chromium 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
5import 'package:flutter/foundation.dart';
6import 'package:flutter/gestures.dart';
7import 'package:flutter/rendering.dart';
8
9import 'basic.dart';
10import 'framework.dart';
11
12export 'package:flutter/gestures.dart' show
13  DragDownDetails,
14  DragStartDetails,
15  DragUpdateDetails,
16  DragEndDetails,
17  GestureTapDownCallback,
18  GestureTapUpCallback,
19  GestureTapCallback,
20  GestureTapCancelCallback,
21  GestureLongPressCallback,
22  GestureLongPressStartCallback,
23  GestureLongPressMoveUpdateCallback,
24  GestureLongPressUpCallback,
25  GestureLongPressEndCallback,
26  GestureDragDownCallback,
27  GestureDragStartCallback,
28  GestureDragUpdateCallback,
29  GestureDragEndCallback,
30  GestureDragCancelCallback,
31  GestureScaleStartCallback,
32  GestureScaleUpdateCallback,
33  GestureScaleEndCallback,
34  GestureForcePressStartCallback,
35  GestureForcePressPeakCallback,
36  GestureForcePressEndCallback,
37  GestureForcePressUpdateCallback,
38  LongPressStartDetails,
39  LongPressMoveUpdateDetails,
40  LongPressEndDetails,
41  ScaleStartDetails,
42  ScaleUpdateDetails,
43  ScaleEndDetails,
44  TapDownDetails,
45  TapUpDetails,
46  ForcePressDetails,
47  Velocity;
48export 'package:flutter/rendering.dart' show RenderSemanticsGestureHandler;
49
50// Examples can assume:
51// bool _lights;
52// void setState(VoidCallback fn) { }
53// String _last;
54
55/// Factory for creating gesture recognizers.
56///
57/// `T` is the type of gesture recognizer this class manages.
58///
59/// Used by [RawGestureDetector.gestures].
60@optionalTypeArgs
61abstract class GestureRecognizerFactory<T extends GestureRecognizer> {
62  /// Abstract const constructor. This constructor enables subclasses to provide
63  /// const constructors so that they can be used in const expressions.
64  const GestureRecognizerFactory();
65
66  /// Must return an instance of T.
67  T constructor();
68
69  /// Must configure the given instance (which will have been created by
70  /// `constructor`).
71  ///
72  /// This normally means setting the callbacks.
73  void initializer(T instance);
74
75  bool _debugAssertTypeMatches(Type type) {
76    assert(type == T, 'GestureRecognizerFactory of type $T was used where type $type was specified.');
77    return true;
78  }
79}
80
81/// Signature for closures that implement [GestureRecognizerFactory.constructor].
82typedef GestureRecognizerFactoryConstructor<T extends GestureRecognizer> = T Function();
83
84/// Signature for closures that implement [GestureRecognizerFactory.initializer].
85typedef GestureRecognizerFactoryInitializer<T extends GestureRecognizer> = void Function(T instance);
86
87/// Factory for creating gesture recognizers that delegates to callbacks.
88///
89/// Used by [RawGestureDetector.gestures].
90class GestureRecognizerFactoryWithHandlers<T extends GestureRecognizer> extends GestureRecognizerFactory<T> {
91  /// Creates a gesture recognizer factory with the given callbacks.
92  ///
93  /// The arguments must not be null.
94  const GestureRecognizerFactoryWithHandlers(this._constructor, this._initializer)
95    : assert(_constructor != null),
96      assert(_initializer != null);
97
98  final GestureRecognizerFactoryConstructor<T> _constructor;
99
100  final GestureRecognizerFactoryInitializer<T> _initializer;
101
102  @override
103  T constructor() => _constructor();
104
105  @override
106  void initializer(T instance) => _initializer(instance);
107}
108
109/// A widget that detects gestures.
110///
111/// Attempts to recognize gestures that correspond to its non-null callbacks.
112///
113/// If this widget has a child, it defers to that child for its sizing behavior.
114/// If it does not have a child, it grows to fit the parent instead.
115///
116/// By default a GestureDetector with an invisible child ignores touches;
117/// this behavior can be controlled with [behavior].
118///
119/// GestureDetector also listens for accessibility events and maps
120/// them to the callbacks. To ignore accessibility events, set
121/// [excludeFromSemantics] to true.
122///
123/// See <http://flutter.dev/gestures/> for additional information.
124///
125/// Material design applications typically react to touches with ink splash
126/// effects. The [InkWell] class implements this effect and can be used in place
127/// of a [GestureDetector] for handling taps.
128///
129/// {@tool sample}
130///
131/// This example makes a rectangle react to being tapped by setting the
132/// `_lights` field:
133///
134/// ```dart
135/// GestureDetector(
136///   onTap: () {
137///     setState(() { _lights = true; });
138///   },
139///   child: Container(
140///     color: Colors.yellow,
141///     child: Text('TURN LIGHTS ON'),
142///   ),
143/// )
144/// ```
145/// {@end-tool}
146///
147/// ## Debugging
148///
149/// To see how large the hit test box of a [GestureDetector] is for debugging
150/// purposes, set [debugPaintPointersEnabled] to true.
151class GestureDetector extends StatelessWidget {
152  /// Creates a widget that detects gestures.
153  ///
154  /// Pan and scale callbacks cannot be used simultaneously because scale is a
155  /// superset of pan. Simply use the scale callbacks instead.
156  ///
157  /// Horizontal and vertical drag callbacks cannot be used simultaneously
158  /// because a combination of a horizontal and vertical drag is a pan. Simply
159  /// use the pan callbacks instead.
160  ///
161  /// By default, gesture detectors contribute semantic information to the tree
162  /// that is used by assistive technology.
163  GestureDetector({
164    Key key,
165    this.child,
166    this.onTapDown,
167    this.onTapUp,
168    this.onTap,
169    this.onTapCancel,
170    this.onSecondaryTapDown,
171    this.onSecondaryTapUp,
172    this.onSecondaryTapCancel,
173    this.onDoubleTap,
174    this.onLongPress,
175    this.onLongPressStart,
176    this.onLongPressMoveUpdate,
177    this.onLongPressUp,
178    this.onLongPressEnd,
179    this.onVerticalDragDown,
180    this.onVerticalDragStart,
181    this.onVerticalDragUpdate,
182    this.onVerticalDragEnd,
183    this.onVerticalDragCancel,
184    this.onHorizontalDragDown,
185    this.onHorizontalDragStart,
186    this.onHorizontalDragUpdate,
187    this.onHorizontalDragEnd,
188    this.onHorizontalDragCancel,
189    this.onForcePressStart,
190    this.onForcePressPeak,
191    this.onForcePressUpdate,
192    this.onForcePressEnd,
193    this.onPanDown,
194    this.onPanStart,
195    this.onPanUpdate,
196    this.onPanEnd,
197    this.onPanCancel,
198    this.onScaleStart,
199    this.onScaleUpdate,
200    this.onScaleEnd,
201    this.behavior,
202    this.excludeFromSemantics = false,
203    this.dragStartBehavior = DragStartBehavior.start,
204  }) : assert(excludeFromSemantics != null),
205       assert(dragStartBehavior != null),
206       assert(() {
207         final bool haveVerticalDrag = onVerticalDragStart != null || onVerticalDragUpdate != null || onVerticalDragEnd != null;
208         final bool haveHorizontalDrag = onHorizontalDragStart != null || onHorizontalDragUpdate != null || onHorizontalDragEnd != null;
209         final bool havePan = onPanStart != null || onPanUpdate != null || onPanEnd != null;
210         final bool haveScale = onScaleStart != null || onScaleUpdate != null || onScaleEnd != null;
211         if (havePan || haveScale) {
212           if (havePan && haveScale) {
213             throw FlutterError(
214               'Incorrect GestureDetector arguments.\n'
215               'Having both a pan gesture recognizer and a scale gesture recognizer is redundant; scale is a superset of pan. Just use the scale gesture recognizer.'
216             );
217           }
218           final String recognizer = havePan ? 'pan' : 'scale';
219           if (haveVerticalDrag && haveHorizontalDrag) {
220             throw FlutterError(
221               'Incorrect GestureDetector arguments.\n'
222               'Simultaneously having a vertical drag gesture recognizer, a horizontal drag gesture recognizer, and a $recognizer gesture recognizer '
223               'will result in the $recognizer gesture recognizer being ignored, since the other two will catch all drags.'
224             );
225           }
226         }
227         return true;
228       }()),
229       super(key: key);
230
231  /// The widget below this widget in the tree.
232  ///
233  /// {@macro flutter.widgets.child}
234  final Widget child;
235
236  /// A pointer that might cause a tap with a primary button has contacted the
237  /// screen at a particular location.
238  ///
239  /// This is called after a short timeout, even if the winning gesture has not
240  /// yet been selected. If the tap gesture wins, [onTapUp] will be called,
241  /// otherwise [onTapCancel] will be called.
242  ///
243  /// See also:
244  ///
245  ///  * [kPrimaryButton], the button this callback responds to.
246  final GestureTapDownCallback onTapDown;
247
248  /// A pointer that will trigger a tap with a primary button has stopped
249  /// contacting the screen at a particular location.
250  ///
251  /// This triggers immediately before [onTap] in the case of the tap gesture
252  /// winning. If the tap gesture did not win, [onTapCancel] is called instead.
253  ///
254  /// See also:
255  ///
256  ///  * [kPrimaryButton], the button this callback responds to.
257  final GestureTapUpCallback onTapUp;
258
259  /// A tap with a primary button has occurred.
260  ///
261  /// This triggers when the tap gesture wins. If the tap gesture did not win,
262  /// [onTapCancel] is called instead.
263  ///
264  /// See also:
265  ///
266  ///  * [kPrimaryButton], the button this callback responds to.
267  ///  * [onTapUp], which is called at the same time but includes details
268  ///    regarding the pointer position.
269  final GestureTapCallback onTap;
270
271  /// The pointer that previously triggered [onTapDown] will not end up causing
272  /// a tap.
273  ///
274  /// This is called after [onTapDown], and instead of [onTapUp] and [onTap], if
275  /// the tap gesture did not win.
276  ///
277  /// See also:
278  ///
279  ///  * [kPrimaryButton], the button this callback responds to.
280  final GestureTapCancelCallback onTapCancel;
281
282  /// A pointer that might cause a tap with a secondary button has contacted the
283  /// screen at a particular location.
284  ///
285  /// This is called after a short timeout, even if the winning gesture has not
286  /// yet been selected. If the tap gesture wins, [onSecondaryTapUp] will be
287  /// called, otherwise [onSecondaryTapCancel] will be called.
288  ///
289  /// See also:
290  ///
291  ///  * [kSecondaryButton], the button this callback responds to.
292  final GestureTapDownCallback onSecondaryTapDown;
293
294  /// A pointer that will trigger a tap with a secondary button has stopped
295  /// contacting the screen at a particular location.
296  ///
297  /// This triggers in the case of the tap gesture winning. If the tap gesture
298  /// did not win, [onSecondaryTapCancel] is called instead.
299  ///
300  /// See also:
301  ///
302  ///  * [kSecondaryButton], the button this callback responds to.
303  final GestureTapUpCallback onSecondaryTapUp;
304
305  /// The pointer that previously triggered [onSecondaryTapDown] will not end up
306  /// causing a tap.
307  ///
308  /// This is called after [onSecondaryTapDown], and instead of
309  /// [onSecondaryTapUp], if the tap gesture did not win.
310  ///
311  /// See also:
312  ///
313  ///  * [kSecondaryButton], the button this callback responds to.
314  final GestureTapCancelCallback onSecondaryTapCancel;
315
316  /// The user has tapped the screen with a primary button at the same location
317  /// twice in quick succession.
318  ///
319  /// See also:
320  ///
321  ///  * [kPrimaryButton], the button this callback responds to.
322  final GestureTapCallback onDoubleTap;
323
324  /// Called when a long press gesture with a primary button has been recognized.
325  ///
326  /// Triggered when a pointer has remained in contact with the screen at the
327  /// same location for a long period of time.
328  ///
329  /// See also:
330  ///
331  ///  * [kPrimaryButton], the button this callback responds to.
332  ///  * [onLongPressStart], which has the same timing but has gesture details.
333  final GestureLongPressCallback onLongPress;
334
335  /// Called when a long press gesture with a primary button has been recognized.
336  ///
337  /// Triggered when a pointer has remained in contact with the screen at the
338  /// same location for a long period of time.
339  ///
340  /// See also:
341  ///
342  ///  * [kPrimaryButton], the button this callback responds to.
343  ///  * [onLongPress], which has the same timing but without the gesture details.
344  final GestureLongPressStartCallback onLongPressStart;
345
346  /// A pointer has been drag-moved after a long press with a primary button.
347  ///
348  /// See also:
349  ///
350  ///  * [kPrimaryButton], the button this callback responds to.
351  final GestureLongPressMoveUpdateCallback onLongPressMoveUpdate;
352
353  /// A pointer that has triggered a long-press with a primary button has
354  /// stopped contacting the screen.
355  ///
356  /// See also:
357  ///
358  ///  * [kPrimaryButton], the button this callback responds to.
359  ///  * [onLongPressEnd], which has the same timing but has gesture details.
360  final GestureLongPressUpCallback onLongPressUp;
361
362  /// A pointer that has triggered a long-press with a primary button has
363  /// stopped contacting the screen.
364  ///
365  /// See also:
366  ///
367  ///  * [kPrimaryButton], the button this callback responds to.
368  ///  * [onLongPressUp], which has the same timing but without the gesture
369  ///    details.
370  final GestureLongPressEndCallback onLongPressEnd;
371
372  /// A pointer has contacted the screen with a primary button and might begin
373  /// to move vertically.
374  ///
375  /// See also:
376  ///
377  ///  * [kPrimaryButton], the button this callback responds to.
378  final GestureDragDownCallback onVerticalDragDown;
379
380  /// A pointer has contacted the screen with a primary button and has begun to
381  /// move vertically.
382  ///
383  /// See also:
384  ///
385  ///  * [kPrimaryButton], the button this callback responds to.
386  final GestureDragStartCallback onVerticalDragStart;
387
388  /// A pointer that is in contact with the screen with a primary button and
389  /// moving vertically has moved in the vertical direction.
390  ///
391  /// See also:
392  ///
393  ///  * [kPrimaryButton], the button this callback responds to.
394  final GestureDragUpdateCallback onVerticalDragUpdate;
395
396  /// A pointer that was previously in contact with the screen with a primary
397  /// button and moving vertically is no longer in contact with the screen and
398  /// was moving at a specific velocity when it stopped contacting the screen.
399  ///
400  /// See also:
401  ///
402  ///  * [kPrimaryButton], the button this callback responds to.
403  final GestureDragEndCallback onVerticalDragEnd;
404
405  /// The pointer that previously triggered [onVerticalDragDown] did not
406  /// complete.
407  ///
408  /// See also:
409  ///
410  ///  * [kPrimaryButton], the button this callback responds to.
411  final GestureDragCancelCallback onVerticalDragCancel;
412
413  /// A pointer has contacted the screen with a primary button and might begin
414  /// to move horizontally.
415  ///
416  /// See also:
417  ///
418  ///  * [kPrimaryButton], the button this callback responds to.
419  final GestureDragDownCallback onHorizontalDragDown;
420
421  /// A pointer has contacted the screen with a primary button and has begun to
422  /// move horizontally.
423  ///
424  /// See also:
425  ///
426  ///  * [kPrimaryButton], the button this callback responds to.
427  final GestureDragStartCallback onHorizontalDragStart;
428
429  /// A pointer that is in contact with the screen with a primary button and
430  /// moving horizontally has moved in the horizontal direction.
431  ///
432  /// See also:
433  ///
434  ///  * [kPrimaryButton], the button this callback responds to.
435  final GestureDragUpdateCallback onHorizontalDragUpdate;
436
437  /// A pointer that was previously in contact with the screen with a primary
438  /// button and moving horizontally is no longer in contact with the screen and
439  /// was moving at a specific velocity when it stopped contacting the screen.
440  ///
441  /// See also:
442  ///
443  ///  * [kPrimaryButton], the button this callback responds to.
444  final GestureDragEndCallback onHorizontalDragEnd;
445
446  /// The pointer that previously triggered [onHorizontalDragDown] did not
447  /// complete.
448  ///
449  /// See also:
450  ///
451  ///  * [kPrimaryButton], the button this callback responds to.
452  final GestureDragCancelCallback onHorizontalDragCancel;
453
454  /// A pointer has contacted the screen with a primary button and might begin
455  /// to move.
456  ///
457  /// See also:
458  ///
459  ///  * [kPrimaryButton], the button this callback responds to.
460  final GestureDragDownCallback onPanDown;
461
462  /// A pointer has contacted the screen with a primary button and has begun to
463  /// move.
464  ///
465  /// See also:
466  ///
467  ///  * [kPrimaryButton], the button this callback responds to.
468  final GestureDragStartCallback onPanStart;
469
470  /// A pointer that is in contact with the screen with a primary button and
471  /// moving has moved again.
472  ///
473  /// See also:
474  ///
475  ///  * [kPrimaryButton], the button this callback responds to.
476  final GestureDragUpdateCallback onPanUpdate;
477
478  /// A pointer that was previously in contact with the screen with a primary
479  /// button and moving is no longer in contact with the screen and was moving
480  /// at a specific velocity when it stopped contacting the screen.
481  ///
482  /// See also:
483  ///
484  ///  * [kPrimaryButton], the button this callback responds to.
485  final GestureDragEndCallback onPanEnd;
486
487  /// The pointer that previously triggered [onPanDown] did not complete.
488  ///
489  /// See also:
490  ///
491  ///  * [kPrimaryButton], the button this callback responds to.
492  final GestureDragCancelCallback onPanCancel;
493
494  /// The pointers in contact with the screen have established a focal point and
495  /// initial scale of 1.0.
496  final GestureScaleStartCallback onScaleStart;
497
498  /// The pointers in contact with the screen have indicated a new focal point
499  /// and/or scale.
500  final GestureScaleUpdateCallback onScaleUpdate;
501
502  /// The pointers are no longer in contact with the screen.
503  final GestureScaleEndCallback onScaleEnd;
504
505  /// The pointer is in contact with the screen and has pressed with sufficient
506  /// force to initiate a force press. The amount of force is at least
507  /// [ForcePressGestureRecognizer.startPressure].
508  ///
509  /// Note that this callback will only be fired on devices with pressure
510  /// detecting screens.
511  final GestureForcePressStartCallback onForcePressStart;
512
513  /// The pointer is in contact with the screen and has pressed with the maximum
514  /// force. The amount of force is at least
515  /// [ForcePressGestureRecognizer.peakPressure].
516  ///
517  /// Note that this callback will only be fired on devices with pressure
518  /// detecting screens.
519  final GestureForcePressPeakCallback onForcePressPeak;
520
521  /// A pointer is in contact with the screen, has previously passed the
522  /// [ForcePressGestureRecognizer.startPressure] and is either moving on the
523  /// plane of the screen, pressing the screen with varying forces or both
524  /// simultaneously.
525  ///
526  /// Note that this callback will only be fired on devices with pressure
527  /// detecting screens.
528  final GestureForcePressUpdateCallback onForcePressUpdate;
529
530  /// The pointer is no longer in contact with the screen.
531  ///
532  /// Note that this callback will only be fired on devices with pressure
533  /// detecting screens.
534  final GestureForcePressEndCallback onForcePressEnd;
535
536  /// How this gesture detector should behave during hit testing.
537  ///
538  /// This defaults to [HitTestBehavior.deferToChild] if [child] is not null and
539  /// [HitTestBehavior.translucent] if child is null.
540  final HitTestBehavior behavior;
541
542  /// Whether to exclude these gestures from the semantics tree. For
543  /// example, the long-press gesture for showing a tooltip is
544  /// excluded because the tooltip itself is included in the semantics
545  /// tree directly and so having a gesture to show it would result in
546  /// duplication of information.
547  final bool excludeFromSemantics;
548
549  /// Determines the way that drag start behavior is handled.
550  ///
551  /// If set to [DragStartBehavior.start], gesture drag behavior will
552  /// begin upon the detection of a drag gesture. If set to
553  /// [DragStartBehavior.down] it will begin when a down event is first detected.
554  ///
555  /// In general, setting this to [DragStartBehavior.start] will make drag
556  /// animation smoother and setting it to [DragStartBehavior.down] will make
557  /// drag behavior feel slightly more reactive.
558  ///
559  /// By default, the drag start behavior is [DragStartBehavior.start].
560  ///
561  /// Only the [onStart] callbacks for the [VerticalDragGestureRecognizer],
562  /// [HorizontalDragGestureRecognizer] and [PanGestureRecognizer] are affected
563  /// by this setting.
564  ///
565  /// See also:
566  ///
567  ///  * [DragGestureRecognizer.dragStartBehavior], which gives an example for the different behaviors.
568  final DragStartBehavior dragStartBehavior;
569
570  @override
571  Widget build(BuildContext context) {
572    final Map<Type, GestureRecognizerFactory> gestures = <Type, GestureRecognizerFactory>{};
573
574    if (
575      onTapDown != null ||
576      onTapUp != null ||
577      onTap != null ||
578      onTapCancel != null ||
579      onSecondaryTapDown != null ||
580      onSecondaryTapUp != null ||
581      onSecondaryTapCancel != null
582    ) {
583      gestures[TapGestureRecognizer] = GestureRecognizerFactoryWithHandlers<TapGestureRecognizer>(
584        () => TapGestureRecognizer(debugOwner: this),
585        (TapGestureRecognizer instance) {
586          instance
587            ..onTapDown = onTapDown
588            ..onTapUp = onTapUp
589            ..onTap = onTap
590            ..onTapCancel = onTapCancel
591            ..onSecondaryTapDown = onSecondaryTapDown
592            ..onSecondaryTapUp = onSecondaryTapUp
593            ..onSecondaryTapCancel = onSecondaryTapCancel;
594        },
595      );
596    }
597
598    if (onDoubleTap != null) {
599      gestures[DoubleTapGestureRecognizer] = GestureRecognizerFactoryWithHandlers<DoubleTapGestureRecognizer>(
600        () => DoubleTapGestureRecognizer(debugOwner: this),
601        (DoubleTapGestureRecognizer instance) {
602          instance
603            ..onDoubleTap = onDoubleTap;
604        },
605      );
606    }
607
608    if (onLongPress != null ||
609        onLongPressUp != null ||
610        onLongPressStart != null ||
611        onLongPressMoveUpdate != null ||
612        onLongPressEnd != null) {
613      gestures[LongPressGestureRecognizer] = GestureRecognizerFactoryWithHandlers<LongPressGestureRecognizer>(
614        () => LongPressGestureRecognizer(debugOwner: this),
615        (LongPressGestureRecognizer instance) {
616          instance
617            ..onLongPress = onLongPress
618            ..onLongPressStart = onLongPressStart
619            ..onLongPressMoveUpdate = onLongPressMoveUpdate
620            ..onLongPressEnd =onLongPressEnd
621            ..onLongPressUp = onLongPressUp;
622        },
623      );
624    }
625
626    if (onVerticalDragDown != null ||
627        onVerticalDragStart != null ||
628        onVerticalDragUpdate != null ||
629        onVerticalDragEnd != null ||
630        onVerticalDragCancel != null) {
631      gestures[VerticalDragGestureRecognizer] = GestureRecognizerFactoryWithHandlers<VerticalDragGestureRecognizer>(
632        () => VerticalDragGestureRecognizer(debugOwner: this),
633        (VerticalDragGestureRecognizer instance) {
634          instance
635            ..onDown = onVerticalDragDown
636            ..onStart = onVerticalDragStart
637            ..onUpdate = onVerticalDragUpdate
638            ..onEnd = onVerticalDragEnd
639            ..onCancel = onVerticalDragCancel
640            ..dragStartBehavior = dragStartBehavior;
641        },
642      );
643    }
644
645    if (onHorizontalDragDown != null ||
646        onHorizontalDragStart != null ||
647        onHorizontalDragUpdate != null ||
648        onHorizontalDragEnd != null ||
649        onHorizontalDragCancel != null) {
650      gestures[HorizontalDragGestureRecognizer] = GestureRecognizerFactoryWithHandlers<HorizontalDragGestureRecognizer>(
651        () => HorizontalDragGestureRecognizer(debugOwner: this),
652        (HorizontalDragGestureRecognizer instance) {
653          instance
654            ..onDown = onHorizontalDragDown
655            ..onStart = onHorizontalDragStart
656            ..onUpdate = onHorizontalDragUpdate
657            ..onEnd = onHorizontalDragEnd
658            ..onCancel = onHorizontalDragCancel
659            ..dragStartBehavior = dragStartBehavior;
660        },
661      );
662    }
663
664    if (onPanDown != null ||
665        onPanStart != null ||
666        onPanUpdate != null ||
667        onPanEnd != null ||
668        onPanCancel != null) {
669      gestures[PanGestureRecognizer] = GestureRecognizerFactoryWithHandlers<PanGestureRecognizer>(
670        () => PanGestureRecognizer(debugOwner: this),
671        (PanGestureRecognizer instance) {
672          instance
673            ..onDown = onPanDown
674            ..onStart = onPanStart
675            ..onUpdate = onPanUpdate
676            ..onEnd = onPanEnd
677            ..onCancel = onPanCancel
678            ..dragStartBehavior = dragStartBehavior;
679        },
680      );
681    }
682
683    if (onScaleStart != null || onScaleUpdate != null || onScaleEnd != null) {
684      gestures[ScaleGestureRecognizer] = GestureRecognizerFactoryWithHandlers<ScaleGestureRecognizer>(
685        () => ScaleGestureRecognizer(debugOwner: this),
686        (ScaleGestureRecognizer instance) {
687          instance
688            ..onStart = onScaleStart
689            ..onUpdate = onScaleUpdate
690            ..onEnd = onScaleEnd;
691        },
692      );
693    }
694
695    if (onForcePressStart != null ||
696        onForcePressPeak != null ||
697        onForcePressUpdate != null ||
698        onForcePressEnd != null) {
699      gestures[ForcePressGestureRecognizer] = GestureRecognizerFactoryWithHandlers<ForcePressGestureRecognizer>(
700        () => ForcePressGestureRecognizer(debugOwner: this),
701        (ForcePressGestureRecognizer instance) {
702          instance
703            ..onStart = onForcePressStart
704            ..onPeak = onForcePressPeak
705            ..onUpdate = onForcePressUpdate
706            ..onEnd = onForcePressEnd;
707        },
708      );
709    }
710
711    return RawGestureDetector(
712      gestures: gestures,
713      behavior: behavior,
714      excludeFromSemantics: excludeFromSemantics,
715      child: child,
716    );
717  }
718  @override
719  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
720    super.debugFillProperties(properties);
721    properties.add(EnumProperty<DragStartBehavior>('startBehavior', dragStartBehavior));
722  }
723}
724
725/// A widget that detects gestures described by the given gesture
726/// factories.
727///
728/// For common gestures, use a [GestureRecognizer].
729/// [RawGestureDetector] is useful primarily when developing your
730/// own gesture recognizers.
731///
732/// Configuring the gesture recognizers requires a carefully constructed map, as
733/// described in [gestures] and as shown in the example below.
734///
735/// {@tool sample}
736///
737/// This example shows how to hook up a [TapGestureRecognizer]. It assumes that
738/// the code is being used inside a [State] object with a `_last` field that is
739/// then displayed as the child of the gesture detector.
740///
741/// ```dart
742/// RawGestureDetector(
743///   gestures: <Type, GestureRecognizerFactory>{
744///     TapGestureRecognizer: GestureRecognizerFactoryWithHandlers<TapGestureRecognizer>(
745///       () => TapGestureRecognizer(),
746///       (TapGestureRecognizer instance) {
747///         instance
748///           ..onTapDown = (TapDownDetails details) { setState(() { _last = 'down'; }); }
749///           ..onTapUp = (TapUpDetails details) { setState(() { _last = 'up'; }); }
750///           ..onTap = () { setState(() { _last = 'tap'; }); }
751///           ..onTapCancel = () { setState(() { _last = 'cancel'; }); };
752///       },
753///     ),
754///   },
755///   child: Container(width: 300.0, height: 300.0, color: Colors.yellow, child: Text(_last)),
756/// )
757/// ```
758/// {@end-tool}
759///
760/// See also:
761///
762///  * [GestureDetector], a less flexible but much simpler widget that does the same thing.
763///  * [Listener], a widget that reports raw pointer events.
764///  * [GestureRecognizer], the class that you extend to create a custom gesture recognizer.
765class RawGestureDetector extends StatefulWidget {
766  /// Creates a widget that detects gestures.
767  ///
768  /// Gesture detectors can contribute semantic information to the tree that is
769  /// used by assistive technology. The behavior can be configured by
770  /// [semantics], or disabled with [excludeFromSemantics].
771  const RawGestureDetector({
772    Key key,
773    this.child,
774    this.gestures = const <Type, GestureRecognizerFactory>{},
775    this.behavior,
776    this.excludeFromSemantics = false,
777    this.semantics,
778  }) : assert(gestures != null),
779       assert(excludeFromSemantics != null),
780       super(key: key);
781
782  /// The widget below this widget in the tree.
783  ///
784  /// {@macro flutter.widgets.child}
785  final Widget child;
786
787  /// The gestures that this widget will attempt to recognize.
788  ///
789  /// This should be a map from [GestureRecognizer] subclasses to
790  /// [GestureRecognizerFactory] subclasses specialized with the same type.
791  ///
792  /// This value can be late-bound at layout time using
793  /// [RawGestureDetectorState.replaceGestureRecognizers].
794  final Map<Type, GestureRecognizerFactory> gestures;
795
796  /// How this gesture detector should behave during hit testing.
797  ///
798  /// This defaults to [HitTestBehavior.deferToChild] if [child] is not null and
799  /// [HitTestBehavior.translucent] if child is null.
800  final HitTestBehavior behavior;
801
802  /// Whether to exclude these gestures from the semantics tree. For
803  /// example, the long-press gesture for showing a tooltip is
804  /// excluded because the tooltip itself is included in the semantics
805  /// tree directly and so having a gesture to show it would result in
806  /// duplication of information.
807  final bool excludeFromSemantics;
808
809  /// Describes the semantics notations that should be added to the underlying
810  /// render object [RenderSemanticsGestureHandler].
811  ///
812  /// It has no effect if [excludeFromSemantics] is true.
813  ///
814  /// When [semantics] is null, [RawGestureDetector] will fall back to a
815  /// default delegate which checks if the detector owns certain gesture
816  /// recognizers and calls their callbacks if they exist:
817  ///
818  ///  * During a semantic tap, it calls [TapGestureRecognizer]'s
819  ///    `onTapDown`, `onTapUp`, and `onTap`.
820  ///  * During a semantic long press, it calls [LongPressGestureRecognizer]'s
821  ///    `onLongPressStart`, `onLongPress`, `onLongPressEnd` and `onLongPressUp`.
822  ///  * During a semantic horizontal drag, it calls [HorizontalDragGestureRecognizer]'s
823  ///    `onDown`, `onStart`, `onUpdate` and `onEnd`, then
824  ///    [PanGestureRecognizer]'s `onDown`, `onStart`, `onUpdate` and `onEnd`.
825  ///  * During a semantic vertical drag, it calls [VerticalDragGestureRecognizer]'s
826  ///    `onDown`, `onStart`, `onUpdate` and `onEnd`, then
827  ///    [PanGestureRecognizer]'s `onDown`, `onStart`, `onUpdate` and `onEnd`.
828  ///
829  /// {@tool sample}
830  /// This custom gesture detector listens to force presses, while also allows
831  /// the same callback to be triggered by semantic long presses.
832  ///
833  /// ```dart
834  /// class ForcePressGestureDetectorWithSemantics extends StatelessWidget {
835  ///   const ForcePressGestureDetectorWithSemantics({
836  ///     this.child,
837  ///     this.onForcePress,
838  ///   });
839  ///
840  ///   final Widget child;
841  ///   final VoidCallback onForcePress;
842  ///
843  ///   @override
844  ///   Widget build(BuildContext context) {
845  ///     return RawGestureDetector(
846  ///       gestures: <Type, GestureRecognizerFactory>{
847  ///         ForcePressGestureRecognizer: GestureRecognizerFactoryWithHandlers<ForcePressGestureRecognizer>(
848  ///           () => ForcePressGestureRecognizer(debugOwner: this),
849  ///           (ForcePressGestureRecognizer instance) {
850  ///             instance.onStart = (_) => onForcePress();
851  ///           }
852  ///         ),
853  ///       },
854  ///       behavior: HitTestBehavior.opaque,
855  ///       semantics: _LongPressSemanticsDelegate(onForcePress),
856  ///       child: child,
857  ///     );
858  ///   }
859  /// }
860  ///
861  /// class _LongPressSemanticsDelegate extends SemanticsGestureDelegate {
862  ///   _LongPressSemanticsDelegate(this.onLongPress);
863  ///
864  ///   VoidCallback onLongPress;
865  ///
866  ///   @override
867  ///   void assignSemantics(RenderSemanticsGestureHandler renderObject) {
868  ///     renderObject.onLongPress = onLongPress;
869  ///   }
870  /// }
871  /// ```
872  /// {@end-tool}
873  final SemanticsGestureDelegate semantics;
874
875  @override
876  RawGestureDetectorState createState() => RawGestureDetectorState();
877}
878
879/// State for a [RawGestureDetector].
880class RawGestureDetectorState extends State<RawGestureDetector> {
881  Map<Type, GestureRecognizer> _recognizers = const <Type, GestureRecognizer>{};
882  SemanticsGestureDelegate _semantics;
883
884  @override
885  void initState() {
886    super.initState();
887    _semantics = widget.semantics ?? _DefaultSemanticsGestureDelegate(this);
888    _syncAll(widget.gestures);
889  }
890
891  @override
892  void didUpdateWidget(RawGestureDetector oldWidget) {
893    super.didUpdateWidget(oldWidget);
894    if (!(oldWidget.semantics == null && widget.semantics == null)) {
895      _semantics = widget.semantics ?? _DefaultSemanticsGestureDelegate(this);
896    }
897    _syncAll(widget.gestures);
898  }
899
900  /// This method can be called after the build phase, during the
901  /// layout of the nearest descendant [RenderObjectWidget] of the
902  /// gesture detector, to update the list of active gesture
903  /// recognizers.
904  ///
905  /// The typical use case is [Scrollable]s, which put their viewport
906  /// in their gesture detector, and then need to know the dimensions
907  /// of the viewport and the viewport's child to determine whether
908  /// the gesture detector should be enabled.
909  ///
910  /// The argument should follow the same conventions as
911  /// [RawGestureDetector.gestures]. It acts like a temporary replacement for
912  /// that value until the next build.
913  void replaceGestureRecognizers(Map<Type, GestureRecognizerFactory> gestures) {
914    assert(() {
915      if (!context.findRenderObject().owner.debugDoingLayout) {
916        throw FlutterError(
917          'Unexpected call to replaceGestureRecognizers() method of RawGestureDetectorState.\n'
918          'The replaceGestureRecognizers() method can only be called during the layout phase. '
919          'To set the gesture recognizers at other times, trigger a new build using setState() '
920          'and provide the new gesture recognizers as constructor arguments to the corresponding '
921          'RawGestureDetector or GestureDetector object.'
922        );
923      }
924      return true;
925    }());
926    _syncAll(gestures);
927    if (!widget.excludeFromSemantics) {
928      final RenderSemanticsGestureHandler semanticsGestureHandler = context.findRenderObject();
929      _updateSemanticsForRenderObject(semanticsGestureHandler);
930    }
931  }
932
933  /// This method can be called outside of the build phase to filter the list of
934  /// available semantic actions.
935  ///
936  /// The actual filtering is happening in the next frame and a frame will be
937  /// scheduled if non is pending.
938  ///
939  /// This is used by [Scrollable] to configure system accessibility tools so
940  /// that they know in which direction a particular list can be scrolled.
941  ///
942  /// If this is never called, then the actions are not filtered. If the list of
943  /// actions to filter changes, it must be called again.
944  void replaceSemanticsActions(Set<SemanticsAction> actions) {
945    assert(() {
946      final Element element = context;
947      if (element.owner.debugBuilding) {
948        throw FlutterError(
949          'Unexpected call to replaceSemanticsActions() method of RawGestureDetectorState.\n'
950          'The replaceSemanticsActions() method can only be called outside of the build phase.'
951        );
952      }
953      return true;
954    }());
955    if (!widget.excludeFromSemantics) {
956      final RenderSemanticsGestureHandler semanticsGestureHandler = context.findRenderObject();
957      semanticsGestureHandler.validActions = actions; // will call _markNeedsSemanticsUpdate(), if required.
958    }
959  }
960
961  @override
962  void dispose() {
963    for (GestureRecognizer recognizer in _recognizers.values)
964      recognizer.dispose();
965    _recognizers = null;
966    super.dispose();
967  }
968
969  void _syncAll(Map<Type, GestureRecognizerFactory> gestures) {
970    assert(_recognizers != null);
971    final Map<Type, GestureRecognizer> oldRecognizers = _recognizers;
972    _recognizers = <Type, GestureRecognizer>{};
973    for (Type type in gestures.keys) {
974      assert(gestures[type] != null);
975      assert(gestures[type]._debugAssertTypeMatches(type));
976      assert(!_recognizers.containsKey(type));
977      _recognizers[type] = oldRecognizers[type] ?? gestures[type].constructor();
978      assert(_recognizers[type].runtimeType == type, '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.');
979      gestures[type].initializer(_recognizers[type]);
980    }
981    for (Type type in oldRecognizers.keys) {
982      if (!_recognizers.containsKey(type))
983        oldRecognizers[type].dispose();
984    }
985  }
986
987  void _handlePointerDown(PointerDownEvent event) {
988    assert(_recognizers != null);
989    for (GestureRecognizer recognizer in _recognizers.values)
990      recognizer.addPointer(event);
991  }
992
993  HitTestBehavior get _defaultBehavior {
994    return widget.child == null ? HitTestBehavior.translucent : HitTestBehavior.deferToChild;
995  }
996
997  void _updateSemanticsForRenderObject(RenderSemanticsGestureHandler renderObject) {
998    assert(!widget.excludeFromSemantics);
999    assert(_semantics != null);
1000    _semantics.assignSemantics(renderObject);
1001  }
1002
1003  @override
1004  Widget build(BuildContext context) {
1005    Widget result = Listener(
1006      onPointerDown: _handlePointerDown,
1007      behavior: widget.behavior ?? _defaultBehavior,
1008      child: widget.child,
1009    );
1010    if (!widget.excludeFromSemantics)
1011      result = _GestureSemantics(
1012        child: result,
1013        assignSemantics: _updateSemanticsForRenderObject,
1014      );
1015    return result;
1016  }
1017
1018  @override
1019  void debugFillProperties(DiagnosticPropertiesBuilder properties) {
1020    super.debugFillProperties(properties);
1021    if (_recognizers == null) {
1022      properties.add(DiagnosticsNode.message('DISPOSED'));
1023    } else {
1024      final List<String> gestures = _recognizers.values.map<String>((GestureRecognizer recognizer) => recognizer.debugDescription).toList();
1025      properties.add(IterableProperty<String>('gestures', gestures, ifEmpty: '<none>'));
1026      properties.add(IterableProperty<GestureRecognizer>('recognizers', _recognizers.values, level: DiagnosticLevel.fine));
1027      properties.add(DiagnosticsProperty<bool>('excludeFromSemantics', widget.excludeFromSemantics, defaultValue: false));
1028      if (!widget.excludeFromSemantics) {
1029        properties.add(DiagnosticsProperty<SemanticsGestureDelegate>('semantics', widget.semantics, defaultValue: null));
1030      }
1031    }
1032    properties.add(EnumProperty<HitTestBehavior>('behavior', widget.behavior, defaultValue: null));
1033  }
1034}
1035
1036typedef _AssignSemantics = void Function(RenderSemanticsGestureHandler);
1037
1038class _GestureSemantics extends SingleChildRenderObjectWidget {
1039  const _GestureSemantics({
1040    Key key,
1041    Widget child,
1042    @required this.assignSemantics,
1043  }) : assert(assignSemantics != null),
1044       super(key: key, child: child);
1045
1046  final _AssignSemantics assignSemantics;
1047
1048  @override
1049  RenderSemanticsGestureHandler createRenderObject(BuildContext context) {
1050    final RenderSemanticsGestureHandler renderObject = RenderSemanticsGestureHandler();
1051    assignSemantics(renderObject);
1052    return renderObject;
1053  }
1054
1055  @override
1056  void updateRenderObject(BuildContext context, RenderSemanticsGestureHandler renderObject) {
1057    assignSemantics(renderObject);
1058  }
1059}
1060
1061/// A base class that describes what semantics notations a [RawGestureDetector]
1062/// should add to the render object [RenderSemanticsGestureHandler].
1063///
1064/// It is used to allow custom [GestureDetector]s to add semantics notations.
1065abstract class SemanticsGestureDelegate {
1066  /// Create a delegate of gesture semantics.
1067  const SemanticsGestureDelegate();
1068
1069  /// Assigns semantics notations to the [RenderSemanticsGestureHandler] render
1070  /// object of the gesture detector.
1071  ///
1072  /// This method is called when the widget is created, updated, or during
1073  /// [RawGestureDetector.replaceGestureRecognizers].
1074  void assignSemantics(RenderSemanticsGestureHandler renderObject);
1075
1076  @override
1077  String toString() => '$runtimeType()';
1078}
1079
1080// The default semantics delegate of [RawGestureDetector]. Its behavior is
1081// described in [RawGestureDetector.semantics].
1082//
1083// For readers who come here to learn how to write custom semantics delegates:
1084// this is not a proper sample code. It has access to the detector state as well
1085// as its private properties, which are inaccessible normally. It is designed
1086// this way in order to work independenly in a [RawGestureRecognizer] to
1087// preserve existing behavior.
1088//
1089// Instead, a normal delegate will store callbacks as properties, and use them
1090// in `assignSemantics`.
1091class _DefaultSemanticsGestureDelegate extends SemanticsGestureDelegate {
1092  _DefaultSemanticsGestureDelegate(this.detectorState);
1093
1094  final RawGestureDetectorState detectorState;
1095
1096  @override
1097  void assignSemantics(RenderSemanticsGestureHandler renderObject) {
1098    assert(!detectorState.widget.excludeFromSemantics);
1099    final Map<Type, GestureRecognizer> recognizers = detectorState._recognizers;
1100    renderObject
1101      ..onTap = _getTapHandler(recognizers)
1102      ..onLongPress = _getLongPressHandler(recognizers)
1103      ..onHorizontalDragUpdate = _getHorizontalDragUpdateHandler(recognizers)
1104      ..onVerticalDragUpdate = _getVerticalDragUpdateHandler(recognizers);
1105  }
1106
1107  GestureTapCallback _getTapHandler(Map<Type, GestureRecognizer> recognizers) {
1108    final TapGestureRecognizer tap = recognizers[TapGestureRecognizer];
1109    if (tap == null)
1110      return null;
1111    assert(tap is TapGestureRecognizer);
1112
1113    return () {
1114      assert(tap != null);
1115      if (tap.onTapDown != null)
1116        tap.onTapDown(TapDownDetails());
1117      if (tap.onTapUp != null)
1118        tap.onTapUp(TapUpDetails());
1119      if (tap.onTap != null)
1120        tap.onTap();
1121    };
1122  }
1123
1124  GestureLongPressCallback _getLongPressHandler(Map<Type, GestureRecognizer> recognizers) {
1125    final LongPressGestureRecognizer longPress = recognizers[LongPressGestureRecognizer];
1126    if (longPress == null)
1127      return null;
1128
1129    return () {
1130      assert(longPress is LongPressGestureRecognizer);
1131      if (longPress.onLongPressStart != null)
1132        longPress.onLongPressStart(const LongPressStartDetails());
1133      if (longPress.onLongPress != null)
1134        longPress.onLongPress();
1135      if (longPress.onLongPressEnd != null)
1136        longPress.onLongPressEnd(const LongPressEndDetails());
1137      if (longPress.onLongPressUp != null)
1138        longPress.onLongPressUp();
1139    };
1140  }
1141
1142  GestureDragUpdateCallback _getHorizontalDragUpdateHandler(Map<Type, GestureRecognizer> recognizers) {
1143    final HorizontalDragGestureRecognizer horizontal = recognizers[HorizontalDragGestureRecognizer];
1144    final PanGestureRecognizer pan = recognizers[PanGestureRecognizer];
1145
1146    final GestureDragUpdateCallback horizontalHandler = horizontal == null ?
1147      null :
1148      (DragUpdateDetails details) {
1149        assert(horizontal is HorizontalDragGestureRecognizer);
1150        if (horizontal.onDown != null)
1151          horizontal.onDown(DragDownDetails());
1152        if (horizontal.onStart != null)
1153          horizontal.onStart(DragStartDetails());
1154        if (horizontal.onUpdate != null)
1155          horizontal.onUpdate(details);
1156        if (horizontal.onEnd != null)
1157          horizontal.onEnd(DragEndDetails(primaryVelocity: 0.0));
1158      };
1159
1160    final GestureDragUpdateCallback panHandler = pan == null ?
1161      null :
1162      (DragUpdateDetails details) {
1163        assert(pan is PanGestureRecognizer);
1164        if (pan.onDown != null)
1165          pan.onDown(DragDownDetails());
1166        if (pan.onStart != null)
1167          pan.onStart(DragStartDetails());
1168        if (pan.onUpdate != null)
1169          pan.onUpdate(details);
1170        if (pan.onEnd != null)
1171          pan.onEnd(DragEndDetails());
1172      };
1173
1174    if (horizontalHandler == null && panHandler == null)
1175      return null;
1176    return (DragUpdateDetails details) {
1177      if (horizontalHandler != null)
1178        horizontalHandler(details);
1179      if (panHandler != null)
1180        panHandler(details);
1181    };
1182  }
1183
1184  GestureDragUpdateCallback _getVerticalDragUpdateHandler(Map<Type, GestureRecognizer> recognizers) {
1185    final VerticalDragGestureRecognizer vertical = recognizers[VerticalDragGestureRecognizer];
1186    final PanGestureRecognizer pan = recognizers[PanGestureRecognizer];
1187
1188    final GestureDragUpdateCallback verticalHandler = vertical == null ?
1189      null :
1190      (DragUpdateDetails details) {
1191        assert(vertical is VerticalDragGestureRecognizer);
1192        if (vertical.onDown != null)
1193          vertical.onDown(DragDownDetails());
1194        if (vertical.onStart != null)
1195          vertical.onStart(DragStartDetails());
1196        if (vertical.onUpdate != null)
1197          vertical.onUpdate(details);
1198        if (vertical.onEnd != null)
1199          vertical.onEnd(DragEndDetails(primaryVelocity: 0.0));
1200      };
1201
1202    final GestureDragUpdateCallback panHandler = pan == null ?
1203      null :
1204      (DragUpdateDetails details) {
1205        assert(pan is PanGestureRecognizer);
1206        if (pan.onDown != null)
1207          pan.onDown(DragDownDetails());
1208        if (pan.onStart != null)
1209          pan.onStart(DragStartDetails());
1210        if (pan.onUpdate != null)
1211          pan.onUpdate(details);
1212        if (pan.onEnd != null)
1213          pan.onEnd(DragEndDetails());
1214      };
1215
1216    if (verticalHandler == null && panHandler == null)
1217      return null;
1218    return (DragUpdateDetails details) {
1219      if (verticalHandler != null)
1220        verticalHandler(details);
1221      if (panHandler != null)
1222        panHandler(details);
1223    };
1224  }
1225}
1226