mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
Dismissible RTL (#13137)
Fix the dismissible demo in the gallery (make it actuall update when you pick something from its menu; give it a better affordance for resetting once you've dismissed everything). Improve some docs. Fix various flinging bugs with dismissible. Add tests for those cases. Add a feature to flutter_test to support a drag-then-fling gesture (used by the flinging tests).
This commit is contained in:
parent
e6119282b7
commit
2db0c25f82
@ -60,20 +60,22 @@ class LeaveBehindDemoState extends State<LeaveBehindDemo> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void handleDemoAction(LeaveBehindDemoAction action) {
|
void handleDemoAction(LeaveBehindDemoAction action) {
|
||||||
switch (action) {
|
setState(() {
|
||||||
case LeaveBehindDemoAction.reset:
|
switch (action) {
|
||||||
initListItems();
|
case LeaveBehindDemoAction.reset:
|
||||||
break;
|
initListItems();
|
||||||
case LeaveBehindDemoAction.horizontalSwipe:
|
break;
|
||||||
_dismissDirection = DismissDirection.horizontal;
|
case LeaveBehindDemoAction.horizontalSwipe:
|
||||||
break;
|
_dismissDirection = DismissDirection.horizontal;
|
||||||
case LeaveBehindDemoAction.leftSwipe:
|
break;
|
||||||
_dismissDirection = DismissDirection.endToStart;
|
case LeaveBehindDemoAction.leftSwipe:
|
||||||
break;
|
_dismissDirection = DismissDirection.endToStart;
|
||||||
case LeaveBehindDemoAction.rightSwipe:
|
break;
|
||||||
_dismissDirection = DismissDirection.startToEnd;
|
case LeaveBehindDemoAction.rightSwipe:
|
||||||
break;
|
_dismissDirection = DismissDirection.startToEnd;
|
||||||
}
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void handleUndo(LeaveBehindItem item) {
|
void handleUndo(LeaveBehindItem item) {
|
||||||
@ -161,9 +163,16 @@ class LeaveBehindDemoState extends State<LeaveBehindDemo> {
|
|||||||
)
|
)
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
body: new ListView(
|
body: leaveBehindItems.isEmpty
|
||||||
children: leaveBehindItems.map(buildItem).toList()
|
? new Center(
|
||||||
)
|
child: new RaisedButton(
|
||||||
|
onPressed: () => handleDemoAction(LeaveBehindDemoAction.reset),
|
||||||
|
child: const Text('Reset the list'),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: new ListView(
|
||||||
|
children: leaveBehindItems.map(buildItem).toList()
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ import 'package:flutter/foundation.dart';
|
|||||||
|
|
||||||
import 'automatic_keep_alive.dart';
|
import 'automatic_keep_alive.dart';
|
||||||
import 'basic.dart';
|
import 'basic.dart';
|
||||||
|
import 'debug.dart';
|
||||||
import 'framework.dart';
|
import 'framework.dart';
|
||||||
import 'gesture_detector.dart';
|
import 'gesture_detector.dart';
|
||||||
import 'ticker_provider.dart';
|
import 'ticker_provider.dart';
|
||||||
@ -114,12 +115,21 @@ class Dismissible extends StatefulWidget {
|
|||||||
/// immediately after the the widget is dismissed.
|
/// immediately after the the widget is dismissed.
|
||||||
final Duration resizeDuration;
|
final Duration resizeDuration;
|
||||||
|
|
||||||
/// The offset threshold the item has to be dragged in order to be considered dismissed.
|
/// The offset threshold the item has to be dragged in order to be considered
|
||||||
|
/// dismissed.
|
||||||
///
|
///
|
||||||
/// Represented as a fraction, e.g. if it is 0.4, then the item has to be dragged at least
|
/// Represented as a fraction, e.g. if it is 0.4 (the default), then the item
|
||||||
/// 40% towards one direction to be considered dismissed. Clients can define different
|
/// has to be dragged at least 40% towards one direction to be considered
|
||||||
/// thresholds for each dismiss direction. This allows for use cases where item can be
|
/// dismissed. Clients can define different thresholds for each dismiss
|
||||||
/// dismissed to end but not to start.
|
/// direction.
|
||||||
|
///
|
||||||
|
/// Flinging is treated as being equivalent to dragging almost to 1.0, so
|
||||||
|
/// flinging can dismiss an item past any threshold less than 1.0.
|
||||||
|
///
|
||||||
|
/// See also [direction], which controls the directions in which the items can
|
||||||
|
/// be dismissed. Setting a threshold of 1.0 (or greater) prevents a drag in
|
||||||
|
/// the given [DismissDirection] even if it would be allowed by the
|
||||||
|
/// [direction] property.
|
||||||
final Map<DismissDirection, double> dismissThresholds;
|
final Map<DismissDirection, double> dismissThresholds;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -165,6 +175,8 @@ class _DismissibleClipper extends CustomClipper<Rect> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum _FlingGestureKind { none, forward, reverse }
|
||||||
|
|
||||||
class _DismissibleState extends State<Dismissible> with TickerProviderStateMixin, AutomaticKeepAliveClientMixin {
|
class _DismissibleState extends State<Dismissible> with TickerProviderStateMixin, AutomaticKeepAliveClientMixin {
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@ -200,15 +212,23 @@ class _DismissibleState extends State<Dismissible> with TickerProviderStateMixin
|
|||||||
|| widget.direction == DismissDirection.startToEnd;
|
|| widget.direction == DismissDirection.startToEnd;
|
||||||
}
|
}
|
||||||
|
|
||||||
DismissDirection get _dismissDirection {
|
DismissDirection _extentToDirection(double extent) {
|
||||||
if (_directionIsXAxis)
|
if (extent == 0.0)
|
||||||
return _dragExtent > 0 ? DismissDirection.startToEnd : DismissDirection.endToStart;
|
return null;
|
||||||
return _dragExtent > 0 ? DismissDirection.down : DismissDirection.up;
|
if (_directionIsXAxis) {
|
||||||
|
switch (Directionality.of(context)) {
|
||||||
|
case TextDirection.rtl:
|
||||||
|
return extent < 0 ? DismissDirection.startToEnd : DismissDirection.endToStart;
|
||||||
|
case TextDirection.ltr:
|
||||||
|
return extent > 0 ? DismissDirection.startToEnd : DismissDirection.endToStart;
|
||||||
|
}
|
||||||
|
assert(false);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return extent > 0 ? DismissDirection.down : DismissDirection.up;
|
||||||
}
|
}
|
||||||
|
|
||||||
double get _dismissThreshold {
|
DismissDirection get _dismissDirection => _extentToDirection(_dragExtent);
|
||||||
return widget.dismissThresholds[_dismissDirection] ?? _kDismissThreshold;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool get _isActive {
|
bool get _isActive {
|
||||||
return _dragUnderway || _moveController.isAnimating;
|
return _dragUnderway || _moveController.isAnimating;
|
||||||
@ -246,16 +266,40 @@ class _DismissibleState extends State<Dismissible> with TickerProviderStateMixin
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case DismissDirection.up:
|
case DismissDirection.up:
|
||||||
case DismissDirection.endToStart:
|
|
||||||
if (_dragExtent + delta < 0)
|
if (_dragExtent + delta < 0)
|
||||||
_dragExtent += delta;
|
_dragExtent += delta;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case DismissDirection.down:
|
case DismissDirection.down:
|
||||||
case DismissDirection.startToEnd:
|
|
||||||
if (_dragExtent + delta > 0)
|
if (_dragExtent + delta > 0)
|
||||||
_dragExtent += delta;
|
_dragExtent += delta;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case DismissDirection.endToStart:
|
||||||
|
switch (Directionality.of(context)) {
|
||||||
|
case TextDirection.rtl:
|
||||||
|
if (_dragExtent + delta > 0)
|
||||||
|
_dragExtent += delta;
|
||||||
|
break;
|
||||||
|
case TextDirection.ltr:
|
||||||
|
if (_dragExtent + delta < 0)
|
||||||
|
_dragExtent += delta;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DismissDirection.startToEnd:
|
||||||
|
switch (Directionality.of(context)) {
|
||||||
|
case TextDirection.rtl:
|
||||||
|
if (_dragExtent + delta < 0)
|
||||||
|
_dragExtent += delta;
|
||||||
|
break;
|
||||||
|
case TextDirection.ltr:
|
||||||
|
if (_dragExtent + delta > 0)
|
||||||
|
_dragExtent += delta;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
if (oldDragExtent.sign != _dragExtent.sign) {
|
if (oldDragExtent.sign != _dragExtent.sign) {
|
||||||
setState(() {
|
setState(() {
|
||||||
@ -275,35 +319,35 @@ class _DismissibleState extends State<Dismissible> with TickerProviderStateMixin
|
|||||||
).animate(_moveController);
|
).animate(_moveController);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool _isFlingGesture(Velocity velocity) {
|
_FlingGestureKind _describeFlingGesture(Velocity velocity) {
|
||||||
// Cannot fling an item if it cannot be dismissed by drag.
|
assert(widget.direction != null);
|
||||||
if (_dismissThreshold >= 1.0)
|
if (_dragExtent == 0.0) {
|
||||||
return false;
|
// If it was a fling, then it was a fling that was let loose at the exact
|
||||||
|
// middle of the range (i.e. when there's no displacement). In that case,
|
||||||
|
// we assume that the user meant to fling it back to the center, as
|
||||||
|
// opposed to having wanted to drag it out one way, then fling it past the
|
||||||
|
// center and into and out the other side.
|
||||||
|
return _FlingGestureKind.none;
|
||||||
|
}
|
||||||
final double vx = velocity.pixelsPerSecond.dx;
|
final double vx = velocity.pixelsPerSecond.dx;
|
||||||
final double vy = velocity.pixelsPerSecond.dy;
|
final double vy = velocity.pixelsPerSecond.dy;
|
||||||
|
DismissDirection flingDirection;
|
||||||
|
// Verify that the fling is in the generally right direction and fast enough.
|
||||||
if (_directionIsXAxis) {
|
if (_directionIsXAxis) {
|
||||||
if (vx.abs() - vy.abs() < _kMinFlingVelocityDelta)
|
if (vx.abs() - vy.abs() < _kMinFlingVelocityDelta || vx.abs() < _kMinFlingVelocity)
|
||||||
return false;
|
return _FlingGestureKind.none;
|
||||||
switch (widget.direction) {
|
assert(vx != 0.0);
|
||||||
case DismissDirection.horizontal:
|
flingDirection = _extentToDirection(vx);
|
||||||
return vx.abs() > _kMinFlingVelocity;
|
|
||||||
case DismissDirection.endToStart:
|
|
||||||
return -vx > _kMinFlingVelocity;
|
|
||||||
default:
|
|
||||||
return vx > _kMinFlingVelocity;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
if (vy.abs() - vx.abs() < _kMinFlingVelocityDelta)
|
if (vy.abs() - vx.abs() < _kMinFlingVelocityDelta || vy.abs() < _kMinFlingVelocity)
|
||||||
return false;
|
return _FlingGestureKind.none;
|
||||||
switch (widget.direction) {
|
assert(vy != 0.0);
|
||||||
case DismissDirection.vertical:
|
flingDirection = _extentToDirection(vy);
|
||||||
return vy.abs() > _kMinFlingVelocity;
|
|
||||||
case DismissDirection.up:
|
|
||||||
return -vy > _kMinFlingVelocity;
|
|
||||||
default:
|
|
||||||
return vy > _kMinFlingVelocity;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
assert(_dismissDirection != null);
|
||||||
|
if (flingDirection == _dismissDirection)
|
||||||
|
return _FlingGestureKind.forward;
|
||||||
|
return _FlingGestureKind.reverse;
|
||||||
}
|
}
|
||||||
|
|
||||||
void _handleDragEnd(DragEndDetails details) {
|
void _handleDragEnd(DragEndDetails details) {
|
||||||
@ -312,14 +356,35 @@ class _DismissibleState extends State<Dismissible> with TickerProviderStateMixin
|
|||||||
_dragUnderway = false;
|
_dragUnderway = false;
|
||||||
if (_moveController.isCompleted) {
|
if (_moveController.isCompleted) {
|
||||||
_startResizeAnimation();
|
_startResizeAnimation();
|
||||||
} else if (_isFlingGesture(details.velocity)) {
|
return;
|
||||||
final double flingVelocity = _directionIsXAxis ? details.velocity.pixelsPerSecond.dx : details.velocity.pixelsPerSecond.dy;
|
}
|
||||||
_dragExtent = flingVelocity.sign;
|
final double flingVelocity = _directionIsXAxis ? details.velocity.pixelsPerSecond.dx : details.velocity.pixelsPerSecond.dy;
|
||||||
_moveController.fling(velocity: flingVelocity.abs() * _kFlingVelocityScale);
|
switch (_describeFlingGesture(details.velocity)) {
|
||||||
} else if (_moveController.value > _dismissThreshold) {
|
case _FlingGestureKind.forward:
|
||||||
_moveController.forward();
|
assert(_dragExtent != 0.0);
|
||||||
} else {
|
assert(!_moveController.isDismissed);
|
||||||
_moveController.reverse();
|
if ((widget.dismissThresholds[_dismissDirection] ?? _kDismissThreshold) >= 1.0) {
|
||||||
|
_moveController.reverse();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
_dragExtent = flingVelocity.sign;
|
||||||
|
_moveController.fling(velocity: flingVelocity.abs() * _kFlingVelocityScale);
|
||||||
|
break;
|
||||||
|
case _FlingGestureKind.reverse:
|
||||||
|
assert(_dragExtent != 0.0);
|
||||||
|
assert(!_moveController.isDismissed);
|
||||||
|
_dragExtent = flingVelocity.sign;
|
||||||
|
_moveController.fling(velocity: -flingVelocity.abs() * _kFlingVelocityScale);
|
||||||
|
break;
|
||||||
|
case _FlingGestureKind.none:
|
||||||
|
if (!_moveController.isDismissed) { // we already know it's not completed, we check that above
|
||||||
|
if (_moveController.value > (widget.dismissThresholds[_dismissDirection] ?? _kDismissThreshold)) {
|
||||||
|
_moveController.forward();
|
||||||
|
} else {
|
||||||
|
_moveController.reverse();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -335,8 +400,11 @@ class _DismissibleState extends State<Dismissible> with TickerProviderStateMixin
|
|||||||
assert(_resizeController == null);
|
assert(_resizeController == null);
|
||||||
assert(_sizePriorToCollapse == null);
|
assert(_sizePriorToCollapse == null);
|
||||||
if (widget.resizeDuration == null) {
|
if (widget.resizeDuration == null) {
|
||||||
if (widget.onDismissed != null)
|
if (widget.onDismissed != null) {
|
||||||
widget.onDismissed(_dismissDirection);
|
final DismissDirection direction = _dismissDirection;
|
||||||
|
assert(direction != null);
|
||||||
|
widget.onDismissed(direction);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
_resizeController = new AnimationController(duration: widget.resizeDuration, vsync: this)
|
_resizeController = new AnimationController(duration: widget.resizeDuration, vsync: this)
|
||||||
..addListener(_handleResizeProgressChanged)
|
..addListener(_handleResizeProgressChanged)
|
||||||
@ -357,8 +425,11 @@ class _DismissibleState extends State<Dismissible> with TickerProviderStateMixin
|
|||||||
|
|
||||||
void _handleResizeProgressChanged() {
|
void _handleResizeProgressChanged() {
|
||||||
if (_resizeController.isCompleted) {
|
if (_resizeController.isCompleted) {
|
||||||
if (widget.onDismissed != null)
|
if (widget.onDismissed != null) {
|
||||||
widget.onDismissed(_dismissDirection);
|
final DismissDirection direction = _dismissDirection;
|
||||||
|
assert(direction != null);
|
||||||
|
widget.onDismissed(direction);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if (widget.onResize != null)
|
if (widget.onResize != null)
|
||||||
widget.onResize();
|
widget.onResize();
|
||||||
@ -368,6 +439,9 @@ class _DismissibleState extends State<Dismissible> with TickerProviderStateMixin
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
super.build(context); // See AutomaticKeepAliveClientMixin.
|
super.build(context); // See AutomaticKeepAliveClientMixin.
|
||||||
|
|
||||||
|
assert(!_directionIsXAxis || debugCheckHasDirectionality(context));
|
||||||
|
|
||||||
Widget background = widget.background;
|
Widget background = widget.background;
|
||||||
if (widget.secondaryBackground != null) {
|
if (widget.secondaryBackground != null) {
|
||||||
final DismissDirection direction = _dismissDirection;
|
final DismissDirection direction = _dismissDirection;
|
||||||
|
@ -2,9 +2,10 @@
|
|||||||
// Use of this source code is governed by a BSD-style license that can be
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/rendering.dart';
|
import 'package:flutter/rendering.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
|
||||||
const double itemExtent = 100.0;
|
const double itemExtent = 100.0;
|
||||||
Axis scrollDirection = Axis.vertical;
|
Axis scrollDirection = Axis.vertical;
|
||||||
@ -13,9 +14,9 @@ DismissDirection reportedDismissDirection;
|
|||||||
List<int> dismissedItems = <int>[];
|
List<int> dismissedItems = <int>[];
|
||||||
Widget background;
|
Widget background;
|
||||||
|
|
||||||
Widget buildTest({ double startToEndThreshold }) {
|
Widget buildTest({ double startToEndThreshold, TextDirection textDirection: TextDirection.ltr }) {
|
||||||
return new Directionality(
|
return new Directionality(
|
||||||
textDirection: TextDirection.ltr,
|
textDirection: textDirection,
|
||||||
child: new StatefulBuilder(
|
child: new StatefulBuilder(
|
||||||
builder: (BuildContext context, StateSetter setState) {
|
builder: (BuildContext context, StateSetter setState) {
|
||||||
Widget buildDismissibleItem(int item) {
|
Widget buildDismissibleItem(int item) {
|
||||||
@ -44,17 +45,14 @@ Widget buildTest({ double startToEndThreshold }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Directionality(
|
return new Container(
|
||||||
textDirection: TextDirection.ltr,
|
padding: const EdgeInsets.all(10.0),
|
||||||
child: new Container(
|
child: new ListView(
|
||||||
padding: const EdgeInsets.all(10.0),
|
scrollDirection: scrollDirection,
|
||||||
child: new ListView(
|
itemExtent: itemExtent,
|
||||||
scrollDirection: scrollDirection,
|
children: <int>[0, 1, 2, 3, 4]
|
||||||
itemExtent: itemExtent,
|
.where((int i) => !dismissedItems.contains(i))
|
||||||
children: <int>[0, 1, 2, 3, 4]
|
.map(buildDismissibleItem).toList(),
|
||||||
.where((int i) => !dismissedItems.contains(i))
|
|
||||||
.map(buildDismissibleItem).toList(),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@ -62,32 +60,30 @@ Widget buildTest({ double startToEndThreshold }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Null> dismissElement(WidgetTester tester, Finder finder, { DismissDirection gestureDirection }) async {
|
typedef Future<Null> DismissMethod(WidgetTester tester, Finder finder, { @required AxisDirection gestureDirection });
|
||||||
assert(tester.any(finder));
|
|
||||||
assert(gestureDirection != DismissDirection.horizontal);
|
|
||||||
assert(gestureDirection != DismissDirection.vertical);
|
|
||||||
|
|
||||||
|
Future<Null> dismissElement(WidgetTester tester, Finder finder, { @required AxisDirection gestureDirection }) async {
|
||||||
Offset downLocation;
|
Offset downLocation;
|
||||||
Offset upLocation;
|
Offset upLocation;
|
||||||
switch (gestureDirection) {
|
switch (gestureDirection) {
|
||||||
case DismissDirection.endToStart:
|
case AxisDirection.left:
|
||||||
// getTopRight() returns a point that's just beyond itemWidget's right
|
// getTopRight() returns a point that's just beyond itemWidget's right
|
||||||
// edge and outside the Dismissible event listener's bounds.
|
// edge and outside the Dismissible event listener's bounds.
|
||||||
downLocation = tester.getTopRight(finder) + const Offset(-0.1, 0.0);
|
downLocation = tester.getTopRight(finder) + const Offset(-0.1, 0.0);
|
||||||
upLocation = tester.getTopLeft(finder);
|
upLocation = tester.getTopLeft(finder);
|
||||||
break;
|
break;
|
||||||
case DismissDirection.startToEnd:
|
case AxisDirection.right:
|
||||||
// we do the same thing here to keep the test symmetric
|
// we do the same thing here to keep the test symmetric
|
||||||
downLocation = tester.getTopLeft(finder) + const Offset(0.1, 0.0);
|
downLocation = tester.getTopLeft(finder) + const Offset(0.1, 0.0);
|
||||||
upLocation = tester.getTopRight(finder);
|
upLocation = tester.getTopRight(finder);
|
||||||
break;
|
break;
|
||||||
case DismissDirection.up:
|
case AxisDirection.up:
|
||||||
// getBottomLeft() returns a point that's just below itemWidget's bottom
|
// getBottomLeft() returns a point that's just below itemWidget's bottom
|
||||||
// edge and outside the Dismissible event listener's bounds.
|
// edge and outside the Dismissible event listener's bounds.
|
||||||
downLocation = tester.getBottomLeft(finder) + const Offset(0.0, -0.1);
|
downLocation = tester.getBottomLeft(finder) + const Offset(0.0, -0.1);
|
||||||
upLocation = tester.getTopLeft(finder);
|
upLocation = tester.getTopLeft(finder);
|
||||||
break;
|
break;
|
||||||
case DismissDirection.down:
|
case AxisDirection.down:
|
||||||
// again with doing the same here for symmetry
|
// again with doing the same here for symmetry
|
||||||
downLocation = tester.getTopLeft(finder) + const Offset(0.1, 0.0);
|
downLocation = tester.getTopLeft(finder) + const Offset(0.1, 0.0);
|
||||||
upLocation = tester.getBottomLeft(finder);
|
upLocation = tester.getBottomLeft(finder);
|
||||||
@ -96,19 +92,49 @@ Future<Null> dismissElement(WidgetTester tester, Finder finder, { DismissDirecti
|
|||||||
fail('unsupported gestureDirection');
|
fail('unsupported gestureDirection');
|
||||||
}
|
}
|
||||||
|
|
||||||
final TestGesture gesture = await tester.startGesture(downLocation, pointer: 5);
|
final TestGesture gesture = await tester.startGesture(downLocation);
|
||||||
await gesture.moveTo(upLocation);
|
await gesture.moveTo(upLocation);
|
||||||
await gesture.up();
|
await gesture.up();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Null> dismissItem(WidgetTester tester, int item, { DismissDirection gestureDirection }) async {
|
Future<Null> flingElement(WidgetTester tester, Finder finder, { @required AxisDirection gestureDirection, double initialOffsetFactor: 0.0 }) async {
|
||||||
assert(gestureDirection != DismissDirection.horizontal);
|
Offset delta;
|
||||||
assert(gestureDirection != DismissDirection.vertical);
|
switch (gestureDirection) {
|
||||||
|
case AxisDirection.left:
|
||||||
|
delta = const Offset(-300.0, 0.0);
|
||||||
|
break;
|
||||||
|
case AxisDirection.right:
|
||||||
|
delta = const Offset(300.0, 0.0);
|
||||||
|
break;
|
||||||
|
case AxisDirection.up:
|
||||||
|
delta = const Offset(0.0, -300.0);
|
||||||
|
break;
|
||||||
|
case AxisDirection.down:
|
||||||
|
delta = const Offset(0.0, 300.0);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
fail('unsupported gestureDirection');
|
||||||
|
}
|
||||||
|
await tester.fling(finder, delta, 1000.0, initialOffset: delta * initialOffsetFactor);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Null> flingElementFromZero(WidgetTester tester, Finder finder, { @required AxisDirection gestureDirection }) async {
|
||||||
|
// This is a special case where we drag in one direction, then fling back so
|
||||||
|
// that at the point of release, we're at exactly the point at which we
|
||||||
|
// started, but with velocity. This is needed to check a boundary coundition
|
||||||
|
// in the flinging behavior.
|
||||||
|
await flingElement(tester, finder, gestureDirection: gestureDirection, initialOffsetFactor: -1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Null> dismissItem(WidgetTester tester, int item, {
|
||||||
|
@required AxisDirection gestureDirection,
|
||||||
|
DismissMethod mechanism: dismissElement,
|
||||||
|
}) async {
|
||||||
|
assert(gestureDirection != null);
|
||||||
final Finder itemFinder = find.text(item.toString());
|
final Finder itemFinder = find.text(item.toString());
|
||||||
expect(itemFinder, findsOneWidget);
|
expect(itemFinder, findsOneWidget);
|
||||||
|
|
||||||
await dismissElement(tester, itemFinder, gestureDirection: gestureDirection);
|
await mechanism(tester, itemFinder, gestureDirection: gestureDirection);
|
||||||
|
|
||||||
await tester.pump(); // start the slide
|
await tester.pump(); // start the slide
|
||||||
await tester.pump(const Duration(seconds: 1)); // finish the slide and start shrinking...
|
await tester.pump(const Duration(seconds: 1)); // finish the slide and start shrinking...
|
||||||
@ -147,12 +173,56 @@ void main() {
|
|||||||
await tester.pumpWidget(buildTest());
|
await tester.pumpWidget(buildTest());
|
||||||
expect(dismissedItems, isEmpty);
|
expect(dismissedItems, isEmpty);
|
||||||
|
|
||||||
await dismissItem(tester, 0, gestureDirection: DismissDirection.startToEnd);
|
await dismissItem(tester, 0, gestureDirection: AxisDirection.right);
|
||||||
expect(find.text('0'), findsNothing);
|
expect(find.text('0'), findsNothing);
|
||||||
expect(dismissedItems, equals(<int>[0]));
|
expect(dismissedItems, equals(<int>[0]));
|
||||||
expect(reportedDismissDirection, DismissDirection.startToEnd);
|
expect(reportedDismissDirection, DismissDirection.startToEnd);
|
||||||
|
|
||||||
await dismissItem(tester, 1, gestureDirection: DismissDirection.endToStart);
|
await dismissItem(tester, 1, gestureDirection: AxisDirection.left);
|
||||||
|
expect(find.text('1'), findsNothing);
|
||||||
|
expect(dismissedItems, equals(<int>[0, 1]));
|
||||||
|
expect(reportedDismissDirection, DismissDirection.endToStart);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('Horizontal fling triggers dismiss scrollDirection=vertical', (WidgetTester tester) async {
|
||||||
|
scrollDirection = Axis.vertical;
|
||||||
|
dismissDirection = DismissDirection.horizontal;
|
||||||
|
|
||||||
|
await tester.pumpWidget(buildTest());
|
||||||
|
expect(dismissedItems, isEmpty);
|
||||||
|
|
||||||
|
await dismissItem(tester, 0, gestureDirection: AxisDirection.right, mechanism: flingElement);
|
||||||
|
expect(find.text('0'), findsNothing);
|
||||||
|
expect(dismissedItems, equals(<int>[0]));
|
||||||
|
expect(reportedDismissDirection, DismissDirection.startToEnd);
|
||||||
|
|
||||||
|
await dismissItem(tester, 1, gestureDirection: AxisDirection.left, mechanism: flingElement);
|
||||||
|
expect(find.text('1'), findsNothing);
|
||||||
|
expect(dismissedItems, equals(<int>[0, 1]));
|
||||||
|
expect(reportedDismissDirection, DismissDirection.endToStart);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('Horizontal fling does not trigger at zero offset, but does otherwise', (WidgetTester tester) async {
|
||||||
|
scrollDirection = Axis.vertical;
|
||||||
|
dismissDirection = DismissDirection.horizontal;
|
||||||
|
|
||||||
|
await tester.pumpWidget(buildTest(startToEndThreshold: 0.95));
|
||||||
|
expect(dismissedItems, isEmpty);
|
||||||
|
|
||||||
|
await dismissItem(tester, 0, gestureDirection: AxisDirection.right, mechanism: flingElementFromZero);
|
||||||
|
expect(find.text('0'), findsOneWidget);
|
||||||
|
expect(dismissedItems, equals(<int>[]));
|
||||||
|
|
||||||
|
await dismissItem(tester, 0, gestureDirection: AxisDirection.left, mechanism: flingElementFromZero);
|
||||||
|
expect(find.text('0'), findsOneWidget);
|
||||||
|
expect(dismissedItems, equals(<int>[]));
|
||||||
|
|
||||||
|
await dismissItem(tester, 0, gestureDirection: AxisDirection.right, mechanism: flingElement);
|
||||||
|
expect(find.text('0'), findsNothing);
|
||||||
|
expect(dismissedItems, equals(<int>[0]));
|
||||||
|
expect(reportedDismissDirection, DismissDirection.startToEnd);
|
||||||
|
|
||||||
|
await dismissItem(tester, 1, gestureDirection: AxisDirection.left, mechanism: flingElement);
|
||||||
expect(find.text('1'), findsNothing);
|
expect(find.text('1'), findsNothing);
|
||||||
expect(dismissedItems, equals(<int>[0, 1]));
|
expect(dismissedItems, equals(<int>[0, 1]));
|
||||||
expect(reportedDismissDirection, DismissDirection.endToStart);
|
expect(reportedDismissDirection, DismissDirection.endToStart);
|
||||||
@ -165,51 +235,153 @@ void main() {
|
|||||||
await tester.pumpWidget(buildTest());
|
await tester.pumpWidget(buildTest());
|
||||||
expect(dismissedItems, isEmpty);
|
expect(dismissedItems, isEmpty);
|
||||||
|
|
||||||
await dismissItem(tester, 0, gestureDirection: DismissDirection.up);
|
await dismissItem(tester, 0, gestureDirection: AxisDirection.up);
|
||||||
expect(find.text('0'), findsNothing);
|
expect(find.text('0'), findsNothing);
|
||||||
expect(dismissedItems, equals(<int>[0]));
|
expect(dismissedItems, equals(<int>[0]));
|
||||||
expect(reportedDismissDirection, DismissDirection.up);
|
expect(reportedDismissDirection, DismissDirection.up);
|
||||||
|
|
||||||
await dismissItem(tester, 1, gestureDirection: DismissDirection.down);
|
await dismissItem(tester, 1, gestureDirection: AxisDirection.down);
|
||||||
expect(find.text('1'), findsNothing);
|
expect(find.text('1'), findsNothing);
|
||||||
expect(dismissedItems, equals(<int>[0, 1]));
|
expect(dismissedItems, equals(<int>[0, 1]));
|
||||||
expect(reportedDismissDirection, DismissDirection.down);
|
expect(reportedDismissDirection, DismissDirection.down);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('drag-left with DismissDirection.left triggers dismiss', (WidgetTester tester) async {
|
testWidgets('drag-left with DismissDirection.endToStart triggers dismiss (LTR)', (WidgetTester tester) async {
|
||||||
scrollDirection = Axis.vertical;
|
scrollDirection = Axis.vertical;
|
||||||
dismissDirection = DismissDirection.endToStart;
|
dismissDirection = DismissDirection.endToStart;
|
||||||
|
|
||||||
await tester.pumpWidget(buildTest());
|
await tester.pumpWidget(buildTest());
|
||||||
expect(dismissedItems, isEmpty);
|
expect(dismissedItems, isEmpty);
|
||||||
|
|
||||||
await dismissItem(tester, 0, gestureDirection: DismissDirection.startToEnd);
|
await dismissItem(tester, 0, gestureDirection: AxisDirection.right);
|
||||||
expect(find.text('0'), findsOneWidget);
|
expect(find.text('0'), findsOneWidget);
|
||||||
expect(dismissedItems, isEmpty);
|
expect(dismissedItems, isEmpty);
|
||||||
await dismissItem(tester, 1, gestureDirection: DismissDirection.startToEnd);
|
await dismissItem(tester, 1, gestureDirection: AxisDirection.right);
|
||||||
|
|
||||||
await dismissItem(tester, 0, gestureDirection: DismissDirection.endToStart);
|
await dismissItem(tester, 0, gestureDirection: AxisDirection.left);
|
||||||
expect(find.text('0'), findsNothing);
|
expect(find.text('0'), findsNothing);
|
||||||
expect(dismissedItems, equals(<int>[0]));
|
expect(dismissedItems, equals(<int>[0]));
|
||||||
await dismissItem(tester, 1, gestureDirection: DismissDirection.endToStart);
|
await dismissItem(tester, 1, gestureDirection: AxisDirection.left);
|
||||||
});
|
});
|
||||||
|
|
||||||
testWidgets('drag-right with DismissDirection.right triggers dismiss', (WidgetTester tester) async {
|
testWidgets('drag-right with DismissDirection.startToEnd triggers dismiss (LTR)', (WidgetTester tester) async {
|
||||||
scrollDirection = Axis.vertical;
|
scrollDirection = Axis.vertical;
|
||||||
dismissDirection = DismissDirection.startToEnd;
|
dismissDirection = DismissDirection.startToEnd;
|
||||||
|
|
||||||
await tester.pumpWidget(buildTest());
|
await tester.pumpWidget(buildTest());
|
||||||
expect(dismissedItems, isEmpty);
|
expect(dismissedItems, isEmpty);
|
||||||
|
|
||||||
await dismissItem(tester, 0, gestureDirection: DismissDirection.endToStart);
|
await dismissItem(tester, 0, gestureDirection: AxisDirection.left);
|
||||||
expect(find.text('0'), findsOneWidget);
|
expect(find.text('0'), findsOneWidget);
|
||||||
expect(dismissedItems, isEmpty);
|
expect(dismissedItems, isEmpty);
|
||||||
|
|
||||||
await dismissItem(tester, 0, gestureDirection: DismissDirection.startToEnd);
|
await dismissItem(tester, 0, gestureDirection: AxisDirection.right);
|
||||||
expect(find.text('0'), findsNothing);
|
expect(find.text('0'), findsNothing);
|
||||||
expect(dismissedItems, equals(<int>[0]));
|
expect(dismissedItems, equals(<int>[0]));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgets('drag-right with DismissDirection.endToStart triggers dismiss (RTL)', (WidgetTester tester) async {
|
||||||
|
scrollDirection = Axis.vertical;
|
||||||
|
dismissDirection = DismissDirection.endToStart;
|
||||||
|
|
||||||
|
await tester.pumpWidget(buildTest(textDirection: TextDirection.rtl));
|
||||||
|
expect(dismissedItems, isEmpty);
|
||||||
|
|
||||||
|
await dismissItem(tester, 0, gestureDirection: AxisDirection.left);
|
||||||
|
expect(find.text('0'), findsOneWidget);
|
||||||
|
expect(dismissedItems, isEmpty);
|
||||||
|
|
||||||
|
await dismissItem(tester, 0, gestureDirection: AxisDirection.right);
|
||||||
|
expect(find.text('0'), findsNothing);
|
||||||
|
expect(dismissedItems, equals(<int>[0]));
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('drag-left with DismissDirection.startToEnd triggers dismiss (RTL)', (WidgetTester tester) async {
|
||||||
|
scrollDirection = Axis.vertical;
|
||||||
|
dismissDirection = DismissDirection.startToEnd;
|
||||||
|
|
||||||
|
await tester.pumpWidget(buildTest(textDirection: TextDirection.rtl));
|
||||||
|
expect(dismissedItems, isEmpty);
|
||||||
|
|
||||||
|
await dismissItem(tester, 0, gestureDirection: AxisDirection.right);
|
||||||
|
expect(find.text('0'), findsOneWidget);
|
||||||
|
expect(dismissedItems, isEmpty);
|
||||||
|
await dismissItem(tester, 1, gestureDirection: AxisDirection.right);
|
||||||
|
|
||||||
|
await dismissItem(tester, 0, gestureDirection: AxisDirection.left);
|
||||||
|
expect(find.text('0'), findsNothing);
|
||||||
|
expect(dismissedItems, equals(<int>[0]));
|
||||||
|
await dismissItem(tester, 1, gestureDirection: AxisDirection.left);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('fling-left with DismissDirection.endToStart triggers dismiss (LTR)', (WidgetTester tester) async {
|
||||||
|
scrollDirection = Axis.vertical;
|
||||||
|
dismissDirection = DismissDirection.endToStart;
|
||||||
|
|
||||||
|
await tester.pumpWidget(buildTest());
|
||||||
|
expect(dismissedItems, isEmpty);
|
||||||
|
|
||||||
|
await dismissItem(tester, 0, gestureDirection: AxisDirection.right);
|
||||||
|
expect(find.text('0'), findsOneWidget);
|
||||||
|
expect(dismissedItems, isEmpty);
|
||||||
|
await dismissItem(tester, 1, gestureDirection: AxisDirection.right);
|
||||||
|
|
||||||
|
await dismissItem(tester, 0, gestureDirection: AxisDirection.left);
|
||||||
|
expect(find.text('0'), findsNothing);
|
||||||
|
expect(dismissedItems, equals(<int>[0]));
|
||||||
|
await dismissItem(tester, 1, gestureDirection: AxisDirection.left);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('fling-right with DismissDirection.startToEnd triggers dismiss (LTR)', (WidgetTester tester) async {
|
||||||
|
scrollDirection = Axis.vertical;
|
||||||
|
dismissDirection = DismissDirection.startToEnd;
|
||||||
|
|
||||||
|
await tester.pumpWidget(buildTest());
|
||||||
|
expect(dismissedItems, isEmpty);
|
||||||
|
|
||||||
|
await dismissItem(tester, 0, mechanism: flingElement, gestureDirection: AxisDirection.left);
|
||||||
|
expect(find.text('0'), findsOneWidget);
|
||||||
|
expect(dismissedItems, isEmpty);
|
||||||
|
|
||||||
|
await dismissItem(tester, 0, mechanism: flingElement, gestureDirection: AxisDirection.right);
|
||||||
|
expect(find.text('0'), findsNothing);
|
||||||
|
expect(dismissedItems, equals(<int>[0]));
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('fling-right with DismissDirection.endToStart triggers dismiss (RTL)', (WidgetTester tester) async {
|
||||||
|
scrollDirection = Axis.vertical;
|
||||||
|
dismissDirection = DismissDirection.endToStart;
|
||||||
|
|
||||||
|
await tester.pumpWidget(buildTest(textDirection: TextDirection.rtl));
|
||||||
|
expect(dismissedItems, isEmpty);
|
||||||
|
|
||||||
|
await dismissItem(tester, 0, mechanism: flingElement, gestureDirection: AxisDirection.left);
|
||||||
|
expect(find.text('0'), findsOneWidget);
|
||||||
|
expect(dismissedItems, isEmpty);
|
||||||
|
|
||||||
|
await dismissItem(tester, 0, mechanism: flingElement, gestureDirection: AxisDirection.right);
|
||||||
|
expect(find.text('0'), findsNothing);
|
||||||
|
expect(dismissedItems, equals(<int>[0]));
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('fling-left with DismissDirection.startToEnd triggers dismiss (RTL)', (WidgetTester tester) async {
|
||||||
|
scrollDirection = Axis.vertical;
|
||||||
|
dismissDirection = DismissDirection.startToEnd;
|
||||||
|
|
||||||
|
await tester.pumpWidget(buildTest(textDirection: TextDirection.rtl));
|
||||||
|
expect(dismissedItems, isEmpty);
|
||||||
|
|
||||||
|
await dismissItem(tester, 0, mechanism: flingElement, gestureDirection: AxisDirection.right);
|
||||||
|
expect(find.text('0'), findsOneWidget);
|
||||||
|
expect(dismissedItems, isEmpty);
|
||||||
|
await dismissItem(tester, 1, mechanism: flingElement, gestureDirection: AxisDirection.right);
|
||||||
|
|
||||||
|
await dismissItem(tester, 0, mechanism: flingElement, gestureDirection: AxisDirection.left);
|
||||||
|
expect(find.text('0'), findsNothing);
|
||||||
|
expect(dismissedItems, equals(<int>[0]));
|
||||||
|
await dismissItem(tester, 1, mechanism: flingElement, gestureDirection: AxisDirection.left);
|
||||||
|
});
|
||||||
|
|
||||||
testWidgets('drag-up with DismissDirection.up triggers dismiss', (WidgetTester tester) async {
|
testWidgets('drag-up with DismissDirection.up triggers dismiss', (WidgetTester tester) async {
|
||||||
scrollDirection = Axis.horizontal;
|
scrollDirection = Axis.horizontal;
|
||||||
dismissDirection = DismissDirection.up;
|
dismissDirection = DismissDirection.up;
|
||||||
@ -217,11 +389,11 @@ void main() {
|
|||||||
await tester.pumpWidget(buildTest());
|
await tester.pumpWidget(buildTest());
|
||||||
expect(dismissedItems, isEmpty);
|
expect(dismissedItems, isEmpty);
|
||||||
|
|
||||||
await dismissItem(tester, 0, gestureDirection: DismissDirection.down);
|
await dismissItem(tester, 0, gestureDirection: AxisDirection.down);
|
||||||
expect(find.text('0'), findsOneWidget);
|
expect(find.text('0'), findsOneWidget);
|
||||||
expect(dismissedItems, isEmpty);
|
expect(dismissedItems, isEmpty);
|
||||||
|
|
||||||
await dismissItem(tester, 0, gestureDirection: DismissDirection.up);
|
await dismissItem(tester, 0, gestureDirection: AxisDirection.up);
|
||||||
expect(find.text('0'), findsNothing);
|
expect(find.text('0'), findsNothing);
|
||||||
expect(dismissedItems, equals(<int>[0]));
|
expect(dismissedItems, equals(<int>[0]));
|
||||||
});
|
});
|
||||||
@ -233,11 +405,43 @@ void main() {
|
|||||||
await tester.pumpWidget(buildTest());
|
await tester.pumpWidget(buildTest());
|
||||||
expect(dismissedItems, isEmpty);
|
expect(dismissedItems, isEmpty);
|
||||||
|
|
||||||
await dismissItem(tester, 0, gestureDirection: DismissDirection.up);
|
await dismissItem(tester, 0, gestureDirection: AxisDirection.up);
|
||||||
expect(find.text('0'), findsOneWidget);
|
expect(find.text('0'), findsOneWidget);
|
||||||
expect(dismissedItems, isEmpty);
|
expect(dismissedItems, isEmpty);
|
||||||
|
|
||||||
await dismissItem(tester, 0, gestureDirection: DismissDirection.down);
|
await dismissItem(tester, 0, gestureDirection: AxisDirection.down);
|
||||||
|
expect(find.text('0'), findsNothing);
|
||||||
|
expect(dismissedItems, equals(<int>[0]));
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('fling-up with DismissDirection.up triggers dismiss', (WidgetTester tester) async {
|
||||||
|
scrollDirection = Axis.horizontal;
|
||||||
|
dismissDirection = DismissDirection.up;
|
||||||
|
|
||||||
|
await tester.pumpWidget(buildTest());
|
||||||
|
expect(dismissedItems, isEmpty);
|
||||||
|
|
||||||
|
await dismissItem(tester, 0, mechanism: flingElement, gestureDirection: AxisDirection.down);
|
||||||
|
expect(find.text('0'), findsOneWidget);
|
||||||
|
expect(dismissedItems, isEmpty);
|
||||||
|
|
||||||
|
await dismissItem(tester, 0, mechanism: flingElement, gestureDirection: AxisDirection.up);
|
||||||
|
expect(find.text('0'), findsNothing);
|
||||||
|
expect(dismissedItems, equals(<int>[0]));
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('fling-down with DismissDirection.down triggers dismiss', (WidgetTester tester) async {
|
||||||
|
scrollDirection = Axis.horizontal;
|
||||||
|
dismissDirection = DismissDirection.down;
|
||||||
|
|
||||||
|
await tester.pumpWidget(buildTest());
|
||||||
|
expect(dismissedItems, isEmpty);
|
||||||
|
|
||||||
|
await dismissItem(tester, 0, mechanism: flingElement, gestureDirection: AxisDirection.up);
|
||||||
|
expect(find.text('0'), findsOneWidget);
|
||||||
|
expect(dismissedItems, isEmpty);
|
||||||
|
|
||||||
|
await dismissItem(tester, 0, mechanism: flingElement, gestureDirection: AxisDirection.down);
|
||||||
expect(find.text('0'), findsNothing);
|
expect(find.text('0'), findsNothing);
|
||||||
expect(dismissedItems, equals(<int>[0]));
|
expect(dismissedItems, equals(<int>[0]));
|
||||||
});
|
});
|
||||||
@ -249,11 +453,27 @@ void main() {
|
|||||||
await tester.pumpWidget(buildTest(startToEndThreshold: 1.0));
|
await tester.pumpWidget(buildTest(startToEndThreshold: 1.0));
|
||||||
expect(dismissedItems, isEmpty);
|
expect(dismissedItems, isEmpty);
|
||||||
|
|
||||||
await dismissItem(tester, 0, gestureDirection: DismissDirection.startToEnd);
|
await dismissItem(tester, 0, gestureDirection: AxisDirection.right);
|
||||||
expect(find.text('0'), findsOneWidget);
|
expect(find.text('0'), findsOneWidget);
|
||||||
expect(dismissedItems, isEmpty);
|
expect(dismissedItems, isEmpty);
|
||||||
|
|
||||||
await dismissItem(tester, 0, gestureDirection: DismissDirection.endToStart);
|
await dismissItem(tester, 0, gestureDirection: AxisDirection.left);
|
||||||
|
expect(find.text('0'), findsNothing);
|
||||||
|
expect(dismissedItems, equals(<int>[0]));
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('fling-left has no effect on dismissible with a high dismiss threshold', (WidgetTester tester) async {
|
||||||
|
scrollDirection = Axis.vertical;
|
||||||
|
dismissDirection = DismissDirection.horizontal;
|
||||||
|
|
||||||
|
await tester.pumpWidget(buildTest(startToEndThreshold: 1.0));
|
||||||
|
expect(dismissedItems, isEmpty);
|
||||||
|
|
||||||
|
await dismissItem(tester, 0, mechanism: flingElement, gestureDirection: AxisDirection.right);
|
||||||
|
expect(find.text('0'), findsOneWidget);
|
||||||
|
expect(dismissedItems, isEmpty);
|
||||||
|
|
||||||
|
await dismissItem(tester, 0, mechanism: flingElement, gestureDirection: AxisDirection.left);
|
||||||
expect(find.text('0'), findsNothing);
|
expect(find.text('0'), findsNothing);
|
||||||
expect(dismissedItems, equals(<int>[0]));
|
expect(dismissedItems, equals(<int>[0]));
|
||||||
});
|
});
|
||||||
@ -309,12 +529,12 @@ void main() {
|
|||||||
);
|
);
|
||||||
expect(find.text('1'), findsOneWidget);
|
expect(find.text('1'), findsOneWidget);
|
||||||
expect(find.text('2'), findsOneWidget);
|
expect(find.text('2'), findsOneWidget);
|
||||||
await dismissElement(tester, find.text('2'), gestureDirection: DismissDirection.startToEnd);
|
await dismissElement(tester, find.text('2'), gestureDirection: AxisDirection.right);
|
||||||
await tester.pump(); // start the slide away
|
await tester.pump(); // start the slide away
|
||||||
await tester.pump(const Duration(seconds: 1)); // finish the slide away
|
await tester.pump(const Duration(seconds: 1)); // finish the slide away
|
||||||
expect(find.text('1'), findsOneWidget);
|
expect(find.text('1'), findsOneWidget);
|
||||||
expect(find.text('2'), findsNothing);
|
expect(find.text('2'), findsNothing);
|
||||||
await dismissElement(tester, find.text('1'), gestureDirection: DismissDirection.startToEnd);
|
await dismissElement(tester, find.text('1'), gestureDirection: AxisDirection.right);
|
||||||
await tester.pump(); // start the slide away
|
await tester.pump(); // start the slide away
|
||||||
await tester.pump(const Duration(seconds: 1)); // finish the slide away (at which point the child is no longer included in the tree)
|
await tester.pump(const Duration(seconds: 1)); // finish the slide away (at which point the child is no longer included in the tree)
|
||||||
expect(find.text('1'), findsNothing);
|
expect(find.text('1'), findsNothing);
|
||||||
@ -331,7 +551,7 @@ void main() {
|
|||||||
|
|
||||||
final Finder itemFinder = find.text('0');
|
final Finder itemFinder = find.text('0');
|
||||||
expect(itemFinder, findsOneWidget);
|
expect(itemFinder, findsOneWidget);
|
||||||
await dismissElement(tester, itemFinder, gestureDirection: DismissDirection.startToEnd);
|
await dismissElement(tester, itemFinder, gestureDirection: AxisDirection.right);
|
||||||
await tester.pump();
|
await tester.pump();
|
||||||
|
|
||||||
expect(find.text('background'), findsOneWidget); // The other four have been culled.
|
expect(find.text('background'), findsOneWidget); // The other four have been culled.
|
||||||
|
@ -299,11 +299,28 @@ class WidgetController {
|
|||||||
///
|
///
|
||||||
/// A fling is essentially a drag that ends at a particular speed. If you
|
/// A fling is essentially a drag that ends at a particular speed. If you
|
||||||
/// just want to drag and end without a fling, use [drag].
|
/// just want to drag and end without a fling, use [drag].
|
||||||
|
///
|
||||||
|
/// The `initialOffset` argument, if non-zero, causes the pointer to first
|
||||||
|
/// apply that offset, then pump a delay of `initialOffsetDelay`. This can be
|
||||||
|
/// used to simulate a drag followed by a fling, including dragging in the
|
||||||
|
/// opposite direction of the fling (e.g. dragging 200 pixels to the right,
|
||||||
|
/// then fling to the left over 200 pixels, ending at the exact point that the
|
||||||
|
/// drag started).
|
||||||
Future<Null> fling(Finder finder, Offset offset, double speed, {
|
Future<Null> fling(Finder finder, Offset offset, double speed, {
|
||||||
int pointer,
|
int pointer,
|
||||||
Duration frameInterval: const Duration(milliseconds: 16),
|
Duration frameInterval: const Duration(milliseconds: 16),
|
||||||
|
Offset initialOffset: Offset.zero,
|
||||||
|
Duration initialOffsetDelay: const Duration(seconds: 1),
|
||||||
}) {
|
}) {
|
||||||
return flingFrom(getCenter(finder), offset, speed, pointer: pointer, frameInterval: frameInterval);
|
return flingFrom(
|
||||||
|
getCenter(finder),
|
||||||
|
offset,
|
||||||
|
speed,
|
||||||
|
pointer: pointer,
|
||||||
|
frameInterval: frameInterval,
|
||||||
|
initialOffset: initialOffset,
|
||||||
|
initialOffsetDelay: initialOffsetDelay,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Attempts a fling gesture starting from the given location, moving the
|
/// Attempts a fling gesture starting from the given location, moving the
|
||||||
@ -324,7 +341,19 @@ class WidgetController {
|
|||||||
///
|
///
|
||||||
/// A fling is essentially a drag that ends at a particular speed. If you
|
/// A fling is essentially a drag that ends at a particular speed. If you
|
||||||
/// just want to drag and end without a fling, use [dragFrom].
|
/// just want to drag and end without a fling, use [dragFrom].
|
||||||
Future<Null> flingFrom(Offset startLocation, Offset offset, double speed, { int pointer, Duration frameInterval: const Duration(milliseconds: 16) }) {
|
///
|
||||||
|
/// The `initialOffset` argument, if non-zero, causes the pointer to first
|
||||||
|
/// apply that offset, then pump a delay of `initialOffsetDelay`. This can be
|
||||||
|
/// used to simulate a drag followed by a fling, including dragging in the
|
||||||
|
/// opposite direction of the fling (e.g. dragging 200 pixels to the right,
|
||||||
|
/// then fling to the left over 200 pixels, ending at the exact point that the
|
||||||
|
/// drag started).
|
||||||
|
Future<Null> flingFrom(Offset startLocation, Offset offset, double speed, {
|
||||||
|
int pointer,
|
||||||
|
Duration frameInterval: const Duration(milliseconds: 16),
|
||||||
|
Offset initialOffset: Offset.zero,
|
||||||
|
Duration initialOffsetDelay: const Duration(seconds: 1),
|
||||||
|
}) {
|
||||||
assert(offset.distance > 0.0);
|
assert(offset.distance > 0.0);
|
||||||
assert(speed > 0.0); // speed is pixels/second
|
assert(speed > 0.0); // speed is pixels/second
|
||||||
return TestAsyncUtils.guard(() async {
|
return TestAsyncUtils.guard(() async {
|
||||||
@ -335,8 +364,13 @@ class WidgetController {
|
|||||||
double timeStamp = 0.0;
|
double timeStamp = 0.0;
|
||||||
double lastTimeStamp = timeStamp;
|
double lastTimeStamp = timeStamp;
|
||||||
await sendEventToBinding(testPointer.down(startLocation, timeStamp: new Duration(milliseconds: timeStamp.round())), result);
|
await sendEventToBinding(testPointer.down(startLocation, timeStamp: new Duration(milliseconds: timeStamp.round())), result);
|
||||||
|
if (initialOffset.distance > 0.0) {
|
||||||
|
await sendEventToBinding(testPointer.move(startLocation + initialOffset, timeStamp: new Duration(milliseconds: timeStamp.round())), result);
|
||||||
|
timeStamp += initialOffsetDelay.inMilliseconds;
|
||||||
|
await pump(initialOffsetDelay);
|
||||||
|
}
|
||||||
for (int i = 0; i <= kMoveCount; i += 1) {
|
for (int i = 0; i <= kMoveCount; i += 1) {
|
||||||
final Offset location = startLocation + Offset.lerp(Offset.zero, offset, i / kMoveCount);
|
final Offset location = startLocation + initialOffset + Offset.lerp(Offset.zero, offset, i / kMoveCount);
|
||||||
await sendEventToBinding(testPointer.move(location, timeStamp: new Duration(milliseconds: timeStamp.round())), result);
|
await sendEventToBinding(testPointer.move(location, timeStamp: new Duration(milliseconds: timeStamp.round())), result);
|
||||||
timeStamp += timeStampDelta;
|
timeStamp += timeStampDelta;
|
||||||
if (timeStamp - lastTimeStamp > frameInterval.inMilliseconds) {
|
if (timeStamp - lastTimeStamp > frameInterval.inMilliseconds) {
|
||||||
|
Loading…
Reference in New Issue
Block a user