Unverified Commit 47724f97 authored by jslavitz's avatar jslavitz Committed by GitHub

Breaks the moveBy call from drag and dragFrom into two separate calls and...

Breaks the moveBy call from drag and dragFrom into two separate calls and changes the default behavior of DragStartBehavior to DragStartBehavior.start (#26438)

* Breaking change which sets the default DragStartBehavior to DragStartBehavior.start and changes WidgetTester.drag and WidgetTester.dragFrom by breaking the moveBy function call into multiple pieces.
parent 62e55a45
......@@ -60,7 +60,7 @@ class CupertinoSwitch extends StatefulWidget {
@required this.value,
@required this.onChanged,
this.activeColor,
this.dragStartBehavior = DragStartBehavior.down,
this.dragStartBehavior = DragStartBehavior.start,
}) : assert(dragStartBehavior != null),
super(key: key);
......@@ -97,7 +97,6 @@ class CupertinoSwitch extends StatefulWidget {
/// [CupertinoTheme] in accordance to native iOS behavior.
final Color activeColor;
// TODO(jslavitz): Set the DragStartBehavior default to be start across all widgets.
/// {@template flutter.cupertino.switch.dragStartBehavior}
/// Determines the way that drag start behavior is handled.
///
......@@ -110,7 +109,7 @@ class CupertinoSwitch extends StatefulWidget {
/// animation smoother and setting it to [DragStartBehavior.down] will make
/// drag behavior feel slightly more reactive.
///
/// By default, the drag start behavior is [DragStartBehavior.down].
/// By default, the drag start behavior is [DragStartBehavior.start].
///
/// See also:
///
......@@ -149,7 +148,7 @@ class _CupertinoSwitchRenderObjectWidget extends LeafRenderObjectWidget {
this.activeColor,
this.onChanged,
this.vsync,
this.dragStartBehavior = DragStartBehavior.down,
this.dragStartBehavior = DragStartBehavior.start,
}) : super(key: key);
final bool value;
......@@ -202,7 +201,7 @@ class _RenderCupertinoSwitch extends RenderConstrainedBox {
ValueChanged<bool> onChanged,
@required TextDirection textDirection,
@required TickerProvider vsync,
DragStartBehavior dragStartBehavior = DragStartBehavior.down,
DragStartBehavior dragStartBehavior = DragStartBehavior.start,
}) : assert(value != null),
assert(activeColor != null),
assert(vsync != null),
......
......@@ -54,6 +54,10 @@ const Duration kZoomControlsTimeout = Duration(milliseconds: 3000);
/// the gesture is a scroll gesture, or, inversely, the maximum distance that a
/// touch can travel before the framework becomes confident that it is not a
/// tap.
///
/// A total delta less than or equal to [kTouchSlop] is not considered to be a
/// drag, whereas if the delta is greater than [kTouchSlop] it is considered to
/// be a drag.
// This value was empirically derived. We started at 8.0 and increased it to
// 18.0 after getting complaints that it was too difficult to hit targets.
const double kTouchSlop = 18.0; // Logical pixels
......
......@@ -54,7 +54,7 @@ abstract class DragGestureRecognizer extends OneSequenceGestureRecognizer {
/// [dragStartBehavior] must not be null.
DragGestureRecognizer({
Object debugOwner,
this.dragStartBehavior = DragStartBehavior.down,
this.dragStartBehavior = DragStartBehavior.start,
}) : assert(dragStartBehavior != null),
super(debugOwner: debugOwner);
......
......@@ -254,7 +254,7 @@ class DayPicker extends StatelessWidget {
@required this.lastDate,
@required this.displayedMonth,
this.selectableDayPredicate,
this.dragStartBehavior = DragStartBehavior.down,
this.dragStartBehavior = DragStartBehavior.start,
}) : assert(selectedDate != null),
assert(currentDate != null),
assert(onChanged != null),
......@@ -287,7 +287,6 @@ class DayPicker extends StatelessWidget {
/// Optional user supplied predicate function to customize selectable days.
final SelectableDayPredicate selectableDayPredicate;
// TODO(jslavitz): Set the DragStartBehavior default to be start across all widgets.
/// Determines the way that drag start behavior is handled.
///
/// If set to [DragStartBehavior.start], the drag gesture used to scroll a
......@@ -299,7 +298,7 @@ class DayPicker extends StatelessWidget {
/// animation smoother and setting it to [DragStartBehavior.down] will make
/// drag behavior feel slightly more reactive.
///
/// By default, the drag start behavior is [DragStartBehavior.down].
/// By default, the drag start behavior is [DragStartBehavior.start].
///
/// See also:
///
......@@ -528,7 +527,7 @@ class MonthPicker extends StatefulWidget {
@required this.firstDate,
@required this.lastDate,
this.selectableDayPredicate,
this.dragStartBehavior = DragStartBehavior.down,
this.dragStartBehavior = DragStartBehavior.start,
}) : assert(selectedDate != null),
assert(onChanged != null),
assert(!firstDate.isAfter(lastDate)),
......@@ -791,7 +790,7 @@ class YearPicker extends StatefulWidget {
@required this.onChanged,
@required this.firstDate,
@required this.lastDate,
this.dragStartBehavior = DragStartBehavior.down,
this.dragStartBehavior = DragStartBehavior.start,
}) : assert(selectedDate != null),
assert(onChanged != null),
assert(!firstDate.isAfter(lastDate)),
......
......@@ -180,7 +180,7 @@ class DrawerController extends StatefulWidget {
@required this.child,
@required this.alignment,
this.drawerCallback,
this.dragStartBehavior = DragStartBehavior.down,
this.dragStartBehavior = DragStartBehavior.start,
}) : assert(child != null),
assert(dragStartBehavior != null),
assert(alignment != null),
......@@ -200,7 +200,6 @@ class DrawerController extends StatefulWidget {
/// Optional callback that is called when a [Drawer] is opened or closed.
final DrawerCallback drawerCallback;
// TODO(jslavitz): Set the DragStartBehavior default to be start across all widgets.
/// {@template flutter.material.drawer.dragStartBehavior}
/// Determines the way that drag start behavior is handled.
///
......@@ -213,7 +212,7 @@ class DrawerController extends StatefulWidget {
/// animation smoother and setting it to [DragStartBehavior.down] will make
/// drag behavior feel slightly more reactive.
///
/// By default, the drag start behavior is [DragStartBehavior.down].
/// By default, the drag start behavior is [DragStartBehavior.start].
///
/// See also:
///
......
......@@ -75,7 +75,7 @@ class PaginatedDataTable extends StatefulWidget {
this.rowsPerPage = defaultRowsPerPage,
this.availableRowsPerPage = const <int>[defaultRowsPerPage, defaultRowsPerPage * 2, defaultRowsPerPage * 5, defaultRowsPerPage * 10],
this.onRowsPerPageChanged,
this.dragStartBehavior = DragStartBehavior.down,
this.dragStartBehavior = DragStartBehavior.start,
@required this.source
}) : assert(header != null),
assert(columns != null),
......
......@@ -876,8 +876,8 @@ class Scaffold extends StatefulWidget {
this.resizeToAvoidBottomPadding,
this.resizeToAvoidBottomInset,
this.primary = true,
this.drawerDragStartBehavior = DragStartBehavior.start,
this.extendBody = false,
this.drawerDragStartBehavior = DragStartBehavior.down,
}) : assert(primary != null),
assert(extendBody != null),
assert(drawerDragStartBehavior != null),
......
......@@ -73,7 +73,7 @@ class Switch extends StatefulWidget {
this.activeThumbImage,
this.inactiveThumbImage,
this.materialTapTargetSize,
this.dragStartBehavior = DragStartBehavior.down,
this.dragStartBehavior = DragStartBehavior.start,
}) : _switchType = _SwitchType.material,
assert(dragStartBehavior != null),
super(key: key);
......@@ -97,7 +97,7 @@ class Switch extends StatefulWidget {
this.activeThumbImage,
this.inactiveThumbImage,
this.materialTapTargetSize,
this.dragStartBehavior = DragStartBehavior.down,
this.dragStartBehavior = DragStartBehavior.start,
}) : _switchType = _SwitchType.adaptive,
super(key: key);
......
......@@ -562,7 +562,7 @@ class TabBar extends StatefulWidget implements PreferredSizeWidget {
this.labelPadding,
this.unselectedLabelColor,
this.unselectedLabelStyle,
this.dragStartBehavior = DragStartBehavior.down,
this.dragStartBehavior = DragStartBehavior.start,
this.onTap,
}) : assert(tabs != null),
assert(isScrollable != null),
......@@ -1057,7 +1057,7 @@ class TabBarView extends StatefulWidget {
@required this.children,
this.controller,
this.physics,
this.dragStartBehavior = DragStartBehavior.down,
this.dragStartBehavior = DragStartBehavior.start,
}) : assert(children != null),
assert(dragStartBehavior != null),
super(key: key);
......
......@@ -143,7 +143,7 @@ class TextField extends StatefulWidget {
this.cursorColor,
this.keyboardAppearance,
this.scrollPadding = const EdgeInsets.all(20.0),
this.dragStartBehavior = DragStartBehavior.down,
this.dragStartBehavior = DragStartBehavior.start,
this.enableInteractiveSelection,
this.onTap,
this.buildCounter,
......
......@@ -91,7 +91,7 @@ class Dismissible extends StatefulWidget {
this.dismissThresholds = const <DismissDirection, double>{},
this.movementDuration = const Duration(milliseconds: 200),
this.crossAxisEndOffset = 0.0,
this.dragStartBehavior = DragStartBehavior.down,
this.dragStartBehavior = DragStartBehavior.start,
}) : assert(key != null),
assert(secondaryBackground != null ? background != null : true),
assert(dragStartBehavior != null),
......@@ -162,7 +162,6 @@ class Dismissible extends StatefulWidget {
/// it is positive or negative.
final double crossAxisEndOffset;
// TODO(jslavitz): Set the DragStartBehavior default to be start across all widgets.
/// Determines the way that drag start behavior is handled.
///
/// If set to [DragStartBehavior.start], the drag gesture used to dismiss a
......@@ -173,7 +172,7 @@ class Dismissible extends StatefulWidget {
/// animation smoother and setting it to [DragStartBehavior.down] will make
/// drag behavior feel slightly more reactive.
///
/// By default, the drag start behavior is [DragStartBehavior.down].
/// By default, the drag start behavior is [DragStartBehavior.start].
///
/// See also:
///
......
......@@ -231,7 +231,7 @@ class EditableText extends StatefulWidget {
this.paintCursorAboveText = false,
this.scrollPadding = const EdgeInsets.all(20.0),
this.keyboardAppearance = Brightness.light,
this.dragStartBehavior = DragStartBehavior.down,
this.dragStartBehavior = DragStartBehavior.start,
this.enableInteractiveSelection,
}) : assert(controller != null),
assert(focusNode != null),
......
......@@ -186,7 +186,7 @@ class GestureDetector extends StatelessWidget {
this.onScaleEnd,
this.behavior,
this.excludeFromSemantics = false,
this.dragStartBehavior = DragStartBehavior.down,
this.dragStartBehavior = DragStartBehavior.start,
}) : assert(excludeFromSemantics != null),
assert(dragStartBehavior != null),
assert(() {
......@@ -373,7 +373,6 @@ class GestureDetector extends StatelessWidget {
/// duplication of information.
final bool excludeFromSemantics;
// TODO(jslavitz): Set the DragStartBehavior default to be start across all widgets.
/// Determines the way that drag start behavior is handled.
///
/// If set to [DragStartBehavior.start], gesture drag behavior will
......@@ -384,7 +383,7 @@ class GestureDetector extends StatelessWidget {
/// animation smoother and setting it to [DragStartBehavior.down] will make
/// drag behavior feel slightly more reactive.
///
/// By default, the drag start behavior is [DragStartBehavior.down].
/// By default, the drag start behavior is [DragStartBehavior.start].
///
/// Only the [onStart] callbacks for the [VerticalDragGestureRecognizer],
/// [HorizontalDragGestureRecognizer] and [PanGestureRecognizer] are affected
......
......@@ -189,7 +189,7 @@ class NestedScrollView extends StatefulWidget {
this.physics,
@required this.headerSliverBuilder,
@required this.body,
this.dragStartBehavior = DragStartBehavior.down,
this.dragStartBehavior = DragStartBehavior.start,
}) : assert(scrollDirection != null),
assert(reverse != null),
assert(headerSliverBuilder != null),
......@@ -371,7 +371,7 @@ class _NestedScrollViewCustomScrollView extends CustomScrollView {
@required ScrollController controller,
@required List<Widget> slivers,
@required this.handle,
DragStartBehavior dragStartBehavior = DragStartBehavior.down,
DragStartBehavior dragStartBehavior = DragStartBehavior.start,
}) : super(
scrollDirection: scrollDirection,
reverse: reverse,
......
......@@ -425,7 +425,7 @@ class PageView extends StatefulWidget {
this.pageSnapping = true,
this.onPageChanged,
List<Widget> children = const <Widget>[],
this.dragStartBehavior = DragStartBehavior.down,
this.dragStartBehavior = DragStartBehavior.start,
}) : controller = controller ?? _defaultPageController,
childrenDelegate = SliverChildListDelegate(children),
super(key: key);
......@@ -452,7 +452,7 @@ class PageView extends StatefulWidget {
this.onPageChanged,
@required IndexedWidgetBuilder itemBuilder,
int itemCount,
this.dragStartBehavior = DragStartBehavior.down,
this.dragStartBehavior = DragStartBehavior.start,
}) : controller = controller ?? _defaultPageController,
childrenDelegate = SliverChildBuilderDelegate(itemBuilder, childCount: itemCount),
super(key: key);
......@@ -468,7 +468,7 @@ class PageView extends StatefulWidget {
this.pageSnapping = true,
this.onPageChanged,
@required this.childrenDelegate,
this.dragStartBehavior = DragStartBehavior.down,
this.dragStartBehavior = DragStartBehavior.start,
}) : assert(childrenDelegate != null),
controller = controller ?? _defaultPageController,
super(key: key);
......
......@@ -312,7 +312,6 @@ class ScrollDragController implements Drag {
// May be null for proxied drags like via accessibility.
return offset;
}
if (offset == 0.0) {
if (motionStartDistanceThreshold != null &&
_offsetSinceLastStop == null &&
......
......@@ -69,7 +69,7 @@ abstract class ScrollView extends StatelessWidget {
this.anchor = 0.0,
this.cacheExtent,
this.semanticChildCount,
this.dragStartBehavior = DragStartBehavior.down,
this.dragStartBehavior = DragStartBehavior.start,
}) : assert(scrollDirection != null),
assert(reverse != null),
assert(shrinkWrap != null),
......@@ -454,7 +454,7 @@ class CustomScrollView extends ScrollView {
double cacheExtent,
this.slivers = const <Widget>[],
int semanticChildCount,
DragStartBehavior dragStartBehavior = DragStartBehavior.down,
DragStartBehavior dragStartBehavior = DragStartBehavior.start,
}) : super(
key: key,
scrollDirection: scrollDirection,
......@@ -500,7 +500,7 @@ abstract class BoxScrollView extends ScrollView {
this.padding,
double cacheExtent,
int semanticChildCount,
DragStartBehavior dragStartBehavior = DragStartBehavior.down,
DragStartBehavior dragStartBehavior = DragStartBehavior.start,
}) : super(
key: key,
scrollDirection: scrollDirection,
......@@ -802,7 +802,7 @@ class ListView extends BoxScrollView {
double cacheExtent,
List<Widget> children = const <Widget>[],
int semanticChildCount,
DragStartBehavior dragStartBehavior = DragStartBehavior.down,
DragStartBehavior dragStartBehavior = DragStartBehavior.start,
}) : childrenDelegate = SliverChildListDelegate(
children,
addAutomaticKeepAlives: addAutomaticKeepAlives,
......@@ -866,7 +866,7 @@ class ListView extends BoxScrollView {
bool addSemanticIndexes = true,
double cacheExtent,
int semanticChildCount,
DragStartBehavior dragStartBehavior = DragStartBehavior.down,
DragStartBehavior dragStartBehavior = DragStartBehavior.start,
}) : childrenDelegate = SliverChildBuilderDelegate(
itemBuilder,
childCount: itemCount,
......@@ -1320,7 +1320,7 @@ class GridView extends BoxScrollView {
@required this.childrenDelegate,
double cacheExtent,
int semanticChildCount,
DragStartBehavior dragStartBehavior = DragStartBehavior.down,
DragStartBehavior dragStartBehavior = DragStartBehavior.start,
}) : assert(gridDelegate != null),
assert(childrenDelegate != null),
super(
......@@ -1370,7 +1370,7 @@ class GridView extends BoxScrollView {
double cacheExtent,
List<Widget> children = const <Widget>[],
int semanticChildCount,
DragStartBehavior dragStartBehavior = DragStartBehavior.down,
DragStartBehavior dragStartBehavior = DragStartBehavior.start,
}) : gridDelegate = SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: crossAxisCount,
mainAxisSpacing: mainAxisSpacing,
......@@ -1429,7 +1429,7 @@ class GridView extends BoxScrollView {
bool addSemanticIndexes = true,
List<Widget> children = const <Widget>[],
int semanticChildCount,
DragStartBehavior dragStartBehavior = DragStartBehavior.down,
DragStartBehavior dragStartBehavior = DragStartBehavior.start,
}) : gridDelegate = SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: maxCrossAxisExtent,
mainAxisSpacing: mainAxisSpacing,
......
......@@ -81,7 +81,7 @@ class Scrollable extends StatefulWidget {
@required this.viewportBuilder,
this.excludeFromSemantics = false,
this.semanticChildCount,
this.dragStartBehavior = DragStartBehavior.down,
this.dragStartBehavior = DragStartBehavior.start,
}) : assert(axisDirection != null),
assert(dragStartBehavior != null),
assert(viewportBuilder != null),
......@@ -194,7 +194,7 @@ class Scrollable extends StatefulWidget {
/// animation smoother and setting it to [DragStartBehavior.down] will make
/// drag behavior feel slightly more reactive.
///
/// By default, the drag start behavior is [DragStartBehavior.down].
/// By default, the drag start behavior is [DragStartBehavior.start].
///
/// See also:
///
......
......@@ -204,7 +204,7 @@ class SingleChildScrollView extends StatelessWidget {
this.physics,
this.controller,
this.child,
this.dragStartBehavior = DragStartBehavior.down,
this.dragStartBehavior = DragStartBehavior.start,
}) : assert(scrollDirection != null),
assert(dragStartBehavior != null),
assert(!(controller != null && primary == true),
......
......@@ -230,7 +230,7 @@ class TextSelectionOverlay {
@required this.renderObject,
this.selectionControls,
this.selectionDelegate,
this.dragStartBehavior = DragStartBehavior.down,
this.dragStartBehavior = DragStartBehavior.start,
}) : assert(value != null),
assert(context != null),
_value = value {
......@@ -265,7 +265,6 @@ class TextSelectionOverlay {
/// text field.
final TextSelectionDelegate selectionDelegate;
// TODO(jslavitz): Set the DragStartBehavior default to be start across all widgets.
/// Determines the way that drag start behavior is handled.
///
/// If set to [DragStartBehavior.start], handle drag behavior will
......@@ -276,7 +275,7 @@ class TextSelectionOverlay {
/// animation smoother and setting it to [DragStartBehavior.down] will make
/// drag behavior feel slightly more reactive.
///
/// By default, the drag start behavior is [DragStartBehavior.down].
/// By default, the drag start behavior is [DragStartBehavior.start].
///
/// See also:
///
......@@ -468,7 +467,7 @@ class _TextSelectionHandleOverlay extends StatefulWidget {
@required this.onSelectionHandleChanged,
@required this.onSelectionHandleTapped,
@required this.selectionControls,
this.dragStartBehavior = DragStartBehavior.down,
this.dragStartBehavior = DragStartBehavior.start,
}) : super(key: key);
final TextSelection selection;
......
......@@ -8,7 +8,7 @@ import 'package:flutter/semantics.dart';
import 'package:flutter_test/flutter_test.dart';
// scrolling by this offset will move the picker to the next item
const Offset _kRowOffset = Offset(0.0, -32.0);
const Offset _kRowOffset = Offset(0.0, -50.0);
void main() {
group('Countdown timer picker', () {
......@@ -280,7 +280,7 @@ void main() {
),
);
await tester.drag(find.text('10'), const Offset(0.0, 32.0));
await tester.drag(find.text('10'), const Offset(0.0, 32.0), touchSlopY: 0);
await tester.pump();
await tester.pump(const Duration(milliseconds: 500));
......@@ -301,7 +301,7 @@ void main() {
),
);
await tester.drag(find.text('9'), const Offset(0.0, 32.0));
await tester.drag(find.text('9'), const Offset(0.0, 32.0), touchSlopY: 0);
await tester.pump();
await tester.pump(const Duration(milliseconds: 500));
......@@ -487,7 +487,8 @@ void main() {
),
);
await tester.drag(find.text('March'), const Offset(0.0, 32.0));
await tester.drag(find.text('March'), const Offset(0, 32.0), touchSlopY: 0.0);
// Momentarily, the 2018 and the incorrect 30 of February is aligned.
expect(
tester.getTopLeft(find.text('2018')).dy,
......@@ -525,15 +526,14 @@ void main() {
),
);
await tester.drag(find.text('27'), const Offset(0.0, -32.0));
await tester.drag(find.text('27'), const Offset(0.0, -32.0), touchSlopY: 0.0);
await tester.pump();
expect(
date,
DateTime(2018, 2, 28),
);
await tester.drag(find.text('28'), const Offset(0.0, -32.0));
await tester.drag(find.text('28'), const Offset(0.0, -32.0), touchSlopY: 0.0);
await tester.pump(); // Once to trigger the post frame animate call.
// Callback doesn't transiently go into invalid dates.
......@@ -706,6 +706,8 @@ void main() {
),
);
const Offset deltaOffset = Offset(0.0, -18.0);
// 11:59 -> 12:59
await tester.drag(find.text('11'), _kRowOffset);
await tester.pump();
......@@ -721,14 +723,14 @@ void main() {
expect(date, DateTime(2018, 1, 1, 11, 59));
// 11:59 -> 9:59
await tester.drag(find.text('11'), -_kRowOffset * 2);
await tester.drag(find.text('11'), -((_kRowOffset - deltaOffset) * 2 + deltaOffset));
await tester.pump();
await tester.pump(const Duration(milliseconds: 500));
expect(date, DateTime(2018, 1, 1, 9, 59));
// 9:59 -> 15:59
await tester.drag(find.text('9'), _kRowOffset * 6);
await tester.drag(find.text('9'), (_kRowOffset - deltaOffset) * 6 + deltaOffset);
await tester.pump();
await tester.pump(const Duration(milliseconds: 500));
......
......@@ -298,7 +298,7 @@ void main() {
);
// Drag it by a bit but not enough to move to the next item.
await tester.drag(find.text('10'), const Offset(0.0, 30.0));
await tester.drag(find.text('10'), const Offset(0.0, 30.0), touchSlopY: 0.0);
// The item that was in the center now moved a bit.
expect(
......@@ -315,7 +315,7 @@ void main() {
expect(selectedItems.isEmpty, true);
// Drag it by enough to move to the next item.
await tester.drag(find.text('10'), const Offset(0.0, 70.0));
await tester.drag(find.text('10'), const Offset(0.0, 70.0), touchSlopY: 0.0);
await tester.pumpAndSettle();
......
......@@ -117,7 +117,7 @@ void main() {
);
// Drag down but not enough to trigger the refresh.
await tester.drag(find.text('0'), const Offset(0.0, 50.0));
await tester.drag(find.text('0'), const Offset(0.0, 50.0), touchSlopY: 0);
await tester.pump();
// The function is referenced once while passing into CupertinoSliverRefreshControl
......@@ -191,7 +191,7 @@ void main() {
);
// Drag down but not enough to trigger the refresh.
await tester.drag(find.text('0'), const Offset(0.0, 50.0));
await tester.drag(find.text('0'), const Offset(0.0, 50.0), touchSlopY: 0);
await tester.pump();
await tester.pump(const Duration(milliseconds: 20));
await tester.pump(const Duration(milliseconds: 20));
......@@ -317,7 +317,7 @@ void main() {
),
);
await tester.drag(find.text('0'), const Offset(0.0, 150.0));
await tester.drag(find.text('0'), const Offset(0.0, 150.0), touchSlopY: 0);
await tester.pump();
// Let it start snapping back.
await tester.pump(const Duration(milliseconds: 50));
......@@ -394,7 +394,7 @@ void main() {
),
);
await tester.drag(find.text('0'), const Offset(0.0, 150.0));
await tester.drag(find.text('0'), const Offset(0.0, 150.0), touchSlopY: 0);
await tester.pump();
verify(mockHelper.builder(
......@@ -411,7 +411,7 @@ void main() {
Rect.fromLTRB(0.0, 0.0, 800.0, 150.0),
);
await tester.drag(find.text('0'), const Offset(0.0, -300.0));
await tester.drag(find.text('0'), const Offset(0.0, -300.0), touchSlopY: 0);
await tester.pump();
// Refresh indicator still being told to layout the same way.
......@@ -474,7 +474,7 @@ void main() {
),
);
await tester.drag(find.text('0'), const Offset(0.0, 150.0));
await tester.drag(find.text('0'), const Offset(0.0, 150.0), touchSlopY: 0);
await tester.pump();
verify(mockHelper.builder(
any,
......@@ -549,7 +549,7 @@ void main() {
),
);
await tester.drag(find.text('0'), const Offset(0.0, 150.0));
await tester.drag(find.text('0'), const Offset(0.0, 150.0), touchSlopY: 0);
await tester.pump();
verify(mockHelper.builder(
any,
......@@ -639,7 +639,7 @@ void main() {
),
);
await tester.drag(find.text('0'), const Offset(0.0, 150.0));
await tester.drag(find.text('0'), const Offset(0.0, 150.0), touchSlopY: 0.0);
await tester.pump();
verify(mockHelper.refreshTask());
......@@ -670,7 +670,7 @@ void main() {
// Start another drag by an amount that would have been enough to
// trigger another refresh if it were in the right state.
await tester.drag(find.text('0'), const Offset(0.0, 150.0));
await tester.drag(find.text('0'), const Offset(0.0, 150.0), touchSlopY: 0.0);
await tester.pump();
// Instead, it's still in the done state because the sliver never
......@@ -692,7 +692,7 @@ void main() {
);
// Start another drag. It's now in drag mode.
await tester.drag(find.text('0'), const Offset(0.0, 40.0));
await tester.drag(find.text('0'), const Offset(0.0, 40.0), touchSlopY: 0.0);
await tester.pump();
verify(mockHelper.builder(
any,
......@@ -908,7 +908,7 @@ void main() {
),
);
await tester.drag(find.text('0'), const Offset(0.0, 150.0));
await tester.drag(find.text('0'), const Offset(0.0, 150.0), touchSlopY: 0.0);
await tester.pump();
verify(mockHelper.builder(
any,
......@@ -1101,7 +1101,7 @@ void main() {
),
);
await tester.drag(find.text('0'), const Offset(0.0, 100.0));
await tester.drag(find.text('0'), const Offset(0.0, 100.0), touchSlopY: 0.0);
await tester.pump();
expect(
CupertinoSliverRefreshControl.state(tester.element(find.byType(LayoutBuilder))),
......
......@@ -255,7 +255,6 @@ void main() {
return Center(
child: CupertinoSwitch(
value: value,
dragStartBehavior: DragStartBehavior.down,
onChanged: (bool newValue) {
setState(() {
value = newValue;
......@@ -270,21 +269,21 @@ void main() {
expect(value, isFalse);
await tester.drag(find.byType(CupertinoSwitch), const Offset(-30.0, 0.0));
await tester.drag(find.byType(CupertinoSwitch), const Offset(-48.0, 0.0));
expect(value, isFalse);
await tester.drag(find.byType(CupertinoSwitch), const Offset(30.0, 0.0));
await tester.drag(find.byType(CupertinoSwitch), const Offset(48.0, 0.0));
expect(value, isTrue);
await tester.pump();
await tester.drag(find.byType(CupertinoSwitch), const Offset(30.0, 0.0));
await tester.drag(find.byType(CupertinoSwitch), const Offset(48.0, 0.0));
expect(value, isTrue);
await tester.pump();
await tester.drag(find.byType(CupertinoSwitch), const Offset(-30.0, 0.0));
await tester.drag(find.byType(CupertinoSwitch), const Offset(-48.0, 0.0));
expect(value, isFalse);
});
......
......@@ -58,9 +58,6 @@ void main() {
tester.route(pointer.move(const Offset(20.0, 30.0))); // moved 10 horizontally and 20 vertically which is 22 total
expect(didStartPan, isTrue); // 22 > 18
didStartPan = false;
// TODO(jslavitz): revert this testing change.
expect(updatedScrollDelta, const Offset(10.0, 20.0));
updatedScrollDelta = null;
expect(didEndPan, isFalse);
expect(didTap, isFalse);
......@@ -82,6 +79,58 @@ void main() {
tap.dispose();
});
testGesture('Should report most recent point to onStart by default', (GestureTester tester) {
final HorizontalDragGestureRecognizer drag = HorizontalDragGestureRecognizer();
final VerticalDragGestureRecognizer competingDrag = VerticalDragGestureRecognizer();
Offset positionAtOnStart;
drag.onStart = (DragStartDetails details) {
positionAtOnStart = details.globalPosition;
};
final TestPointer pointer = TestPointer(5);
final PointerDownEvent down = pointer.down(const Offset(10.0, 10.0));
drag.addPointer(down);
competingDrag.addPointer(down);
tester.closeArena(5);
tester.route(down);
tester.route(pointer.move(const Offset(30.0, 0.0)));
drag.dispose();
competingDrag.dispose();
expect(positionAtOnStart, const Offset(30.0, 00.0));
});
testGesture('Should report most recent point to onStart with a start configuration', (GestureTester tester) {
final HorizontalDragGestureRecognizer drag =
HorizontalDragGestureRecognizer();
final VerticalDragGestureRecognizer competingDrag = VerticalDragGestureRecognizer();
Offset positionAtOnStart;
drag.onStart = (DragStartDetails details) {
positionAtOnStart = details.globalPosition;
};
Offset updateOffset;
drag.onUpdate = (DragUpdateDetails details) {
updateOffset = details.globalPosition;
};
final TestPointer pointer = TestPointer(5);
final PointerDownEvent down = pointer.down(const Offset(10.0, 10.0));
drag.addPointer(down);
competingDrag.addPointer(down);
tester.closeArena(5);
tester.route(down);
tester.route(pointer.move(const Offset(30.0, 0.0)));
drag.dispose();
competingDrag.dispose();
expect(positionAtOnStart, const Offset(30.0, 0.0));
expect(updateOffset, null);
});
testGesture('Should recognize drag', (GestureTester tester) {
final HorizontalDragGestureRecognizer drag = HorizontalDragGestureRecognizer() ..dragStartBehavior = DragStartBehavior.down;
......
......@@ -102,7 +102,7 @@ void main() {
didStartPan = true;
},
onPanUpdate: (DragUpdateDetails details) {
panDelta = details.delta;
panDelta = panDelta == null ? details.delta : panDelta + details.delta;
},
onPanEnd: (DragEndDetails details) {
didEndPan = true;
......
......@@ -35,7 +35,7 @@ void main() {
await tester.pumpWidget(buildFrame(textDirection: TextDirection.ltr));
await tester.pump(const Duration(seconds: 1));
await tester.drag(find.text('1'), const Offset(-300.0, 0.0));
await tester.drag(find.text('1'), const Offset(-300.0, 0.0), touchSlopX: 0.0);
await tester.pump(const Duration(seconds: 1));
// screen is 800px wide, and has the following items:
// -10..280 = 1
......@@ -52,7 +52,7 @@ void main() {
// if item 3 was a bit wider, such that its center was past the 800px mark, this would fail,
// because it wouldn't be hit tested when scrolling from its center, as drag() does.
await tester.pump(const Duration(seconds: 1));
await tester.drag(find.text('3'), const Offset(-290.0, 0.0));
await tester.drag(find.text('3'), const Offset(-290.0, 0.0), touchSlopX: 0.0);
await tester.pump(const Duration(seconds: 1));
// screen is 800px wide, and has the following items:
// -10..280 = 2
......@@ -66,7 +66,7 @@ void main() {
expect(find.text('5'), findsNothing);
await tester.pump(const Duration(seconds: 1));
await tester.drag(find.text('3'), const Offset(0.0, -290.0));
await tester.drag(find.text('3'), const Offset(0.0, -290.0), touchSlopX: 0.0);
await tester.pump(const Duration(seconds: 1));
// unchanged
expect(find.text('0'), findsNothing);
......@@ -77,7 +77,7 @@ void main() {
expect(find.text('5'), findsNothing);
await tester.pump(const Duration(seconds: 1));
await tester.drag(find.text('3'), const Offset(-290.0, 0.0));
await tester.drag(find.text('3'), const Offset(-290.0, 0.0), touchSlopX: 0.0);
await tester.pump(const Duration(seconds: 1));
// screen is 800px wide, and has the following items:
// -10..280 = 3
......@@ -96,7 +96,7 @@ void main() {
// to move item 3 entirely off screen therefore takes:
// 60 + (290-60)*2 = 520 pixels
// plus a couple more to be sure
await tester.drag(find.text('3'), const Offset(-522.0, 0.0));
await tester.drag(find.text('3'), const Offset(-522.0, 0.0), touchSlopX: 0.0);
await tester.pump(); // just after release
// screen is 800px wide, and has the following items:
// -11..279 = 4
......@@ -121,7 +121,7 @@ void main() {
await tester.pumpWidget(Container());
await tester.pumpWidget(buildFrame(textDirection: TextDirection.ltr), const Duration(seconds: 1));
await tester.drag(find.text('2'), const Offset(-280.0, 0.0));
await tester.drag(find.text('2'), const Offset(-280.0, 0.0), touchSlopX: 0.0);
await tester.pump(const Duration(seconds: 1));
// screen is 800px wide, and has the following items:
// -280..10 = 0
......@@ -135,7 +135,7 @@ void main() {
expect(find.text('4'), findsNothing);
expect(find.text('5'), findsNothing);
await tester.pump(const Duration(seconds: 1));
await tester.drag(find.text('2'), const Offset(-290.0, 0.0));
await tester.drag(find.text('2'), const Offset(-290.0, 0.0), touchSlopX: 0.0);
await tester.pump(const Duration(seconds: 1));
// screen is 800px wide, and has the following items:
// -280..10 = 1
......@@ -165,7 +165,7 @@ void main() {
expect(find.text('4'), findsNothing);
expect(find.text('5'), findsNothing);
await tester.drag(find.text('0'), const Offset(300.0, 0.0));
await tester.drag(find.text('0'), const Offset(300.0, 0.0), touchSlopX: 0.0);
await tester.pump(const Duration(seconds: 1));
// screen is 800px wide, and has the following items:
// -80..210 = 3
......@@ -182,7 +182,7 @@ void main() {
// if item 3 was a bit wider, such that its center was past the 800px mark, this would fail,
// because it wouldn't be hit tested when scrolling from its center, as drag() does.
await tester.pump(const Duration(seconds: 1));
await tester.drag(find.text('2'), const Offset(290.0, 0.0));
await tester.drag(find.text('2'), const Offset(290.0, 0.0), touchSlopX: 0.0);
await tester.pump(const Duration(seconds: 1));
// screen is 800px wide, and has the following items:
// -10..280 = 4
......@@ -196,7 +196,7 @@ void main() {
expect(find.text('5'), findsNothing);
await tester.pump(const Duration(seconds: 1));
await tester.drag(find.text('2'), const Offset(0.0, 290.0));
await tester.drag(find.text('2'), const Offset(0.0, 290.0), touchSlopY: 0.0);
await tester.pump(const Duration(seconds: 1));
// unchanged
expect(find.text('0'), findsNothing);
......@@ -207,7 +207,7 @@ void main() {
expect(find.text('5'), findsNothing);
await tester.pump(const Duration(seconds: 1));
await tester.drag(find.text('3'), const Offset(290.0, 0.0));
await tester.drag(find.text('3'), const Offset(290.0, 0.0), touchSlopX: 0.0);
await tester.pump(const Duration(seconds: 1));
// screen is 800px wide, and has the following items:
// -10..280 = 5
......@@ -226,7 +226,7 @@ void main() {
// to move item 3 entirely off screen therefore takes:
// 60 + (290-60)*2 = 520 pixels
// plus a couple more to be sure
await tester.drag(find.text('4'), const Offset(522.0, 0.0));
await tester.drag(find.text('4'), const Offset(522.0, 0.0), touchSlopX: 0.0);
await tester.pump(); // just after release
// screen is 800px wide, and has the following items:
// 280..570 = 5
......@@ -265,7 +265,7 @@ void main() {
expect(find.text('4'), findsNothing);
expect(find.text('5'), findsNothing);
await tester.drag(find.text('0'), const Offset(300.0, 0.0));
await tester.drag(find.text('0'), const Offset(300.0, 0.0), touchSlopX: 0.0);
await tester.pump(const Duration(seconds: 1));
// screen is 800px wide, and has the following items:
// -80..210 = 3
......@@ -282,7 +282,7 @@ void main() {
// if item 3 was a bit wider, such that its center was past the 800px mark, this would fail,
// because it wouldn't be hit tested when scrolling from its center, as drag() does.
await tester.pump(const Duration(seconds: 1));
await tester.drag(find.text('2'), const Offset(290.0, 0.0));
await tester.drag(find.text('2'), const Offset(290.0, 0.0), touchSlopX: 0.0);
await tester.pump(const Duration(seconds: 1));
// screen is 800px wide, and has the following items:
// -10..280 = 4
......@@ -296,7 +296,7 @@ void main() {
expect(find.text('5'), findsNothing);
await tester.pump(const Duration(seconds: 1));
await tester.drag(find.text('2'), const Offset(0.0, 290.0));
await tester.drag(find.text('2'), const Offset(0.0, 290.0), touchSlopY: 0.0);
await tester.pump(const Duration(seconds: 1));
// unchanged
expect(find.text('0'), findsNothing);
......@@ -307,7 +307,7 @@ void main() {
expect(find.text('5'), findsNothing);
await tester.pump(const Duration(seconds: 1));
await tester.drag(find.text('3'), const Offset(290.0, 0.0));
await tester.drag(find.text('3'), const Offset(290.0, 0.0), touchSlopX: 0.0);
await tester.pump(const Duration(seconds: 1));
// screen is 800px wide, and has the following items:
// -10..280 = 5
......@@ -326,7 +326,7 @@ void main() {
// to move item 3 entirely off screen therefore takes:
// 60 + (290-60)*2 = 520 pixels
// plus a couple more to be sure
await tester.drag(find.text('4'), const Offset(522.0, 0.0));
await tester.drag(find.text('4'), const Offset(522.0, 0.0), touchSlopX: 0.0);
await tester.pump(); // just after release
// screen is 800px wide, and has the following items:
// 280..570 = 5
......@@ -354,7 +354,7 @@ void main() {
await tester.pumpWidget(buildFrame(reverse: true, textDirection: TextDirection.rtl));
await tester.pump(const Duration(seconds: 1));
await tester.drag(find.text('1'), const Offset(-300.0, 0.0));
await tester.drag(find.text('1'), const Offset(-300.0, 0.0), touchSlopX: 0.0);
await tester.pump(const Duration(seconds: 1));
// screen is 800px wide, and has the following items:
// -10..280 = 1
......@@ -371,7 +371,7 @@ void main() {
// if item 3 was a bit wider, such that its center was past the 800px mark, this would fail,
// because it wouldn't be hit tested when scrolling from its center, as drag() does.
await tester.pump(const Duration(seconds: 1));
await tester.drag(find.text('3'), const Offset(-290.0, 0.0));
await tester.drag(find.text('3'), const Offset(-290.0, 0.0), touchSlopX: 0.0);
await tester.pump(const Duration(seconds: 1));
// screen is 800px wide, and has the following items:
// -10..280 = 2
......@@ -385,7 +385,7 @@ void main() {
expect(find.text('5'), findsNothing);
await tester.pump(const Duration(seconds: 1));
await tester.drag(find.text('3'), const Offset(0.0, -290.0));
await tester.drag(find.text('3'), const Offset(0.0, -290.0), touchSlopX: 0.0);
await tester.pump(const Duration(seconds: 1));
// unchanged
expect(find.text('0'), findsNothing);
......@@ -396,7 +396,7 @@ void main() {
expect(find.text('5'), findsNothing);
await tester.pump(const Duration(seconds: 1));
await tester.drag(find.text('3'), const Offset(-290.0, 0.0));
await tester.drag(find.text('3'), const Offset(-290.0, 0.0), touchSlopX: 0.0);
await tester.pump(const Duration(seconds: 1));
// screen is 800px wide, and has the following items:
// -10..280 = 3
......@@ -415,7 +415,7 @@ void main() {
// to move item 3 entirely off screen therefore takes:
// 60 + (290-60)*2 = 520 pixels
// plus a couple more to be sure
await tester.drag(find.text('3'), const Offset(-522.0, 0.0));
await tester.drag(find.text('3'), const Offset(-522.0, 0.0), touchSlopX: 0.0);
await tester.pump(); // just after release
// screen is 800px wide, and has the following items:
// -11..279 = 4
......@@ -440,7 +440,7 @@ void main() {
await tester.pumpWidget(Container());
await tester.pumpWidget(buildFrame(reverse: true, textDirection: TextDirection.rtl), const Duration(seconds: 1));
await tester.drag(find.text('2'), const Offset(-280.0, 0.0));
await tester.drag(find.text('2'), const Offset(-280.0, 0.0), touchSlopX: 0.0);
await tester.pump(const Duration(seconds: 1));
// screen is 800px wide, and has the following items:
// -280..10 = 0
......@@ -454,7 +454,7 @@ void main() {
expect(find.text('4'), findsNothing);
expect(find.text('5'), findsNothing);
await tester.pump(const Duration(seconds: 1));
await tester.drag(find.text('2'), const Offset(-290.0, 0.0));
await tester.drag(find.text('2'), const Offset(-290.0, 0.0), touchSlopX: 0.0);
await tester.pump(const Duration(seconds: 1));
// screen is 800px wide, and has the following items:
// -280..10 = 1
......
......@@ -301,7 +301,7 @@ void main() {
await tester.drag(find.byType(ListView), const Offset(0.0, -250.0));
expect(log, equals(<double>[ 250.0 ]));
expect(log, equals(<double>[ 20.0, 250.0 ]));
log.clear();
controller.dispose();
......
......@@ -65,7 +65,7 @@ void main() {
testWidgets('Holding scroll', (WidgetTester tester) async {
await pumpTest(tester, TargetPlatform.iOS);
await tester.drag(find.byType(Viewport), const Offset(0.0, 200.0));
await tester.drag(find.byType(Viewport), const Offset(0.0, 200.0), touchSlopY: 0.0);
expect(getScrollOffset(tester), -200.0);
await tester.pump(); // trigger ballistic
await tester.pump(const Duration(milliseconds: 10));
......
......@@ -13,6 +13,12 @@ import 'finders.dart';
import 'test_async_utils.dart';
import 'test_pointer.dart';
/// The default drag touch slop used to break up a large drag into multiple
/// smaller moves.
///
/// This value must be greater than [kTouchSlop].
const double kDragSlopDefault = 20.0;
/// Class that programmatically interacts with widgets.
///
/// For a variant of this class suited specifically for unit tests, see
......@@ -410,8 +416,27 @@ abstract class WidgetController {
///
/// If you want the drag to end with a speed so that the gesture recognition
/// system identifies the gesture as a fling, consider using [fling] instead.
Future<void> drag(Finder finder, Offset offset, {int pointer}) {
return dragFrom(getCenter(finder), offset, pointer: pointer);
///
/// {@template flutter.flutter_test.drag}
/// By default, if the x or y component of offset is greater than [kTouchSlop], the
/// gesture is broken up into two separate moves calls. Changing 'touchSlopX' or
/// `touchSlopY` will change the minimum amount of movement in the respective axis
/// before the drag will be broken into multiple calls. To always send the
/// drag with just a single call to [TestGesture.moveBy], `touchSlopX` and `touchSlopY`
/// should be set to 0.
///
/// Breaking the drag into multiple moves is necessary for accurate execution
/// of drag update calls with a [DragStartBehavior] variable set to
/// [DragStartBehavior.start]. Without such a change, the dragUpdate callback
/// from a drag recognizer will never be invoked.
///
/// To force this function to a send a single move event, the 'touchSlopX' and
/// 'touchSlopY' variables should be set to 0. However, generally, these values
/// should be left to their default values.
/// {@end template}
Future<void> drag(Finder finder, Offset offset, { int pointer, double touchSlopX = kDragSlopDefault, double touchSlopY = kDragSlopDefault }) {
assert(kDragSlopDefault > kTouchSlop);
return dragFrom(getCenter(finder), offset, pointer: pointer, touchSlopX: touchSlopX, touchSlopY: touchSlopY);
}
/// Attempts a drag gesture consisting of a pointer down, a move by
......@@ -420,11 +445,78 @@ abstract class WidgetController {
/// If you want the drag to end with a speed so that the gesture recognition
/// system identifies the gesture as a fling, consider using [flingFrom]
/// instead.
Future<void> dragFrom(Offset startLocation, Offset offset, {int pointer}) {
///
/// {@macro flutter.flutter_test.drag}
Future<void> dragFrom(Offset startLocation, Offset offset, { int pointer, double touchSlopX = kDragSlopDefault, double touchSlopY = kDragSlopDefault }) {
assert(kDragSlopDefault > kTouchSlop);
return TestAsyncUtils.guard<void>(() async {
final TestGesture gesture = await startGesture(startLocation, pointer: pointer);
assert(gesture != null);
await gesture.moveBy(offset);
final double xSign = offset.dx.sign;
final double ySign = offset.dy.sign;
final double offsetX = offset.dx;
final double offsetY = offset.dy;
final bool separateX = offset.dx.abs() > touchSlopX && touchSlopX > 0;
final bool separateY = offset.dy.abs() > touchSlopY && touchSlopY > 0;
if (separateY || separateX) {
final double offsetSlope = offsetY / offsetX;
final double inverseOffsetSlope = offsetX / offsetY;
final double slopSlope = touchSlopY / touchSlopX;
final double absoluteOffsetSlope = offsetSlope.abs();
final double signedSlopX = touchSlopX * xSign;
final double signedSlopY = touchSlopY * ySign;
if (absoluteOffsetSlope != slopSlope) {
// The drag goes through one or both of the extents of the edges of the box.
if (absoluteOffsetSlope < slopSlope) {
assert(offsetX.abs() > touchSlopX);
// The drag goes through the vertical edge of the box.
// It is guaranteed that the |offsetX| > touchSlopX.
final double diffY = offsetSlope.abs() * touchSlopX * ySign;
// The vector from the origin to the vertical edge.
await gesture.moveBy(Offset(signedSlopX, diffY));
if (offsetY.abs() <= touchSlopY) {
// The drag ends on or before getting to the horizontal extension of the horizontal edge.
await gesture.moveBy(Offset(offsetX - signedSlopX, offsetY - diffY));
} else {
final double diffY2 = signedSlopY - diffY;
final double diffX2 = inverseOffsetSlope * diffY2;
// The vector from the edge of the box to the horizontal extension of the horizontal edge.
await gesture.moveBy(Offset(diffX2, diffY2));
await gesture.moveBy(Offset(offsetX - diffX2 - signedSlopX, offsetY - signedSlopY));
}
} else {
assert(offsetY.abs() > touchSlopY);
// The drag goes through the horizontal edge of the box.
// It is guaranteed that the |offsetY| > touchSlopY.
final double diffX = inverseOffsetSlope.abs() * touchSlopY * xSign;
// The vector from the origin to the vertical edge.
await gesture.moveBy(Offset(diffX, signedSlopY));
if (offsetX.abs() <= touchSlopX) {
// The drag ends on or before getting to the vertical extension of the vertical edge.
await gesture.moveBy(Offset(offsetX - diffX, offsetY - signedSlopY));
} else {
final double diffX2 = signedSlopX - diffX;
final double diffY2 = offsetSlope * diffX2;
// The vector from the edge of the box to the vertical extension of the vertical edge.
await gesture.moveBy(Offset(diffX2, diffY2));
await gesture.moveBy(Offset(offsetX - signedSlopX, offsetY - diffY2 - signedSlopY));
}
}
} else { // The drag goes through the corner of the box.
await gesture.moveBy(Offset(signedSlopX, signedSlopY));
await gesture.moveBy(Offset(offsetX - signedSlopX, offsetY - signedSlopY));
}
} else { // The drag ends inside the box.
await gesture.moveBy(offset);
}
await gesture.up();
});
}
......
// Copyright 2016 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 'dart:ui';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:flutter/gestures.dart';
void main() {
testWidgets(
'WidgetTester.drag must break the offset into multiple parallel components if'
'the drag goes outside the touch slop values',
(WidgetTester tester) async {
// The first Offset in every sub array (ie. offsetResults[i][0]) is (touchSlopX, touchSlopY).
// The second Offset in every sub array (ie. offsetResults[i][1]) will be the total move offset.
// The remaining values in every sub array are the expected separated drag offsets.
// This test checks to make sure that the total drag will be correctly split into
// pieces such that the first (and potentially second) moveBy function call(s) in
// controller.drag() will never have a component greater than the touch
// slop in that component's respective axis.
final List<List<Offset>> offsetResults = <List<Offset>>[
<Offset>[
const Offset(10.0, 10.0),
const Offset(-150.0, 200.0),
const Offset(-7.5, 10.0),
const Offset(-2.5, 3.333333333333333),
const Offset(-140.0, 186.66666666666666),
],
<Offset>[
const Offset(10.0, 10.0),
const Offset(150, -200),
const Offset(7.5, -10),
const Offset(2.5, -3.333333333333333),
const Offset(140.0, -186.66666666666666),
],
<Offset>[
const Offset(10.0, 10.0),
const Offset(-200, 150),
const Offset(-10, 7.5),
const Offset(-3.333333333333333, 2.5),
const Offset(-186.66666666666666, 140.0),
],
<Offset>[
const Offset(10.0, 10.0),
const Offset(200.0, -150.0),
const Offset(10, -7.5),
const Offset(3.333333333333333, -2.5),
const Offset(186.66666666666666, -140.0),
],
<Offset>[
const Offset(10.0, 10.0),
const Offset(-150.0, -200.0),
const Offset(-7.5, -10.0),
const Offset(-2.5, -3.333333333333333),
const Offset(-140.0, -186.66666666666666),
],
<Offset>[
const Offset(10.0, 10.0),
const Offset(8.0, 3.0),
const Offset(8.0, 3.0),
],
<Offset>[
const Offset(10.0, 10.0),
const Offset(3.0, 8.0),
const Offset(3.0, 8.0),
],
<Offset>[
const Offset(10.0, 10.0),
const Offset(20.0, 5.0),
const Offset(10.0, 2.5),
const Offset(10.0, 2.5),
],
<Offset>[
const Offset(10.0, 10.0),
const Offset(5.0, 20.0),
const Offset(2.5, 10.0),
const Offset(2.5, 10.0),
],
<Offset>[
const Offset(10.0, 10.0),
const Offset(20.0, 15.0),
const Offset(10.0, 7.5),
const Offset(3.333333333333333, 2.5),
const Offset(6.666666666666668, 5.0),
],
<Offset>[
const Offset(10.0, 10.0),
const Offset(15.0, 20.0),
const Offset(7.5, 10.0),
const Offset(2.5, 3.333333333333333),
const Offset(5.0, 6.666666666666668),
],
<Offset>[
const Offset(10.0, 10.0),
const Offset(20.0, 20.0),
const Offset(10.0, 10.0),
const Offset(10.0, 10.0),
],
<Offset>[
const Offset(10.0, 10.0),
const Offset(0.0, 5.0),
const Offset(0.0, 5.0),
],
//// [VARYING TOUCH SLOP] ////
<Offset>[
const Offset(12.0, 5.0),
const Offset(0.0, 5.0),
const Offset(0.0, 5.0),
],
<Offset>[
const Offset(12.0, 5.0),
const Offset(20.0, 5.0),
const Offset(12.0, 3.0),
const Offset(8.0, 2.0),
],
<Offset>[
const Offset(12.0, 5.0),
const Offset(5.0, 20.0),
const Offset(1.25, 5.0),
const Offset(3.75, 15.0),
],
<Offset>[
const Offset(5.0, 12.0),
const Offset(5.0, 20.0),
const Offset(3.0, 12.0),
const Offset(2.0, 8.0),
],
<Offset>[
const Offset(5.0, 12.0),
const Offset(20.0, 5.0),
const Offset(5.0, 1.25),
const Offset(15.0, 3.75),
],
<Offset>[
const Offset(18.0, 18.0),
const Offset(0.0, 150.0),
const Offset(0.0, 18.0),
const Offset(0.0, 132.0),
],
<Offset>[
const Offset(18.0, 18.0),
const Offset(0.0, -150.0),
const Offset(0.0, -18.0),
const Offset(0.0, -132.0),
],
<Offset>[
const Offset(18.0, 18.0),
const Offset(-150.0, 0.0),
const Offset(-18.0, 0.0),
const Offset(-132.0, 0.0),
],
<Offset>[
const Offset(0.0, 0.0),
const Offset(-150.0, 0.0),
const Offset(-150.0, 0.0),
],
<Offset>[
const Offset(18.0, 18.0),
const Offset(-32.0, 0.0),
const Offset(-18.0, 0.0),
const Offset(-14.0, 0.0),
],
];
await tester.pumpWidget(
const Directionality(
textDirection: TextDirection.ltr,
child: Text('test'),
),
);
final WidgetControllerSpy spyController = WidgetControllerSpy(tester.binding);
for (int resultIndex = 0; resultIndex < offsetResults.length; resultIndex += 1) {
final List<Offset> testResult = offsetResults[resultIndex];
await spyController.drag(
find.text('test'),
testResult[1],
touchSlopX: testResult[0].dx,
touchSlopY: testResult[0].dy,
);
final List<Offset> dragOffsets = spyController.testGestureSpy.offsets;
expect(
offsetResults[resultIndex].length - 2,
dragOffsets.length,
reason:
'There is a difference in the number of expected and actual split offsets for the drag with:\n'
'Touch Slop: ' + testResult[0].toString() + '\n'
'Delta: ' + testResult[1].toString() + '\n',
);
for (int valueIndex = 2; valueIndex < offsetResults[resultIndex].length; valueIndex += 1) {
expect(
offsetResults[resultIndex][valueIndex],
dragOffsets[valueIndex - 2],
reason:
'There is a difference in the expected and actual value of the ' +
(valueIndex == 2 ? 'first' : valueIndex == 3 ? 'second' : 'third') +
' split offset for the drag with:\n'
'Touch slop: ' + testResult[0].toString() + '\n'
'Delta: ' + testResult[1].toString() + '\n',
);
}
spyController.testGestureSpy.clearOffsets();
}
},
);
}
class WidgetControllerSpy extends WidgetController {
WidgetControllerSpy(
TestWidgetsFlutterBinding binding
) : super(binding) {
_binding = binding;
}
TestWidgetsFlutterBinding _binding;
@override
Future<void> pump(Duration duration) async {
if (duration != null)
await Future<void>.delayed(duration);
_binding.scheduleFrame();
await _binding.endOfFrame;
}
int _getNextPointer() {
final int result = nextPointer;
nextPointer += 1;
return result;
}
@override
Future<void> sendEventToBinding(PointerEvent event, HitTestResult result) {
return TestAsyncUtils.guard<void>(() async {
_binding.dispatchEvent(event, result, source: TestBindingEventSource.test);
});
}
TestGestureSpy testGestureSpy;
@override
Future<TestGesture> createGesture({int pointer, PointerDeviceKind kind = PointerDeviceKind.touch}) async {
return testGestureSpy = TestGestureSpy(
pointer: pointer ?? _getNextPointer(),
kind: kind,
dispatcher: sendEventToBinding,
hitTester: hitTestOnBinding
);
}
}
class TestGestureSpy extends TestGesture {
TestGestureSpy({
int pointer,
PointerDeviceKind kind,
EventDispatcher dispatcher,
HitTester hitTester
}) : super(
pointer: pointer,
kind: kind,
dispatcher: dispatcher,
hitTester: hitTester
);
List<Offset> offsets = <Offset>[];
void clearOffsets() {
offsets = <Offset>[];
}
@override
Future<void> moveBy(Offset offset, {Duration timeStamp = Duration.zero}) {
offsets.add(offset);
return super.moveBy(offset, timeStamp: timeStamp);
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment