Merge pull request #2724 from abarth/drag_down2
Tapping drawer during animation causes it to stick
This commit is contained in:
commit
0eea48c8bd
@ -14,13 +14,17 @@ enum DragState {
|
||||
accepted
|
||||
}
|
||||
|
||||
typedef void GestureDragDownCallback(Point globalPosition);
|
||||
typedef void GestureDragStartCallback(Point globalPosition);
|
||||
typedef void GestureDragUpdateCallback(double delta);
|
||||
typedef void GestureDragEndCallback(Velocity velocity);
|
||||
typedef void GestureDragCancelCallback();
|
||||
|
||||
typedef void GesturePanDownCallback(Point globalPosition);
|
||||
typedef void GesturePanStartCallback(Point globalPosition);
|
||||
typedef void GesturePanUpdateCallback(Offset delta);
|
||||
typedef void GesturePanEndCallback(Velocity velocity);
|
||||
typedef void GesturePanCancelCallback();
|
||||
|
||||
typedef void _GesturePolymorphicUpdateCallback<T>(T delta);
|
||||
|
||||
@ -32,9 +36,11 @@ bool _isFlingGesture(Velocity velocity) {
|
||||
}
|
||||
|
||||
abstract class _DragGestureRecognizer<T extends dynamic> extends OneSequenceGestureRecognizer {
|
||||
GestureDragDownCallback onDown;
|
||||
GestureDragStartCallback onStart;
|
||||
_GesturePolymorphicUpdateCallback<T> onUpdate;
|
||||
GestureDragEndCallback onEnd;
|
||||
GestureDragCancelCallback onCancel;
|
||||
|
||||
DragState _state = DragState.ready;
|
||||
Point _initialPosition;
|
||||
@ -54,6 +60,8 @@ abstract class _DragGestureRecognizer<T extends dynamic> extends OneSequenceGest
|
||||
_state = DragState.possible;
|
||||
_initialPosition = event.position;
|
||||
_pendingDragDelta = _initialPendingDragDelta;
|
||||
if (onDown != null)
|
||||
onDown(_initialPosition);
|
||||
}
|
||||
}
|
||||
|
||||
@ -90,11 +98,18 @@ abstract class _DragGestureRecognizer<T extends dynamic> extends OneSequenceGest
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void rejectGesture(int pointer) {
|
||||
ensureNotTrackingPointer(pointer);
|
||||
}
|
||||
|
||||
@override
|
||||
void didStopTrackingLastPointer(int pointer) {
|
||||
if (_state == DragState.possible) {
|
||||
resolve(GestureDisposition.rejected);
|
||||
_state = DragState.ready;
|
||||
if (onCancel != null)
|
||||
onCancel();
|
||||
return;
|
||||
}
|
||||
bool wasAccepted = (_state == DragState.accepted);
|
||||
|
@ -99,6 +99,11 @@ abstract class OneSequenceGestureRecognizer extends GestureRecognizer {
|
||||
didStopTrackingLastPointer(pointer);
|
||||
}
|
||||
|
||||
void ensureNotTrackingPointer(int pointer) {
|
||||
if (_trackedPointers.contains(pointer))
|
||||
stopTrackingPointer(pointer);
|
||||
}
|
||||
|
||||
void stopTrackingIfPointerNoLongerDown(PointerEvent event) {
|
||||
if (event is PointerUpEvent || event is PointerCancelEvent)
|
||||
stopTrackingPointer(event.pointer);
|
||||
|
@ -122,11 +122,21 @@ class DrawerControllerState extends State<DrawerController> {
|
||||
|
||||
AnimationController _controller;
|
||||
|
||||
void _handleTapDown(Point position) {
|
||||
void _handleDragDown(Point position) {
|
||||
_controller.stop();
|
||||
_ensureHistoryEntry();
|
||||
}
|
||||
|
||||
void _handleDragCancel() {
|
||||
if (_controller.isDismissed || _controller.isAnimating)
|
||||
return;
|
||||
if (_controller.value < 0.5) {
|
||||
close();
|
||||
} else {
|
||||
open();
|
||||
}
|
||||
}
|
||||
|
||||
double get _width {
|
||||
assert(!Scheduler.debugInFrame); // we should never try to read the tree state while building or laying out
|
||||
RenderBox drawerBox = _drawerKey.currentContext?.findRenderObject();
|
||||
@ -179,8 +189,10 @@ class DrawerControllerState extends State<DrawerController> {
|
||||
} else {
|
||||
return new GestureDetector(
|
||||
key: _gestureDetectorKey,
|
||||
onHorizontalDragDown: _handleDragDown,
|
||||
onHorizontalDragUpdate: _move,
|
||||
onHorizontalDragEnd: _settle,
|
||||
onHorizontalDragCancel: _handleDragCancel,
|
||||
child: new RepaintBoundary(
|
||||
child: new Stack(
|
||||
children: <Widget>[
|
||||
@ -195,16 +207,13 @@ class DrawerControllerState extends State<DrawerController> {
|
||||
),
|
||||
new Align(
|
||||
alignment: const FractionalOffset(0.0, 0.5),
|
||||
child: new GestureDetector(
|
||||
onTapDown: _handleTapDown,
|
||||
child: new Align(
|
||||
alignment: const FractionalOffset(1.0, 0.5),
|
||||
widthFactor: _controller.value,
|
||||
child: new RepaintBoundary(
|
||||
child: new Focus(
|
||||
key: _drawerKey,
|
||||
child: config.child
|
||||
)
|
||||
child: new Align(
|
||||
alignment: const FractionalOffset(1.0, 0.5),
|
||||
widthFactor: _controller.value,
|
||||
child: new RepaintBoundary(
|
||||
child: new Focus(
|
||||
key: _drawerKey,
|
||||
child: config.child
|
||||
)
|
||||
)
|
||||
)
|
||||
|
@ -14,15 +14,16 @@ export 'package:flutter/gestures.dart' show
|
||||
GestureTapCallback,
|
||||
GestureTapCancelCallback,
|
||||
GestureLongPressCallback,
|
||||
GestureDragDownCallback,
|
||||
GestureDragStartCallback,
|
||||
GestureDragUpdateCallback,
|
||||
GestureDragEndCallback,
|
||||
GestureDragStartCallback,
|
||||
GestureDragUpdateCallback,
|
||||
GestureDragEndCallback,
|
||||
GestureDragCancelCallback,
|
||||
GesturePanDownCallback,
|
||||
GesturePanStartCallback,
|
||||
GesturePanUpdateCallback,
|
||||
GesturePanEndCallback,
|
||||
GesturePanCancelCallback,
|
||||
GestureScaleStartCallback,
|
||||
GestureScaleUpdateCallback,
|
||||
GestureScaleEndCallback,
|
||||
@ -49,15 +50,21 @@ class GestureDetector extends StatelessWidget {
|
||||
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,
|
||||
@ -116,6 +123,9 @@ class GestureDetector extends StatelessWidget {
|
||||
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
|
||||
@ -127,7 +137,14 @@ class GestureDetector extends StatelessWidget {
|
||||
/// specific velocity when it stopped contacting the screen.
|
||||
final GestureDragEndCallback onVerticalDragEnd;
|
||||
|
||||
/// The pointer that previously triggered the [onVerticalDragDown] did not
|
||||
/// end up moving vertically.
|
||||
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
|
||||
@ -139,9 +156,15 @@ class GestureDetector extends StatelessWidget {
|
||||
/// specific velocity when it stopped contacting the screen.
|
||||
final GestureDragEndCallback onHorizontalDragEnd;
|
||||
|
||||
/// The pointer that previously triggered the [onHorizontalDragDown] did not
|
||||
/// end up moving horizontally.
|
||||
final GestureDragCancelCallback onHorizontalDragCancel;
|
||||
|
||||
final GesturePanDownCallback onPanDown;
|
||||
final GesturePanStartCallback onPanStart;
|
||||
final GesturePanUpdateCallback onPanUpdate;
|
||||
final GesturePanEndCallback onPanEnd;
|
||||
final GesturePanCancelCallback onPanCancel;
|
||||
|
||||
final GestureScaleStartCallback onScaleStart;
|
||||
final GestureScaleUpdateCallback onScaleUpdate;
|
||||
@ -185,30 +208,48 @@ class GestureDetector extends StatelessWidget {
|
||||
};
|
||||
}
|
||||
|
||||
if (onVerticalDragStart != null || onVerticalDragUpdate != null || onVerticalDragEnd != null) {
|
||||
if (onVerticalDragDown != null ||
|
||||
onVerticalDragStart != null ||
|
||||
onVerticalDragUpdate != null ||
|
||||
onVerticalDragEnd != null ||
|
||||
onVerticalDragCancel != null) {
|
||||
gestures[VerticalDragGestureRecognizer] = (VerticalDragGestureRecognizer recognizer) {
|
||||
return (recognizer ??= new VerticalDragGestureRecognizer())
|
||||
..onDown = onVerticalDragDown
|
||||
..onStart = onVerticalDragStart
|
||||
..onUpdate = onVerticalDragUpdate
|
||||
..onEnd = onVerticalDragEnd;
|
||||
..onEnd = onVerticalDragEnd
|
||||
..onCancel = onVerticalDragCancel;
|
||||
};
|
||||
}
|
||||
|
||||
if (onHorizontalDragStart != null || onHorizontalDragUpdate != null || onHorizontalDragEnd != null) {
|
||||
if (onHorizontalDragDown != null ||
|
||||
onHorizontalDragStart != null ||
|
||||
onHorizontalDragUpdate != null ||
|
||||
onHorizontalDragEnd != null ||
|
||||
onHorizontalDragCancel != null) {
|
||||
gestures[HorizontalDragGestureRecognizer] = (HorizontalDragGestureRecognizer recognizer) {
|
||||
return (recognizer ??= new HorizontalDragGestureRecognizer())
|
||||
..onDown = onHorizontalDragDown
|
||||
..onStart = onHorizontalDragStart
|
||||
..onUpdate = onHorizontalDragUpdate
|
||||
..onEnd = onHorizontalDragEnd;
|
||||
..onEnd = onHorizontalDragEnd
|
||||
..onCancel = onHorizontalDragCancel;
|
||||
};
|
||||
}
|
||||
|
||||
if (onPanStart != null || onPanUpdate != null || onPanEnd != null) {
|
||||
if (onPanDown != null ||
|
||||
onPanStart != null ||
|
||||
onPanUpdate != null ||
|
||||
onPanEnd != null ||
|
||||
onPanCancel != null) {
|
||||
gestures[PanGestureRecognizer] = (PanGestureRecognizer recognizer) {
|
||||
return (recognizer ??= new PanGestureRecognizer())
|
||||
..onDown = onPanDown
|
||||
..onStart = onPanStart
|
||||
..onUpdate = onPanUpdate
|
||||
..onEnd = onPanEnd;
|
||||
..onEnd = onPanEnd
|
||||
..onCancel = onPanCancel;
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -470,7 +470,7 @@ abstract class ScrollableState<T extends Scrollable> extends State<T> {
|
||||
config.onScroll(_scrollOffset);
|
||||
}
|
||||
|
||||
void _handlePointerDown(_) {
|
||||
void _handleDragDown(_) {
|
||||
_controller.stop();
|
||||
}
|
||||
|
||||
@ -527,10 +527,7 @@ abstract class ScrollableState<T extends Scrollable> extends State<T> {
|
||||
key: _gestureDetectorKey,
|
||||
gestures: buildGestureDetectors(),
|
||||
behavior: HitTestBehavior.opaque,
|
||||
child: new Listener(
|
||||
child: buildContent(context),
|
||||
onPointerDown: _handlePointerDown
|
||||
)
|
||||
child: buildContent(context)
|
||||
);
|
||||
}
|
||||
|
||||
@ -559,6 +556,7 @@ abstract class ScrollableState<T extends Scrollable> extends State<T> {
|
||||
return <Type, GestureRecognizerFactory>{
|
||||
VerticalDragGestureRecognizer: (VerticalDragGestureRecognizer recognizer) {
|
||||
return (recognizer ??= new VerticalDragGestureRecognizer())
|
||||
..onDown = _handleDragDown
|
||||
..onStart = _handleDragStart
|
||||
..onUpdate = _handleDragUpdate
|
||||
..onEnd = _handleDragEnd;
|
||||
@ -568,6 +566,7 @@ abstract class ScrollableState<T extends Scrollable> extends State<T> {
|
||||
return <Type, GestureRecognizerFactory>{
|
||||
HorizontalDragGestureRecognizer: (HorizontalDragGestureRecognizer recognizer) {
|
||||
return (recognizer ??= new HorizontalDragGestureRecognizer())
|
||||
..onDown = _handleDragDown
|
||||
..onStart = _handleDragStart
|
||||
..onUpdate = _handleDragUpdate
|
||||
..onEnd = _handleDragEnd;
|
||||
|
@ -45,7 +45,6 @@ void main() {
|
||||
test('Drawer tap test', () {
|
||||
testWidgets((WidgetTester tester) {
|
||||
GlobalKey<ScaffoldState> scaffoldKey = new GlobalKey<ScaffoldState>();
|
||||
tester.pumpWidget(new Container()); // throw away the old App and its Navigator
|
||||
tester.pumpWidget(
|
||||
new MaterialApp(
|
||||
routes: <String, WidgetBuilder>{
|
||||
@ -81,4 +80,63 @@ void main() {
|
||||
});
|
||||
});
|
||||
|
||||
test('Drawer drag cancel resume', () {
|
||||
testWidgets((WidgetTester tester) {
|
||||
GlobalKey<ScaffoldState> scaffoldKey = new GlobalKey<ScaffoldState>();
|
||||
tester.pumpWidget(
|
||||
new MaterialApp(
|
||||
routes: <String, WidgetBuilder>{
|
||||
'/': (BuildContext context) {
|
||||
return new Scaffold(
|
||||
key: scaffoldKey,
|
||||
drawer: new Drawer(
|
||||
child: new Block(
|
||||
children: <Widget>[
|
||||
new Text('drawer'),
|
||||
new Container(
|
||||
height: 1000.0,
|
||||
decoration: new BoxDecoration(
|
||||
backgroundColor: Colors.blue[500]
|
||||
)
|
||||
),
|
||||
]
|
||||
)
|
||||
),
|
||||
body: new Container()
|
||||
);
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
expect(tester.findText('drawer'), isNull);
|
||||
scaffoldKey.currentState.openDrawer();
|
||||
tester.pump(); // drawer should be starting to animate in
|
||||
expect(tester.findText('drawer'), isNotNull);
|
||||
tester.pump(new Duration(seconds: 1)); // animation done
|
||||
expect(tester.findText('drawer'), isNotNull);
|
||||
|
||||
tester.tapAt(const Point(750.0, 100.0)); // on the mask
|
||||
tester.pump();
|
||||
tester.pump(new Duration(milliseconds: 10));
|
||||
// drawer should be starting to animate away
|
||||
RenderBox textBox = tester.findText('drawer').renderObject;
|
||||
double textLeft = textBox.localToGlobal(Point.origin).x;
|
||||
expect(textLeft, lessThan(0.0));
|
||||
|
||||
TestGesture gesture = tester.startGesture(new Point(100.0, 100.0));
|
||||
// drawer should be stopped.
|
||||
tester.pump();
|
||||
tester.pump(new Duration(milliseconds: 10));
|
||||
expect(textBox.localToGlobal(Point.origin).x, equals(textLeft));
|
||||
|
||||
gesture.moveBy(new Offset(0.0, -50.0));
|
||||
// drawer should be returning to visible
|
||||
tester.pump();
|
||||
tester.pump(new Duration(seconds: 1));
|
||||
expect(textBox.localToGlobal(Point.origin).x, equals(0.0));
|
||||
|
||||
gesture.up();
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user