Change RawGestureDetector API to be better for strong mode Dart. (#10553)

This commit is contained in:
Ian Hickson 2017-06-07 18:04:46 -07:00 committed by GitHub
parent b5365d9352
commit 46b316c490
4 changed files with 216 additions and 84 deletions

View file

@ -189,10 +189,13 @@ class _PointDemoState extends State<_PointDemo> {
return new RawGestureDetector(
behavior: _dragTarget == null ? HitTestBehavior.deferToChild : HitTestBehavior.opaque,
gestures: <Type, GestureRecognizerFactory>{
ImmediateMultiDragGestureRecognizer: (ImmediateMultiDragGestureRecognizer recognizer) { // ignore: map_value_type_not_assignable, https://github.com/flutter/flutter/issues/5771
return (recognizer ??= new ImmediateMultiDragGestureRecognizer())
..onStart = _handleOnStart;
}
ImmediateMultiDragGestureRecognizer: new GestureRecognizerFactoryWithHandlers<ImmediateMultiDragGestureRecognizer>(
() => new ImmediateMultiDragGestureRecognizer(),
(ImmediateMultiDragGestureRecognizer instance) {
instance
..onStart = _handleOnStart;
},
),
},
child: new ClipRect(
child: new CustomPaint(
@ -359,10 +362,13 @@ class _RectangleDemoState extends State<_RectangleDemo> {
return new RawGestureDetector(
behavior: _dragTarget == null ? HitTestBehavior.deferToChild : HitTestBehavior.opaque,
gestures: <Type, GestureRecognizerFactory>{
ImmediateMultiDragGestureRecognizer: (ImmediateMultiDragGestureRecognizer recognizer) { // ignore: map_value_type_not_assignable, https://github.com/flutter/flutter/issues/5771
return (recognizer ??= new ImmediateMultiDragGestureRecognizer())
..onStart = _handleOnStart;
}
ImmediateMultiDragGestureRecognizer: new GestureRecognizerFactoryWithHandlers<ImmediateMultiDragGestureRecognizer>(
() => new ImmediateMultiDragGestureRecognizer(),
(ImmediateMultiDragGestureRecognizer instance) {
instance
..onStart = _handleOnStart;
},
),
},
child: new ClipRect(
child: new CustomPaint(

View file

@ -721,18 +721,22 @@ class ScaffoldState extends State<Scaffold> with TickerProviderStateMixin {
NavigationGestureController _backGestureController;
bool _shouldHandleBackGesture() {
assert(mounted);
return Theme.of(context).platform == TargetPlatform.iOS && Navigator.canPop(context);
}
void _handleDragStart(DragStartDetails details) {
assert(mounted);
_backGestureController = Navigator.of(context).startPopGesture();
}
void _handleDragUpdate(DragUpdateDetails details) {
assert(mounted);
_backGestureController?.dragUpdate(details.primaryDelta / context.size.width);
}
void _handleDragEnd(DragEndDetails details) {
assert(mounted);
final bool willPop = _backGestureController?.dragEnd(details.velocity.pixelsPerSecond.dx / context.size.width) ?? false;
if (willPop)
_currentBottomSheet?.close();
@ -740,6 +744,7 @@ class ScaffoldState extends State<Scaffold> with TickerProviderStateMixin {
}
void _handleDragCancel() {
assert(mounted);
final bool willPop = _backGestureController?.dragEnd(0.0) ?? false;
if (willPop)
_currentBottomSheet?.close();

View file

@ -2,6 +2,7 @@
// 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';
@ -33,13 +34,59 @@ export 'package:flutter/gestures.dart' show
TapUpDetails,
Velocity;
/// Signature for creating gesture recognizers.
/// Factory for creating gesture recognizers.
///
/// The `recognizer` argument is the gesture recognizer that currently occupies
/// the slot for which a gesture recognizer is being created.
/// `T` is the type of gesture recognizer this class manages.
///
/// Used by [RawGestureDetector.gestures].
typedef GestureRecognizer GestureRecognizerFactory(GestureRecognizer recognizer);
@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.
///
@ -231,27 +278,36 @@ class GestureDetector extends StatelessWidget {
final Map<Type, GestureRecognizerFactory> gestures = <Type, GestureRecognizerFactory>{};
if (onTapDown != null || onTapUp != null || onTap != null || onTapCancel != null) {
gestures[TapGestureRecognizer] = (TapGestureRecognizer recognizer) { // ignore: invalid_assignment, https://github.com/flutter/flutter/issues/5771
return (recognizer ??= new TapGestureRecognizer())
..onTapDown = onTapDown
..onTapUp = onTapUp
..onTap = onTap
..onTapCancel = onTapCancel;
};
gestures[TapGestureRecognizer] = new GestureRecognizerFactoryWithHandlers<TapGestureRecognizer>(
() => new TapGestureRecognizer(),
(TapGestureRecognizer instance) {
instance
..onTapDown = onTapDown
..onTapUp = onTapUp
..onTap = onTap
..onTapCancel = onTapCancel;
},
);
}
if (onDoubleTap != null) {
gestures[DoubleTapGestureRecognizer] = (DoubleTapGestureRecognizer recognizer) { // ignore: invalid_assignment, https://github.com/flutter/flutter/issues/5771
return (recognizer ??= new DoubleTapGestureRecognizer())
..onDoubleTap = onDoubleTap;
};
gestures[DoubleTapGestureRecognizer] = new GestureRecognizerFactoryWithHandlers<DoubleTapGestureRecognizer>(
() => new DoubleTapGestureRecognizer(),
(DoubleTapGestureRecognizer instance) {
instance
..onDoubleTap = onDoubleTap;
},
);
}
if (onLongPress != null) {
gestures[LongPressGestureRecognizer] = (LongPressGestureRecognizer recognizer) { // ignore: invalid_assignment, https://github.com/flutter/flutter/issues/5771
return (recognizer ??= new LongPressGestureRecognizer())
..onLongPress = onLongPress;
};
gestures[LongPressGestureRecognizer] = new GestureRecognizerFactoryWithHandlers<LongPressGestureRecognizer>(
() => new LongPressGestureRecognizer(),
(LongPressGestureRecognizer instance) {
instance
..onLongPress = onLongPress;
},
);
}
if (onVerticalDragDown != null ||
@ -259,14 +315,17 @@ class GestureDetector extends StatelessWidget {
onVerticalDragUpdate != null ||
onVerticalDragEnd != null ||
onVerticalDragCancel != null) {
gestures[VerticalDragGestureRecognizer] = (VerticalDragGestureRecognizer recognizer) { // ignore: invalid_assignment, https://github.com/flutter/flutter/issues/5771
return (recognizer ??= new VerticalDragGestureRecognizer())
..onDown = onVerticalDragDown
..onStart = onVerticalDragStart
..onUpdate = onVerticalDragUpdate
..onEnd = onVerticalDragEnd
..onCancel = onVerticalDragCancel;
};
gestures[VerticalDragGestureRecognizer] = new GestureRecognizerFactoryWithHandlers<VerticalDragGestureRecognizer>(
() => new VerticalDragGestureRecognizer(),
(VerticalDragGestureRecognizer instance) {
instance
..onDown = onVerticalDragDown
..onStart = onVerticalDragStart
..onUpdate = onVerticalDragUpdate
..onEnd = onVerticalDragEnd
..onCancel = onVerticalDragCancel;
},
);
}
if (onHorizontalDragDown != null ||
@ -274,14 +333,17 @@ class GestureDetector extends StatelessWidget {
onHorizontalDragUpdate != null ||
onHorizontalDragEnd != null ||
onHorizontalDragCancel != null) {
gestures[HorizontalDragGestureRecognizer] = (HorizontalDragGestureRecognizer recognizer) { // ignore: invalid_assignment, https://github.com/flutter/flutter/issues/5771
return (recognizer ??= new HorizontalDragGestureRecognizer())
..onDown = onHorizontalDragDown
..onStart = onHorizontalDragStart
..onUpdate = onHorizontalDragUpdate
..onEnd = onHorizontalDragEnd
..onCancel = onHorizontalDragCancel;
};
gestures[HorizontalDragGestureRecognizer] = new GestureRecognizerFactoryWithHandlers<HorizontalDragGestureRecognizer>(
() => new HorizontalDragGestureRecognizer(),
(HorizontalDragGestureRecognizer instance) {
instance
..onDown = onHorizontalDragDown
..onStart = onHorizontalDragStart
..onUpdate = onHorizontalDragUpdate
..onEnd = onHorizontalDragEnd
..onCancel = onHorizontalDragCancel;
},
);
}
if (onPanDown != null ||
@ -289,23 +351,29 @@ class GestureDetector extends StatelessWidget {
onPanUpdate != null ||
onPanEnd != null ||
onPanCancel != null) {
gestures[PanGestureRecognizer] = (PanGestureRecognizer recognizer) { // ignore: invalid_assignment, https://github.com/flutter/flutter/issues/5771
return (recognizer ??= new PanGestureRecognizer())
..onDown = onPanDown
..onStart = onPanStart
..onUpdate = onPanUpdate
..onEnd = onPanEnd
..onCancel = onPanCancel;
};
gestures[PanGestureRecognizer] = new GestureRecognizerFactoryWithHandlers<PanGestureRecognizer>(
() => new PanGestureRecognizer(),
(PanGestureRecognizer instance) {
instance
..onDown = onPanDown
..onStart = onPanStart
..onUpdate = onPanUpdate
..onEnd = onPanEnd
..onCancel = onPanCancel;
},
);
}
if (onScaleStart != null || onScaleUpdate != null || onScaleEnd != null) {
gestures[ScaleGestureRecognizer] = (ScaleGestureRecognizer recognizer) { // ignore: invalid_assignment, https://github.com/flutter/flutter/issues/5771
return (recognizer ??= new ScaleGestureRecognizer())
..onStart = onScaleStart
..onUpdate = onScaleUpdate
..onEnd = onScaleEnd;
};
gestures[ScaleGestureRecognizer] = new GestureRecognizerFactoryWithHandlers<ScaleGestureRecognizer>(
() => new ScaleGestureRecognizer(),
(ScaleGestureRecognizer instance) {
instance
..onStart = onScaleStart
..onUpdate = onScaleUpdate
..onEnd = onScaleEnd;
},
);
}
return new RawGestureDetector(
@ -321,13 +389,47 @@ class GestureDetector extends StatelessWidget {
/// factories.
///
/// For common gestures, use a [GestureRecognizer].
/// RawGestureDetector is useful primarily when developing your
/// [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.
/// * [PointerListener], 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.
/// that is used by assistive technology. This can be controlled using
/// [excludeFromSemantics].
const RawGestureDetector({
Key key,
this.child,
@ -342,6 +444,12 @@ class RawGestureDetector extends StatefulWidget {
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.
@ -375,7 +483,7 @@ class RawGestureDetectorState extends State<RawGestureDetector> {
}
/// This method can be called after the build phase, during the
/// layout of the nearest descendant RenderObjectWidget of the
/// layout of the nearest descendant [RenderObjectWidget] of the
/// gesture detector, to update the list of active gesture
/// recognizers.
///
@ -383,6 +491,10 @@ class RawGestureDetectorState extends State<RawGestureDetector> {
/// 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) {
@ -419,9 +531,12 @@ class RawGestureDetectorState extends State<RawGestureDetector> {
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] = gestures[type](oldRecognizers[type]);
assert(_recognizers[type].runtimeType == 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))

View file

@ -327,32 +327,38 @@ class ScrollableState extends State<Scrollable> with TickerProviderStateMixin
switch (widget.axis) {
case Axis.vertical:
_gestureRecognizers = <Type, GestureRecognizerFactory>{
VerticalDragGestureRecognizer: (VerticalDragGestureRecognizer recognizer) { // ignore: map_value_type_not_assignable, https://github.com/flutter/flutter/issues/7173
return (recognizer ??= new VerticalDragGestureRecognizer())
..onDown = _handleDragDown
..onStart = _handleDragStart
..onUpdate = _handleDragUpdate
..onEnd = _handleDragEnd
..onCancel = _handleDragCancel
..minFlingDistance = _physics?.minFlingDistance
..minFlingVelocity = _physics?.minFlingVelocity
..maxFlingVelocity = _physics?.maxFlingVelocity;
}
VerticalDragGestureRecognizer: new GestureRecognizerFactoryWithHandlers<VerticalDragGestureRecognizer>(
() => new VerticalDragGestureRecognizer(),
(VerticalDragGestureRecognizer instance) {
instance
..onDown = _handleDragDown
..onStart = _handleDragStart
..onUpdate = _handleDragUpdate
..onEnd = _handleDragEnd
..onCancel = _handleDragCancel
..minFlingDistance = _physics?.minFlingDistance
..minFlingVelocity = _physics?.minFlingVelocity
..maxFlingVelocity = _physics?.maxFlingVelocity;
},
),
};
break;
case Axis.horizontal:
_gestureRecognizers = <Type, GestureRecognizerFactory>{
HorizontalDragGestureRecognizer: (HorizontalDragGestureRecognizer recognizer) { // ignore: map_value_type_not_assignable, https://github.com/flutter/flutter/issues/7173
return (recognizer ??= new HorizontalDragGestureRecognizer())
..onDown = _handleDragDown
..onStart = _handleDragStart
..onUpdate = _handleDragUpdate
..onEnd = _handleDragEnd
..onCancel = _handleDragCancel
..minFlingDistance = _physics?.minFlingDistance
..minFlingVelocity = _physics?.minFlingVelocity
..maxFlingVelocity = _physics?.maxFlingVelocity;
}
HorizontalDragGestureRecognizer: new GestureRecognizerFactoryWithHandlers<HorizontalDragGestureRecognizer>(
() => new HorizontalDragGestureRecognizer(),
(HorizontalDragGestureRecognizer instance) {
instance
..onDown = _handleDragDown
..onStart = _handleDragStart
..onUpdate = _handleDragUpdate
..onEnd = _handleDragEnd
..onCancel = _handleDragCancel
..minFlingDistance = _physics?.minFlingDistance
..minFlingVelocity = _physics?.minFlingVelocity
..maxFlingVelocity = _physics?.maxFlingVelocity;
},
),
};
break;
}