blob: 8cebe17a62c669ca5eb4ade3da04630d23c2b950 [file] [log] [blame]
// Copyright 2015 The Chromium 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:flutter/gestures.dart';
import 'package:flutter/rendering.dart';
import 'basic.dart';
import 'framework.dart';
export 'package:flutter/gestures.dart' show
DragDownDetails,
DragStartDetails,
DragUpdateDetails,
DragEndDetails,
GestureTapDownCallback,
GestureTapUpCallback,
GestureTapCallback,
GestureTapCancelCallback,
GestureLongPressCallback,
GestureDragDownCallback,
GestureDragStartCallback,
GestureDragUpdateCallback,
GestureDragEndCallback,
GestureDragCancelCallback,
GestureScaleStartCallback,
GestureScaleUpdateCallback,
GestureScaleEndCallback,
ScaleStartDetails,
ScaleUpdateDetails,
ScaleEndDetails,
TapDownDetails,
TapUpDetails,
Velocity;
/// Factory for creating gesture recognizers.
///
/// `T` is the type of gesture recognizer this class manages.
///
/// Used by [RawGestureDetector.gestures].
@optionalTypeArgs
abstract class GestureRecognizerFactory<T extends GestureRecognizer> {
/// Abstract const constructor. This constructor enables subclasses to provide
/// const constructors so that they can be used in const expressions.
const GestureRecognizerFactory();
/// Must return an instance of T.
T constructor();
/// Must configure the given instance (which will have been created by
/// `constructor`).
///
/// This normally means setting the callbacks.
void initializer(T instance);
bool _debugAssertTypeMatches(Type type) {
assert(type == T, 'GestureRecognizerFactory of type $T was used where type $type was specified.');
return true;
}
}
/// Signature for closures that implement [GestureRecognizerFactory.constructor].
typedef T GestureRecognizerFactoryConstructor<T extends GestureRecognizer>();
/// Signature for closures that implement [GestureRecognizerFactory.initializer].
typedef void GestureRecognizerFactoryInitializer<T extends GestureRecognizer>(T instance);
/// Factory for creating gesture recognizers that delegates to callbacks.
///
/// Used by [RawGestureDetector.gestures].
class GestureRecognizerFactoryWithHandlers<T extends GestureRecognizer> extends GestureRecognizerFactory<T> {
/// Creates a gesture recognizer factory with the given callbacks.
///
/// The arguments must not be null.
const GestureRecognizerFactoryWithHandlers(this._constructor, this._initializer) :
assert(_constructor != null),
assert(_initializer != null);
final GestureRecognizerFactoryConstructor<T> _constructor;
final GestureRecognizerFactoryInitializer<T> _initializer;
@override
T constructor() => _constructor();
@override
void initializer(T instance) => _initializer(instance);
}
/// A widget that detects gestures.
///
/// Attempts to recognize gestures that correspond to its non-null callbacks.
///
/// If this widget has a child, it defers to that child for its sizing behavior.
/// If it does not have a child, it grows to fit the parent instead.
///
/// By default a GestureDetector with an invisible child ignores touches;
/// this behavior can be controlled with [behavior].
///
/// GestureDetector also listens for accessibility events and maps
/// them to the callbacks. To ignore accessibility events, set
/// [excludeFromSemantics] to true.
///
/// See <http://flutter.io/gestures/> for additional information.
///
/// Material design applications typically react to touches with ink splash
/// effects. The [InkWell] class implements this effect and can be used in place
/// of a [GestureDetector] for handling taps.
///
/// ## Sample code
///
/// This example makes a rectangle react to being tapped by setting the
/// `_lights` field:
///
/// ```dart
/// new GestureDetector(
/// onTap: () {
/// setState(() { _lights = true; });
/// },
/// child: new Container(
/// color: Colors.yellow,
/// child: new Text('TURN LIGHTS ON'),
/// ),
/// )
/// ```
class GestureDetector extends StatelessWidget {
/// Creates a widget that detects gestures.
///
/// Pan and scale callbacks cannot be used simultaneously because scale is a
/// superset of pan. Simply use the scale callbacks instead.
///
/// Horizontal and vertical drag callbacks cannot be used simultaneously
/// because a combination of a horizontal and vertical drag is a pan. Simply
/// use the pan callbacks instead.
///
/// By default, gesture detectors contribute semantic information to the tree
/// that is used by assistive technology.
GestureDetector({
Key key,
this.child,
this.onTapDown,
this.onTapUp,
this.onTap,
this.onTapCancel,
this.onDoubleTap,
this.onLongPress,
this.onVerticalDragDown,
this.onVerticalDragStart,
this.onVerticalDragUpdate,
this.onVerticalDragEnd,
this.onVerticalDragCancel,
this.onHorizontalDragDown,
this.onHorizontalDragStart,
this.onHorizontalDragUpdate,
this.onHorizontalDragEnd,
this.onHorizontalDragCancel,
this.onPanDown,
this.onPanStart,
this.onPanUpdate,
this.onPanEnd,
this.onPanCancel,
this.onScaleStart,
this.onScaleUpdate,
this.onScaleEnd,
this.behavior,
this.excludeFromSemantics: false
}) : assert(excludeFromSemantics != null),
assert(() {
final bool haveVerticalDrag = onVerticalDragStart != null || onVerticalDragUpdate != null || onVerticalDragEnd != null;
final bool haveHorizontalDrag = onHorizontalDragStart != null || onHorizontalDragUpdate != null || onHorizontalDragEnd != null;
final bool havePan = onPanStart != null || onPanUpdate != null || onPanEnd != null;
final bool haveScale = onScaleStart != null || onScaleUpdate != null || onScaleEnd != null;
if (havePan || haveScale) {
if (havePan && haveScale) {
throw new FlutterError(
'Incorrect GestureDetector arguments.\n'
'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.'
);
}
final String recognizer = havePan ? 'pan' : 'scale';
if (haveVerticalDrag && haveHorizontalDrag) {
throw new FlutterError(
'Incorrect GestureDetector arguments.\n'
'Simultaneously having a vertical drag gesture recognizer, a horizontal drag gesture recognizer, and a $recognizer gesture recognizer '
'will result in the $recognizer gesture recognizer being ignored, since the other two will catch all drags.'
);
}
}
return true;
}()),
super(key: key);
/// The widget below this widget in the tree.
///
/// {@macro flutter.widgets.child}
final Widget child;
/// A pointer that might cause a tap has contacted the screen at a particular
/// location.
final GestureTapDownCallback onTapDown;
/// A pointer that will trigger a tap has stopped contacting the screen at a
/// particular location.
final GestureTapUpCallback onTapUp;
/// A tap has occurred.
final GestureTapCallback onTap;
/// The pointer that previously triggered [onTapDown] will not end up causing
/// a tap.
final GestureTapCancelCallback onTapCancel;
/// The user has tapped the screen at the same location twice in quick
/// succession.
final GestureTapCallback onDoubleTap;
/// A pointer has remained in contact with the screen at the same location for
/// a long period of time.
final GestureLongPressCallback onLongPress;
/// A pointer has contacted the screen and might begin to move vertically.
final GestureDragDownCallback onVerticalDragDown;
/// A pointer has contacted the screen and has begun to move vertically.
final GestureDragStartCallback onVerticalDragStart;
/// A pointer that is in contact with the screen and moving vertically has
/// moved in the vertical direction.
final GestureDragUpdateCallback onVerticalDragUpdate;
/// A pointer that was previously in contact with the screen and moving
/// vertically is no longer in contact with the screen and was moving at a
/// specific velocity when it stopped contacting the screen.
final GestureDragEndCallback onVerticalDragEnd;
/// The pointer that previously triggered [onVerticalDragDown] did not
/// complete.
final GestureDragCancelCallback onVerticalDragCancel;
/// A pointer has contacted the screen and might begin to move horizontally.
final GestureDragDownCallback onHorizontalDragDown;
/// A pointer has contacted the screen and has begun to move horizontally.
final GestureDragStartCallback onHorizontalDragStart;
/// A pointer that is in contact with the screen and moving horizontally has
/// moved in the horizontal direction.
final GestureDragUpdateCallback onHorizontalDragUpdate;
/// A pointer that was previously in contact with the screen and moving
/// horizontally is no longer in contact with the screen and was moving at a
/// specific velocity when it stopped contacting the screen.
final GestureDragEndCallback onHorizontalDragEnd;
/// The pointer that previously triggered [onHorizontalDragDown] did not
/// complete.
final GestureDragCancelCallback onHorizontalDragCancel;
/// A pointer has contacted the screen and might begin to move.
final GestureDragDownCallback onPanDown;
/// A pointer has contacted the screen and has begun to move.
final GestureDragStartCallback onPanStart;
/// A pointer that is in contact with the screen and moving has moved again.
final GestureDragUpdateCallback onPanUpdate;
/// A pointer that was previously in contact with the screen and moving
/// is no longer in contact with the screen and was moving at a specific
/// velocity when it stopped contacting the screen.
final GestureDragEndCallback onPanEnd;
/// The pointer that previously triggered [onPanDown] did not complete.
final GestureDragCancelCallback onPanCancel;
/// The pointers in contact with the screen have established a focal point and
/// initial scale of 1.0.
final GestureScaleStartCallback onScaleStart;
/// The pointers in contact with the screen have indicated a new focal point
/// and/or scale.
final GestureScaleUpdateCallback onScaleUpdate;
/// The pointers are no longer in contact with the screen.
final GestureScaleEndCallback onScaleEnd;
/// How this gesture detector should behave during hit testing.
final HitTestBehavior behavior;
/// Whether to exclude these gestures from the semantics tree. For
/// example, the long-press gesture for showing a tooltip is
/// excluded because the tooltip itself is included in the semantics
/// tree directly and so having a gesture to show it would result in
/// duplication of information.
final bool excludeFromSemantics;
@override
Widget build(BuildContext context) {
final Map<Type, GestureRecognizerFactory> gestures = <Type, GestureRecognizerFactory>{};
if (onTapDown != null || onTapUp != null || onTap != null || onTapCancel != null) {
gestures[TapGestureRecognizer] = new GestureRecognizerFactoryWithHandlers<TapGestureRecognizer>(
() => new TapGestureRecognizer(debugOwner: this),
(TapGestureRecognizer instance) {
instance
..onTapDown = onTapDown
..onTapUp = onTapUp
..onTap = onTap
..onTapCancel = onTapCancel;
},
);
}
if (onDoubleTap != null) {
gestures[DoubleTapGestureRecognizer] = new GestureRecognizerFactoryWithHandlers<DoubleTapGestureRecognizer>(
() => new DoubleTapGestureRecognizer(debugOwner: this),
(DoubleTapGestureRecognizer instance) {
instance
..onDoubleTap = onDoubleTap;
},
);
}
if (onLongPress != null) {
gestures[LongPressGestureRecognizer] = new GestureRecognizerFactoryWithHandlers<LongPressGestureRecognizer>(
() => new LongPressGestureRecognizer(debugOwner: this),
(LongPressGestureRecognizer instance) {
instance
..onLongPress = onLongPress;
},
);
}
if (onVerticalDragDown != null ||
onVerticalDragStart != null ||
onVerticalDragUpdate != null ||
onVerticalDragEnd != null ||
onVerticalDragCancel != null) {
gestures[VerticalDragGestureRecognizer] = new GestureRecognizerFactoryWithHandlers<VerticalDragGestureRecognizer>(
() => new VerticalDragGestureRecognizer(debugOwner: this),
(VerticalDragGestureRecognizer instance) {
instance
..onDown = onVerticalDragDown
..onStart = onVerticalDragStart
..onUpdate = onVerticalDragUpdate
..onEnd = onVerticalDragEnd
..onCancel = onVerticalDragCancel;
},
);
}
if (onHorizontalDragDown != null ||
onHorizontalDragStart != null ||
onHorizontalDragUpdate != null ||
onHorizontalDragEnd != null ||
onHorizontalDragCancel != null) {
gestures[HorizontalDragGestureRecognizer] = new GestureRecognizerFactoryWithHandlers<HorizontalDragGestureRecognizer>(
() => new HorizontalDragGestureRecognizer(debugOwner: this),
(HorizontalDragGestureRecognizer instance) {
instance
..onDown = onHorizontalDragDown
..onStart = onHorizontalDragStart
..onUpdate = onHorizontalDragUpdate
..onEnd = onHorizontalDragEnd
..onCancel = onHorizontalDragCancel;
},
);
}
if (onPanDown != null ||
onPanStart != null ||
onPanUpdate != null ||
onPanEnd != null ||
onPanCancel != null) {
gestures[PanGestureRecognizer] = new GestureRecognizerFactoryWithHandlers<PanGestureRecognizer>(
() => new PanGestureRecognizer(debugOwner: this),
(PanGestureRecognizer instance) {
instance
..onDown = onPanDown
..onStart = onPanStart
..onUpdate = onPanUpdate
..onEnd = onPanEnd
..onCancel = onPanCancel;
},
);
}
if (onScaleStart != null || onScaleUpdate != null || onScaleEnd != null) {
gestures[ScaleGestureRecognizer] = new GestureRecognizerFactoryWithHandlers<ScaleGestureRecognizer>(
() => new ScaleGestureRecognizer(debugOwner: this),
(ScaleGestureRecognizer instance) {
instance
..onStart = onScaleStart
..onUpdate = onScaleUpdate
..onEnd = onScaleEnd;
},
);
}
return new RawGestureDetector(
gestures: gestures,
behavior: behavior,
excludeFromSemantics: excludeFromSemantics,
child: child,
);
}
}
/// A widget that detects gestures described by the given gesture
/// factories.
///
/// For common gestures, use a [GestureRecognizer].
/// [RawGestureDetector] is useful primarily when developing your
/// own gesture recognizers.
///
/// Configuring the gesture recognizers requires a carefully constructed map, as
/// described in [gestures] and as shown in the example below.
///
/// ## Sample code
///
/// This example shows how to hook up a [TapGestureRecognizer]. It assumes that
/// the code is being used inside a [State] object with a `_last` field that is
/// then displayed as the child of the gesture detector.
///
/// ```dart
/// new RawGestureDetector(
/// gestures: <Type, GestureRecognizerFactory>{
/// TapGestureRecognizer: new GestureRecognizerFactoryWithHandlers<TapGestureRecognizer>(
/// () => new TapGestureRecognizer(),
/// (TapGestureRecognizer instance) {
/// instance
/// ..onTapDown = (TapDownDetails details) { setState(() { _last = 'down'; }); }
/// ..onTapUp = (TapUpDetails details) { setState(() { _last = 'up'; }); }
/// ..onTap = () { setState(() { _last = 'tap'; }); }
/// ..onTapCancel = () { setState(() { _last = 'cancel'; }); };
/// },
/// ),
/// },
/// child: new Container(width: 300.0, height: 300.0, color: Colors.yellow, child: new Text(_last)),
/// )
/// ```
///
/// See also:
///
/// * [GestureDetector], a less flexible but much simpler widget that does the same thing.
/// * [Listener], a widget that reports raw pointer events.
/// * [GestureRecognizer], the class that you extend to create a custom gesture recognizer.
class RawGestureDetector extends StatefulWidget {
/// Creates a widget that detects gestures.
///
/// By default, gesture detectors contribute semantic information to the tree
/// that is used by assistive technology. This can be controlled using
/// [excludeFromSemantics].
const RawGestureDetector({
Key key,
this.child,
this.gestures: const <Type, GestureRecognizerFactory>{},
this.behavior,
this.excludeFromSemantics: false
}) : assert(gestures != null),
assert(excludeFromSemantics != null),
super(key: key);
/// The widget below this widget in the tree.
///
/// {@macro flutter.widgets.child}
final Widget child;
/// The gestures that this widget will attempt to recognize.
///
/// This should be a map from [GestureRecognizer] subclasses to
/// [GestureRecognizerFactory] subclasses specialized with the same type.
///
/// This value can be late-bound at layout time using
/// [RawGestureDetectorState.replaceGestureRecognizers].
final Map<Type, GestureRecognizerFactory> gestures;
/// How this gesture detector should behave during hit testing.
final HitTestBehavior behavior;
/// Whether to exclude these gestures from the semantics tree. For
/// example, the long-press gesture for showing a tooltip is
/// excluded because the tooltip itself is included in the semantics
/// tree directly and so having a gesture to show it would result in
/// duplication of information.
final bool excludeFromSemantics;
@override
RawGestureDetectorState createState() => new RawGestureDetectorState();
}
/// State for a [RawGestureDetector].
class RawGestureDetectorState extends State<RawGestureDetector> {
Map<Type, GestureRecognizer> _recognizers = const <Type, GestureRecognizer>{};
@override
void initState() {
super.initState();
_syncAll(widget.gestures);
}
@override
void didUpdateWidget(RawGestureDetector oldWidget) {
super.didUpdateWidget(oldWidget);
_syncAll(widget.gestures);
}
/// This method can be called after the build phase, during the
/// layout of the nearest descendant [RenderObjectWidget] of the
/// gesture detector, to update the list of active gesture
/// recognizers.
///
/// The typical use case is [Scrollable]s, which put their viewport
/// in their gesture detector, and then need to know the dimensions
/// of the viewport and the viewport's child to determine whether
/// the gesture detector should be enabled.
///
/// The argument should follow the same conventions as
/// [RawGestureDetector.gestures]. It acts like a temporary replacement for
/// that value until the next build.
void replaceGestureRecognizers(Map<Type, GestureRecognizerFactory> gestures) {
assert(() {
if (!context.findRenderObject().owner.debugDoingLayout) {
throw new FlutterError(
'Unexpected call to replaceGestureRecognizers() method of RawGestureDetectorState.\n'
'The replaceGestureRecognizers() method can only be called during the layout phase. '
'To set the gesture recognizers at other times, trigger a new build using setState() '
'and provide the new gesture recognizers as constructor arguments to the corresponding '
'RawGestureDetector or GestureDetector object.'
);
}
return true;
}());
_syncAll(gestures);
if (!widget.excludeFromSemantics) {
final RenderSemanticsGestureHandler semanticsGestureHandler = context.findRenderObject();
context.visitChildElements((Element element) {
final _GestureSemantics widget = element.widget;
widget._updateHandlers(semanticsGestureHandler);
});
}
}
/// This method can be called outside of the build phase to filter the list of
/// available semantic actions.
///
/// The actual filtering is happening in the next frame and a frame will be
/// scheduled if non is pending.
///
/// This is used by [Scrollable] to configure system accessibility tools so
/// that they know in which direction a particular list can be scrolled.
///
/// If this is never called, then the actions are not filtered. If the list of
/// actions to filter changes, it must be called again.
void replaceSemanticsActions(Set<SemanticsAction> actions) {
assert(() {
final Element element = context;
if (element.owner.debugBuilding) {
throw new FlutterError(
'Unexpected call to replaceSemanticsActions() method of RawGestureDetectorState.\n'
'The replaceSemanticsActions() method can only be called outside of the build phase.'
);
}
return true;
}());
if (!widget.excludeFromSemantics) {
final RenderSemanticsGestureHandler semanticsGestureHandler = context.findRenderObject();
semanticsGestureHandler.validActions = actions; // will call _markNeedsSemanticsUpdate(), if required.
}
}
@override
void dispose() {
for (GestureRecognizer recognizer in _recognizers.values)
recognizer.dispose();
_recognizers = null;
super.dispose();
}
void _syncAll(Map<Type, GestureRecognizerFactory> gestures) {
assert(_recognizers != null);
final Map<Type, GestureRecognizer> oldRecognizers = _recognizers;
_recognizers = <Type, GestureRecognizer>{};
for (Type type in gestures.keys) {
assert(gestures[type] != null);
assert(gestures[type]._debugAssertTypeMatches(type));
assert(!_recognizers.containsKey(type));
_recognizers[type] = oldRecognizers[type] ?? gestures[type].constructor();
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.');
gestures[type].initializer(_recognizers[type]);
}
for (Type type in oldRecognizers.keys) {
if (!_recognizers.containsKey(type))
oldRecognizers[type].dispose();
}
}
void _handlePointerDown(PointerDownEvent event) {
assert(_recognizers != null);
for (GestureRecognizer recognizer in _recognizers.values)
recognizer.addPointer(event);
}
HitTestBehavior get _defaultBehavior {
return widget.child == null ? HitTestBehavior.translucent : HitTestBehavior.deferToChild;
}
void _handleSemanticsTap() {
final TapGestureRecognizer recognizer = _recognizers[TapGestureRecognizer];
assert(recognizer != null);
if (recognizer.onTapDown != null)
recognizer.onTapDown(new TapDownDetails());
if (recognizer.onTapUp != null)
recognizer.onTapUp(new TapUpDetails());
if (recognizer.onTap != null)
recognizer.onTap();
}
void _handleSemanticsLongPress() {
final LongPressGestureRecognizer recognizer = _recognizers[LongPressGestureRecognizer];
assert(recognizer != null);
if (recognizer.onLongPress != null)
recognizer.onLongPress();
}
void _handleSemanticsHorizontalDragUpdate(DragUpdateDetails updateDetails) {
{
final HorizontalDragGestureRecognizer recognizer = _recognizers[HorizontalDragGestureRecognizer];
if (recognizer != null) {
if (recognizer.onDown != null)
recognizer.onDown(new DragDownDetails());
if (recognizer.onStart != null)
recognizer.onStart(new DragStartDetails());
if (recognizer.onUpdate != null)
recognizer.onUpdate(updateDetails);
if (recognizer.onEnd != null)
recognizer.onEnd(new DragEndDetails(primaryVelocity: 0.0));
return;
}
}
{
final PanGestureRecognizer recognizer = _recognizers[PanGestureRecognizer];
if (recognizer != null) {
if (recognizer.onDown != null)
recognizer.onDown(new DragDownDetails());
if (recognizer.onStart != null)
recognizer.onStart(new DragStartDetails());
if (recognizer.onUpdate != null)
recognizer.onUpdate(updateDetails);
if (recognizer.onEnd != null)
recognizer.onEnd(new DragEndDetails());
return;
}
}
}
void _handleSemanticsVerticalDragUpdate(DragUpdateDetails updateDetails) {
{
final VerticalDragGestureRecognizer recognizer = _recognizers[VerticalDragGestureRecognizer];
if (recognizer != null) {
if (recognizer.onDown != null)
recognizer.onDown(new DragDownDetails());
if (recognizer.onStart != null)
recognizer.onStart(new DragStartDetails());
if (recognizer.onUpdate != null)
recognizer.onUpdate(updateDetails);
if (recognizer.onEnd != null)
recognizer.onEnd(new DragEndDetails(primaryVelocity: 0.0));
return;
}
}
{
final PanGestureRecognizer recognizer = _recognizers[PanGestureRecognizer];
if (recognizer != null) {
if (recognizer.onDown != null)
recognizer.onDown(new DragDownDetails());
if (recognizer.onStart != null)
recognizer.onStart(new DragStartDetails());
if (recognizer.onUpdate != null)
recognizer.onUpdate(updateDetails);
if (recognizer.onEnd != null)
recognizer.onEnd(new DragEndDetails());
return;
}
}
}
@override
Widget build(BuildContext context) {
Widget result = new Listener(
onPointerDown: _handlePointerDown,
behavior: widget.behavior ?? _defaultBehavior,
child: widget.child
);
if (!widget.excludeFromSemantics)
result = new _GestureSemantics(owner: this, child: result);
return result;
}
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
if (_recognizers == null) {
properties.add(new DiagnosticsNode.message('DISPOSED'));
} else {
final List<String> gestures = _recognizers.values.map<String>((GestureRecognizer recognizer) => recognizer.debugDescription).toList();
properties.add(new IterableProperty<String>('gestures', gestures, ifEmpty: '<none>'));
properties.add(new IterableProperty<GestureRecognizer>('recognizers', _recognizers.values, level: DiagnosticLevel.fine));
}
properties.add(new EnumProperty<HitTestBehavior>('behavior', widget.behavior, defaultValue: null));
}
}
class _GestureSemantics extends SingleChildRenderObjectWidget {
const _GestureSemantics({
Key key,
Widget child,
this.owner
}) : super(key: key, child: child);
final RawGestureDetectorState owner;
@override
RenderSemanticsGestureHandler createRenderObject(BuildContext context) {
return new RenderSemanticsGestureHandler(
onTap: _onTapHandler,
onLongPress: _onLongPressHandler,
onHorizontalDragUpdate: _onHorizontalDragUpdateHandler,
onVerticalDragUpdate: _onVerticalDragUpdateHandler,
);
}
void _updateHandlers(RenderSemanticsGestureHandler renderObject) {
renderObject
..onTap = _onTapHandler
..onLongPress = _onLongPressHandler
..onHorizontalDragUpdate = _onHorizontalDragUpdateHandler
..onVerticalDragUpdate = _onVerticalDragUpdateHandler;
}
@override
void updateRenderObject(BuildContext context, RenderSemanticsGestureHandler renderObject) {
_updateHandlers(renderObject);
}
GestureTapCallback get _onTapHandler {
return owner._recognizers.containsKey(TapGestureRecognizer) ? owner._handleSemanticsTap : null;
}
GestureTapCallback get _onLongPressHandler {
return owner._recognizers.containsKey(LongPressGestureRecognizer) ? owner._handleSemanticsLongPress : null;
}
GestureDragUpdateCallback get _onHorizontalDragUpdateHandler {
return owner._recognizers.containsKey(HorizontalDragGestureRecognizer) ||
owner._recognizers.containsKey(PanGestureRecognizer) ? owner._handleSemanticsHorizontalDragUpdate : null;
}
GestureDragUpdateCallback get _onVerticalDragUpdateHandler {
return owner._recognizers.containsKey(VerticalDragGestureRecognizer) ||
owner._recognizers.containsKey(PanGestureRecognizer) ? owner._handleSemanticsVerticalDragUpdate : null;
}
}