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 { ...@@ -60,7 +60,7 @@ class CupertinoSwitch extends StatefulWidget {
@required this.value, @required this.value,
@required this.onChanged, @required this.onChanged,
this.activeColor, this.activeColor,
this.dragStartBehavior = DragStartBehavior.down, this.dragStartBehavior = DragStartBehavior.start,
}) : assert(dragStartBehavior != null), }) : assert(dragStartBehavior != null),
super(key: key); super(key: key);
...@@ -97,7 +97,6 @@ class CupertinoSwitch extends StatefulWidget { ...@@ -97,7 +97,6 @@ class CupertinoSwitch extends StatefulWidget {
/// [CupertinoTheme] in accordance to native iOS behavior. /// [CupertinoTheme] in accordance to native iOS behavior.
final Color activeColor; final Color activeColor;
// TODO(jslavitz): Set the DragStartBehavior default to be start across all widgets.
/// {@template flutter.cupertino.switch.dragStartBehavior} /// {@template flutter.cupertino.switch.dragStartBehavior}
/// Determines the way that drag start behavior is handled. /// Determines the way that drag start behavior is handled.
/// ///
...@@ -110,7 +109,7 @@ class CupertinoSwitch extends StatefulWidget { ...@@ -110,7 +109,7 @@ class CupertinoSwitch extends StatefulWidget {
/// animation smoother and setting it to [DragStartBehavior.down] will make /// animation smoother and setting it to [DragStartBehavior.down] will make
/// drag behavior feel slightly more reactive. /// 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: /// See also:
/// ///
...@@ -149,7 +148,7 @@ class _CupertinoSwitchRenderObjectWidget extends LeafRenderObjectWidget { ...@@ -149,7 +148,7 @@ class _CupertinoSwitchRenderObjectWidget extends LeafRenderObjectWidget {
this.activeColor, this.activeColor,
this.onChanged, this.onChanged,
this.vsync, this.vsync,
this.dragStartBehavior = DragStartBehavior.down, this.dragStartBehavior = DragStartBehavior.start,
}) : super(key: key); }) : super(key: key);
final bool value; final bool value;
...@@ -202,7 +201,7 @@ class _RenderCupertinoSwitch extends RenderConstrainedBox { ...@@ -202,7 +201,7 @@ class _RenderCupertinoSwitch extends RenderConstrainedBox {
ValueChanged<bool> onChanged, ValueChanged<bool> onChanged,
@required TextDirection textDirection, @required TextDirection textDirection,
@required TickerProvider vsync, @required TickerProvider vsync,
DragStartBehavior dragStartBehavior = DragStartBehavior.down, DragStartBehavior dragStartBehavior = DragStartBehavior.start,
}) : assert(value != null), }) : assert(value != null),
assert(activeColor != null), assert(activeColor != null),
assert(vsync != null), assert(vsync != null),
......
...@@ -54,6 +54,10 @@ const Duration kZoomControlsTimeout = Duration(milliseconds: 3000); ...@@ -54,6 +54,10 @@ const Duration kZoomControlsTimeout = Duration(milliseconds: 3000);
/// the gesture is a scroll gesture, or, inversely, the maximum distance that a /// 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 /// touch can travel before the framework becomes confident that it is not a
/// tap. /// 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 // 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. // 18.0 after getting complaints that it was too difficult to hit targets.
const double kTouchSlop = 18.0; // Logical pixels const double kTouchSlop = 18.0; // Logical pixels
......
...@@ -54,7 +54,7 @@ abstract class DragGestureRecognizer extends OneSequenceGestureRecognizer { ...@@ -54,7 +54,7 @@ abstract class DragGestureRecognizer extends OneSequenceGestureRecognizer {
/// [dragStartBehavior] must not be null. /// [dragStartBehavior] must not be null.
DragGestureRecognizer({ DragGestureRecognizer({
Object debugOwner, Object debugOwner,
this.dragStartBehavior = DragStartBehavior.down, this.dragStartBehavior = DragStartBehavior.start,
}) : assert(dragStartBehavior != null), }) : assert(dragStartBehavior != null),
super(debugOwner: debugOwner); super(debugOwner: debugOwner);
......
...@@ -254,7 +254,7 @@ class DayPicker extends StatelessWidget { ...@@ -254,7 +254,7 @@ class DayPicker extends StatelessWidget {
@required this.lastDate, @required this.lastDate,
@required this.displayedMonth, @required this.displayedMonth,
this.selectableDayPredicate, this.selectableDayPredicate,
this.dragStartBehavior = DragStartBehavior.down, this.dragStartBehavior = DragStartBehavior.start,
}) : assert(selectedDate != null), }) : assert(selectedDate != null),
assert(currentDate != null), assert(currentDate != null),
assert(onChanged != null), assert(onChanged != null),
...@@ -287,7 +287,6 @@ class DayPicker extends StatelessWidget { ...@@ -287,7 +287,6 @@ class DayPicker extends StatelessWidget {
/// Optional user supplied predicate function to customize selectable days. /// Optional user supplied predicate function to customize selectable days.
final SelectableDayPredicate selectableDayPredicate; final SelectableDayPredicate selectableDayPredicate;
// TODO(jslavitz): Set the DragStartBehavior default to be start across all widgets.
/// Determines the way that drag start behavior is handled. /// Determines the way that drag start behavior is handled.
/// ///
/// If set to [DragStartBehavior.start], the drag gesture used to scroll a /// If set to [DragStartBehavior.start], the drag gesture used to scroll a
...@@ -299,7 +298,7 @@ class DayPicker extends StatelessWidget { ...@@ -299,7 +298,7 @@ class DayPicker extends StatelessWidget {
/// animation smoother and setting it to [DragStartBehavior.down] will make /// animation smoother and setting it to [DragStartBehavior.down] will make
/// drag behavior feel slightly more reactive. /// 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: /// See also:
/// ///
...@@ -528,7 +527,7 @@ class MonthPicker extends StatefulWidget { ...@@ -528,7 +527,7 @@ class MonthPicker extends StatefulWidget {
@required this.firstDate, @required this.firstDate,
@required this.lastDate, @required this.lastDate,
this.selectableDayPredicate, this.selectableDayPredicate,
this.dragStartBehavior = DragStartBehavior.down, this.dragStartBehavior = DragStartBehavior.start,
}) : assert(selectedDate != null), }) : assert(selectedDate != null),
assert(onChanged != null), assert(onChanged != null),
assert(!firstDate.isAfter(lastDate)), assert(!firstDate.isAfter(lastDate)),
...@@ -791,7 +790,7 @@ class YearPicker extends StatefulWidget { ...@@ -791,7 +790,7 @@ class YearPicker extends StatefulWidget {
@required this.onChanged, @required this.onChanged,
@required this.firstDate, @required this.firstDate,
@required this.lastDate, @required this.lastDate,
this.dragStartBehavior = DragStartBehavior.down, this.dragStartBehavior = DragStartBehavior.start,
}) : assert(selectedDate != null), }) : assert(selectedDate != null),
assert(onChanged != null), assert(onChanged != null),
assert(!firstDate.isAfter(lastDate)), assert(!firstDate.isAfter(lastDate)),
......
...@@ -180,7 +180,7 @@ class DrawerController extends StatefulWidget { ...@@ -180,7 +180,7 @@ class DrawerController extends StatefulWidget {
@required this.child, @required this.child,
@required this.alignment, @required this.alignment,
this.drawerCallback, this.drawerCallback,
this.dragStartBehavior = DragStartBehavior.down, this.dragStartBehavior = DragStartBehavior.start,
}) : assert(child != null), }) : assert(child != null),
assert(dragStartBehavior != null), assert(dragStartBehavior != null),
assert(alignment != null), assert(alignment != null),
...@@ -200,7 +200,6 @@ class DrawerController extends StatefulWidget { ...@@ -200,7 +200,6 @@ class DrawerController extends StatefulWidget {
/// Optional callback that is called when a [Drawer] is opened or closed. /// Optional callback that is called when a [Drawer] is opened or closed.
final DrawerCallback drawerCallback; final DrawerCallback drawerCallback;
// TODO(jslavitz): Set the DragStartBehavior default to be start across all widgets.
/// {@template flutter.material.drawer.dragStartBehavior} /// {@template flutter.material.drawer.dragStartBehavior}
/// Determines the way that drag start behavior is handled. /// Determines the way that drag start behavior is handled.
/// ///
...@@ -213,7 +212,7 @@ class DrawerController extends StatefulWidget { ...@@ -213,7 +212,7 @@ class DrawerController extends StatefulWidget {
/// animation smoother and setting it to [DragStartBehavior.down] will make /// animation smoother and setting it to [DragStartBehavior.down] will make
/// drag behavior feel slightly more reactive. /// 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: /// See also:
/// ///
......
...@@ -75,7 +75,7 @@ class PaginatedDataTable extends StatefulWidget { ...@@ -75,7 +75,7 @@ class PaginatedDataTable extends StatefulWidget {
this.rowsPerPage = defaultRowsPerPage, this.rowsPerPage = defaultRowsPerPage,
this.availableRowsPerPage = const <int>[defaultRowsPerPage, defaultRowsPerPage * 2, defaultRowsPerPage * 5, defaultRowsPerPage * 10], this.availableRowsPerPage = const <int>[defaultRowsPerPage, defaultRowsPerPage * 2, defaultRowsPerPage * 5, defaultRowsPerPage * 10],
this.onRowsPerPageChanged, this.onRowsPerPageChanged,
this.dragStartBehavior = DragStartBehavior.down, this.dragStartBehavior = DragStartBehavior.start,
@required this.source @required this.source
}) : assert(header != null), }) : assert(header != null),
assert(columns != null), assert(columns != null),
......
...@@ -876,8 +876,8 @@ class Scaffold extends StatefulWidget { ...@@ -876,8 +876,8 @@ class Scaffold extends StatefulWidget {
this.resizeToAvoidBottomPadding, this.resizeToAvoidBottomPadding,
this.resizeToAvoidBottomInset, this.resizeToAvoidBottomInset,
this.primary = true, this.primary = true,
this.drawerDragStartBehavior = DragStartBehavior.start,
this.extendBody = false, this.extendBody = false,
this.drawerDragStartBehavior = DragStartBehavior.down,
}) : assert(primary != null), }) : assert(primary != null),
assert(extendBody != null), assert(extendBody != null),
assert(drawerDragStartBehavior != null), assert(drawerDragStartBehavior != null),
......
...@@ -73,7 +73,7 @@ class Switch extends StatefulWidget { ...@@ -73,7 +73,7 @@ class Switch extends StatefulWidget {
this.activeThumbImage, this.activeThumbImage,
this.inactiveThumbImage, this.inactiveThumbImage,
this.materialTapTargetSize, this.materialTapTargetSize,
this.dragStartBehavior = DragStartBehavior.down, this.dragStartBehavior = DragStartBehavior.start,
}) : _switchType = _SwitchType.material, }) : _switchType = _SwitchType.material,
assert(dragStartBehavior != null), assert(dragStartBehavior != null),
super(key: key); super(key: key);
...@@ -97,7 +97,7 @@ class Switch extends StatefulWidget { ...@@ -97,7 +97,7 @@ class Switch extends StatefulWidget {
this.activeThumbImage, this.activeThumbImage,
this.inactiveThumbImage, this.inactiveThumbImage,
this.materialTapTargetSize, this.materialTapTargetSize,
this.dragStartBehavior = DragStartBehavior.down, this.dragStartBehavior = DragStartBehavior.start,
}) : _switchType = _SwitchType.adaptive, }) : _switchType = _SwitchType.adaptive,
super(key: key); super(key: key);
......
...@@ -562,7 +562,7 @@ class TabBar extends StatefulWidget implements PreferredSizeWidget { ...@@ -562,7 +562,7 @@ class TabBar extends StatefulWidget implements PreferredSizeWidget {
this.labelPadding, this.labelPadding,
this.unselectedLabelColor, this.unselectedLabelColor,
this.unselectedLabelStyle, this.unselectedLabelStyle,
this.dragStartBehavior = DragStartBehavior.down, this.dragStartBehavior = DragStartBehavior.start,
this.onTap, this.onTap,
}) : assert(tabs != null), }) : assert(tabs != null),
assert(isScrollable != null), assert(isScrollable != null),
...@@ -1057,7 +1057,7 @@ class TabBarView extends StatefulWidget { ...@@ -1057,7 +1057,7 @@ class TabBarView extends StatefulWidget {
@required this.children, @required this.children,
this.controller, this.controller,
this.physics, this.physics,
this.dragStartBehavior = DragStartBehavior.down, this.dragStartBehavior = DragStartBehavior.start,
}) : assert(children != null), }) : assert(children != null),
assert(dragStartBehavior != null), assert(dragStartBehavior != null),
super(key: key); super(key: key);
......
...@@ -143,7 +143,7 @@ class TextField extends StatefulWidget { ...@@ -143,7 +143,7 @@ class TextField extends StatefulWidget {
this.cursorColor, this.cursorColor,
this.keyboardAppearance, this.keyboardAppearance,
this.scrollPadding = const EdgeInsets.all(20.0), this.scrollPadding = const EdgeInsets.all(20.0),
this.dragStartBehavior = DragStartBehavior.down, this.dragStartBehavior = DragStartBehavior.start,
this.enableInteractiveSelection, this.enableInteractiveSelection,
this.onTap, this.onTap,
this.buildCounter, this.buildCounter,
......
...@@ -91,7 +91,7 @@ class Dismissible extends StatefulWidget { ...@@ -91,7 +91,7 @@ class Dismissible extends StatefulWidget {
this.dismissThresholds = const <DismissDirection, double>{}, this.dismissThresholds = const <DismissDirection, double>{},
this.movementDuration = const Duration(milliseconds: 200), this.movementDuration = const Duration(milliseconds: 200),
this.crossAxisEndOffset = 0.0, this.crossAxisEndOffset = 0.0,
this.dragStartBehavior = DragStartBehavior.down, this.dragStartBehavior = DragStartBehavior.start,
}) : assert(key != null), }) : assert(key != null),
assert(secondaryBackground != null ? background != null : true), assert(secondaryBackground != null ? background != null : true),
assert(dragStartBehavior != null), assert(dragStartBehavior != null),
...@@ -162,7 +162,6 @@ class Dismissible extends StatefulWidget { ...@@ -162,7 +162,6 @@ class Dismissible extends StatefulWidget {
/// it is positive or negative. /// it is positive or negative.
final double crossAxisEndOffset; final double crossAxisEndOffset;
// TODO(jslavitz): Set the DragStartBehavior default to be start across all widgets.
/// Determines the way that drag start behavior is handled. /// Determines the way that drag start behavior is handled.
/// ///
/// If set to [DragStartBehavior.start], the drag gesture used to dismiss a /// If set to [DragStartBehavior.start], the drag gesture used to dismiss a
...@@ -173,7 +172,7 @@ class Dismissible extends StatefulWidget { ...@@ -173,7 +172,7 @@ class Dismissible extends StatefulWidget {
/// animation smoother and setting it to [DragStartBehavior.down] will make /// animation smoother and setting it to [DragStartBehavior.down] will make
/// drag behavior feel slightly more reactive. /// 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: /// See also:
/// ///
......
...@@ -231,7 +231,7 @@ class EditableText extends StatefulWidget { ...@@ -231,7 +231,7 @@ class EditableText extends StatefulWidget {
this.paintCursorAboveText = false, this.paintCursorAboveText = false,
this.scrollPadding = const EdgeInsets.all(20.0), this.scrollPadding = const EdgeInsets.all(20.0),
this.keyboardAppearance = Brightness.light, this.keyboardAppearance = Brightness.light,
this.dragStartBehavior = DragStartBehavior.down, this.dragStartBehavior = DragStartBehavior.start,
this.enableInteractiveSelection, this.enableInteractiveSelection,
}) : assert(controller != null), }) : assert(controller != null),
assert(focusNode != null), assert(focusNode != null),
......
...@@ -186,7 +186,7 @@ class GestureDetector extends StatelessWidget { ...@@ -186,7 +186,7 @@ class GestureDetector extends StatelessWidget {
this.onScaleEnd, this.onScaleEnd,
this.behavior, this.behavior,
this.excludeFromSemantics = false, this.excludeFromSemantics = false,
this.dragStartBehavior = DragStartBehavior.down, this.dragStartBehavior = DragStartBehavior.start,
}) : assert(excludeFromSemantics != null), }) : assert(excludeFromSemantics != null),
assert(dragStartBehavior != null), assert(dragStartBehavior != null),
assert(() { assert(() {
...@@ -373,7 +373,6 @@ class GestureDetector extends StatelessWidget { ...@@ -373,7 +373,6 @@ class GestureDetector extends StatelessWidget {
/// duplication of information. /// duplication of information.
final bool excludeFromSemantics; final bool excludeFromSemantics;
// TODO(jslavitz): Set the DragStartBehavior default to be start across all widgets.
/// Determines the way that drag start behavior is handled. /// Determines the way that drag start behavior is handled.
/// ///
/// If set to [DragStartBehavior.start], gesture drag behavior will /// If set to [DragStartBehavior.start], gesture drag behavior will
...@@ -384,7 +383,7 @@ class GestureDetector extends StatelessWidget { ...@@ -384,7 +383,7 @@ class GestureDetector extends StatelessWidget {
/// animation smoother and setting it to [DragStartBehavior.down] will make /// animation smoother and setting it to [DragStartBehavior.down] will make
/// drag behavior feel slightly more reactive. /// 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], /// Only the [onStart] callbacks for the [VerticalDragGestureRecognizer],
/// [HorizontalDragGestureRecognizer] and [PanGestureRecognizer] are affected /// [HorizontalDragGestureRecognizer] and [PanGestureRecognizer] are affected
......
...@@ -189,7 +189,7 @@ class NestedScrollView extends StatefulWidget { ...@@ -189,7 +189,7 @@ class NestedScrollView extends StatefulWidget {
this.physics, this.physics,
@required this.headerSliverBuilder, @required this.headerSliverBuilder,
@required this.body, @required this.body,
this.dragStartBehavior = DragStartBehavior.down, this.dragStartBehavior = DragStartBehavior.start,
}) : assert(scrollDirection != null), }) : assert(scrollDirection != null),
assert(reverse != null), assert(reverse != null),
assert(headerSliverBuilder != null), assert(headerSliverBuilder != null),
...@@ -371,7 +371,7 @@ class _NestedScrollViewCustomScrollView extends CustomScrollView { ...@@ -371,7 +371,7 @@ class _NestedScrollViewCustomScrollView extends CustomScrollView {
@required ScrollController controller, @required ScrollController controller,
@required List<Widget> slivers, @required List<Widget> slivers,
@required this.handle, @required this.handle,
DragStartBehavior dragStartBehavior = DragStartBehavior.down, DragStartBehavior dragStartBehavior = DragStartBehavior.start,
}) : super( }) : super(
scrollDirection: scrollDirection, scrollDirection: scrollDirection,
reverse: reverse, reverse: reverse,
......
...@@ -425,7 +425,7 @@ class PageView extends StatefulWidget { ...@@ -425,7 +425,7 @@ class PageView extends StatefulWidget {
this.pageSnapping = true, this.pageSnapping = true,
this.onPageChanged, this.onPageChanged,
List<Widget> children = const <Widget>[], List<Widget> children = const <Widget>[],
this.dragStartBehavior = DragStartBehavior.down, this.dragStartBehavior = DragStartBehavior.start,
}) : controller = controller ?? _defaultPageController, }) : controller = controller ?? _defaultPageController,
childrenDelegate = SliverChildListDelegate(children), childrenDelegate = SliverChildListDelegate(children),
super(key: key); super(key: key);
...@@ -452,7 +452,7 @@ class PageView extends StatefulWidget { ...@@ -452,7 +452,7 @@ class PageView extends StatefulWidget {
this.onPageChanged, this.onPageChanged,
@required IndexedWidgetBuilder itemBuilder, @required IndexedWidgetBuilder itemBuilder,
int itemCount, int itemCount,
this.dragStartBehavior = DragStartBehavior.down, this.dragStartBehavior = DragStartBehavior.start,
}) : controller = controller ?? _defaultPageController, }) : controller = controller ?? _defaultPageController,
childrenDelegate = SliverChildBuilderDelegate(itemBuilder, childCount: itemCount), childrenDelegate = SliverChildBuilderDelegate(itemBuilder, childCount: itemCount),
super(key: key); super(key: key);
...@@ -468,7 +468,7 @@ class PageView extends StatefulWidget { ...@@ -468,7 +468,7 @@ class PageView extends StatefulWidget {
this.pageSnapping = true, this.pageSnapping = true,
this.onPageChanged, this.onPageChanged,
@required this.childrenDelegate, @required this.childrenDelegate,
this.dragStartBehavior = DragStartBehavior.down, this.dragStartBehavior = DragStartBehavior.start,
}) : assert(childrenDelegate != null), }) : assert(childrenDelegate != null),
controller = controller ?? _defaultPageController, controller = controller ?? _defaultPageController,
super(key: key); super(key: key);
......
...@@ -312,7 +312,6 @@ class ScrollDragController implements Drag { ...@@ -312,7 +312,6 @@ class ScrollDragController implements Drag {
// May be null for proxied drags like via accessibility. // May be null for proxied drags like via accessibility.
return offset; return offset;
} }
if (offset == 0.0) { if (offset == 0.0) {
if (motionStartDistanceThreshold != null && if (motionStartDistanceThreshold != null &&
_offsetSinceLastStop == null && _offsetSinceLastStop == null &&
......
...@@ -69,7 +69,7 @@ abstract class ScrollView extends StatelessWidget { ...@@ -69,7 +69,7 @@ abstract class ScrollView extends StatelessWidget {
this.anchor = 0.0, this.anchor = 0.0,
this.cacheExtent, this.cacheExtent,
this.semanticChildCount, this.semanticChildCount,
this.dragStartBehavior = DragStartBehavior.down, this.dragStartBehavior = DragStartBehavior.start,
}) : assert(scrollDirection != null), }) : assert(scrollDirection != null),
assert(reverse != null), assert(reverse != null),
assert(shrinkWrap != null), assert(shrinkWrap != null),
...@@ -454,7 +454,7 @@ class CustomScrollView extends ScrollView { ...@@ -454,7 +454,7 @@ class CustomScrollView extends ScrollView {
double cacheExtent, double cacheExtent,
this.slivers = const <Widget>[], this.slivers = const <Widget>[],
int semanticChildCount, int semanticChildCount,
DragStartBehavior dragStartBehavior = DragStartBehavior.down, DragStartBehavior dragStartBehavior = DragStartBehavior.start,
}) : super( }) : super(
key: key, key: key,
scrollDirection: scrollDirection, scrollDirection: scrollDirection,
...@@ -500,7 +500,7 @@ abstract class BoxScrollView extends ScrollView { ...@@ -500,7 +500,7 @@ abstract class BoxScrollView extends ScrollView {
this.padding, this.padding,
double cacheExtent, double cacheExtent,
int semanticChildCount, int semanticChildCount,
DragStartBehavior dragStartBehavior = DragStartBehavior.down, DragStartBehavior dragStartBehavior = DragStartBehavior.start,
}) : super( }) : super(
key: key, key: key,
scrollDirection: scrollDirection, scrollDirection: scrollDirection,
...@@ -802,7 +802,7 @@ class ListView extends BoxScrollView { ...@@ -802,7 +802,7 @@ class ListView extends BoxScrollView {
double cacheExtent, double cacheExtent,
List<Widget> children = const <Widget>[], List<Widget> children = const <Widget>[],
int semanticChildCount, int semanticChildCount,
DragStartBehavior dragStartBehavior = DragStartBehavior.down, DragStartBehavior dragStartBehavior = DragStartBehavior.start,
}) : childrenDelegate = SliverChildListDelegate( }) : childrenDelegate = SliverChildListDelegate(
children, children,
addAutomaticKeepAlives: addAutomaticKeepAlives, addAutomaticKeepAlives: addAutomaticKeepAlives,
...@@ -866,7 +866,7 @@ class ListView extends BoxScrollView { ...@@ -866,7 +866,7 @@ class ListView extends BoxScrollView {
bool addSemanticIndexes = true, bool addSemanticIndexes = true,
double cacheExtent, double cacheExtent,
int semanticChildCount, int semanticChildCount,
DragStartBehavior dragStartBehavior = DragStartBehavior.down, DragStartBehavior dragStartBehavior = DragStartBehavior.start,
}) : childrenDelegate = SliverChildBuilderDelegate( }) : childrenDelegate = SliverChildBuilderDelegate(
itemBuilder, itemBuilder,
childCount: itemCount, childCount: itemCount,
...@@ -1320,7 +1320,7 @@ class GridView extends BoxScrollView { ...@@ -1320,7 +1320,7 @@ class GridView extends BoxScrollView {
@required this.childrenDelegate, @required this.childrenDelegate,
double cacheExtent, double cacheExtent,
int semanticChildCount, int semanticChildCount,
DragStartBehavior dragStartBehavior = DragStartBehavior.down, DragStartBehavior dragStartBehavior = DragStartBehavior.start,
}) : assert(gridDelegate != null), }) : assert(gridDelegate != null),
assert(childrenDelegate != null), assert(childrenDelegate != null),
super( super(
...@@ -1370,7 +1370,7 @@ class GridView extends BoxScrollView { ...@@ -1370,7 +1370,7 @@ class GridView extends BoxScrollView {
double cacheExtent, double cacheExtent,
List<Widget> children = const <Widget>[], List<Widget> children = const <Widget>[],
int semanticChildCount, int semanticChildCount,
DragStartBehavior dragStartBehavior = DragStartBehavior.down, DragStartBehavior dragStartBehavior = DragStartBehavior.start,
}) : gridDelegate = SliverGridDelegateWithFixedCrossAxisCount( }) : gridDelegate = SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: crossAxisCount, crossAxisCount: crossAxisCount,
mainAxisSpacing: mainAxisSpacing, mainAxisSpacing: mainAxisSpacing,
...@@ -1429,7 +1429,7 @@ class GridView extends BoxScrollView { ...@@ -1429,7 +1429,7 @@ class GridView extends BoxScrollView {
bool addSemanticIndexes = true, bool addSemanticIndexes = true,
List<Widget> children = const <Widget>[], List<Widget> children = const <Widget>[],
int semanticChildCount, int semanticChildCount,
DragStartBehavior dragStartBehavior = DragStartBehavior.down, DragStartBehavior dragStartBehavior = DragStartBehavior.start,
}) : gridDelegate = SliverGridDelegateWithMaxCrossAxisExtent( }) : gridDelegate = SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: maxCrossAxisExtent, maxCrossAxisExtent: maxCrossAxisExtent,
mainAxisSpacing: mainAxisSpacing, mainAxisSpacing: mainAxisSpacing,
......
...@@ -81,7 +81,7 @@ class Scrollable extends StatefulWidget { ...@@ -81,7 +81,7 @@ class Scrollable extends StatefulWidget {
@required this.viewportBuilder, @required this.viewportBuilder,
this.excludeFromSemantics = false, this.excludeFromSemantics = false,
this.semanticChildCount, this.semanticChildCount,
this.dragStartBehavior = DragStartBehavior.down, this.dragStartBehavior = DragStartBehavior.start,
}) : assert(axisDirection != null), }) : assert(axisDirection != null),
assert(dragStartBehavior != null), assert(dragStartBehavior != null),
assert(viewportBuilder != null), assert(viewportBuilder != null),
...@@ -194,7 +194,7 @@ class Scrollable extends StatefulWidget { ...@@ -194,7 +194,7 @@ class Scrollable extends StatefulWidget {
/// animation smoother and setting it to [DragStartBehavior.down] will make /// animation smoother and setting it to [DragStartBehavior.down] will make
/// drag behavior feel slightly more reactive. /// 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: /// See also:
/// ///
......
...@@ -204,7 +204,7 @@ class SingleChildScrollView extends StatelessWidget { ...@@ -204,7 +204,7 @@ class SingleChildScrollView extends StatelessWidget {
this.physics, this.physics,
this.controller, this.controller,
this.child, this.child,
this.dragStartBehavior = DragStartBehavior.down, this.dragStartBehavior = DragStartBehavior.start,
}) : assert(scrollDirection != null), }) : assert(scrollDirection != null),
assert(dragStartBehavior != null), assert(dragStartBehavior != null),
assert(!(controller != null && primary == true), assert(!(controller != null && primary == true),
......
...@@ -230,7 +230,7 @@ class TextSelectionOverlay { ...@@ -230,7 +230,7 @@ class TextSelectionOverlay {
@required this.renderObject, @required this.renderObject,
this.selectionControls, this.selectionControls,
this.selectionDelegate, this.selectionDelegate,
this.dragStartBehavior = DragStartBehavior.down, this.dragStartBehavior = DragStartBehavior.start,
}) : assert(value != null), }) : assert(value != null),
assert(context != null), assert(context != null),
_value = value { _value = value {
...@@ -265,7 +265,6 @@ class TextSelectionOverlay { ...@@ -265,7 +265,6 @@ class TextSelectionOverlay {
/// text field. /// text field.
final TextSelectionDelegate selectionDelegate; final TextSelectionDelegate selectionDelegate;
// TODO(jslavitz): Set the DragStartBehavior default to be start across all widgets.
/// Determines the way that drag start behavior is handled. /// Determines the way that drag start behavior is handled.
/// ///
/// If set to [DragStartBehavior.start], handle drag behavior will /// If set to [DragStartBehavior.start], handle drag behavior will
...@@ -276,7 +275,7 @@ class TextSelectionOverlay { ...@@ -276,7 +275,7 @@ class TextSelectionOverlay {
/// animation smoother and setting it to [DragStartBehavior.down] will make /// animation smoother and setting it to [DragStartBehavior.down] will make
/// drag behavior feel slightly more reactive. /// 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: /// See also:
/// ///
...@@ -468,7 +467,7 @@ class _TextSelectionHandleOverlay extends StatefulWidget { ...@@ -468,7 +467,7 @@ class _TextSelectionHandleOverlay extends StatefulWidget {
@required this.onSelectionHandleChanged, @required this.onSelectionHandleChanged,
@required this.onSelectionHandleTapped, @required this.onSelectionHandleTapped,
@required this.selectionControls, @required this.selectionControls,
this.dragStartBehavior = DragStartBehavior.down, this.dragStartBehavior = DragStartBehavior.start,
}) : super(key: key); }) : super(key: key);
final TextSelection selection; final TextSelection selection;
......
...@@ -8,7 +8,7 @@ import 'package:flutter/semantics.dart'; ...@@ -8,7 +8,7 @@ import 'package:flutter/semantics.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
// scrolling by this offset will move the picker to the next item // 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() { void main() {
group('Countdown timer picker', () { group('Countdown timer picker', () {
...@@ -280,7 +280,7 @@ void main() { ...@@ -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();
await tester.pump(const Duration(milliseconds: 500)); await tester.pump(const Duration(milliseconds: 500));
...@@ -301,7 +301,7 @@ void main() { ...@@ -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();
await tester.pump(const Duration(milliseconds: 500)); await tester.pump(const Duration(milliseconds: 500));
...@@ -487,7 +487,8 @@ void main() { ...@@ -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. // Momentarily, the 2018 and the incorrect 30 of February is aligned.
expect( expect(
tester.getTopLeft(find.text('2018')).dy, tester.getTopLeft(find.text('2018')).dy,
...@@ -525,15 +526,14 @@ void main() { ...@@ -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(); await tester.pump();
expect( expect(
date, date,
DateTime(2018, 2, 28), DateTime(2018, 2, 28),
); );
await tester.drag(find.text('28'), const Offset(0.0, -32.0), touchSlopY: 0.0);
await tester.drag(find.text('28'), const Offset(0.0, -32.0));
await tester.pump(); // Once to trigger the post frame animate call. await tester.pump(); // Once to trigger the post frame animate call.
// Callback doesn't transiently go into invalid dates. // Callback doesn't transiently go into invalid dates.
...@@ -706,6 +706,8 @@ void main() { ...@@ -706,6 +706,8 @@ void main() {
), ),
); );
const Offset deltaOffset = Offset(0.0, -18.0);
// 11:59 -> 12:59 // 11:59 -> 12:59
await tester.drag(find.text('11'), _kRowOffset); await tester.drag(find.text('11'), _kRowOffset);
await tester.pump(); await tester.pump();
...@@ -721,14 +723,14 @@ void main() { ...@@ -721,14 +723,14 @@ void main() {
expect(date, DateTime(2018, 1, 1, 11, 59)); expect(date, DateTime(2018, 1, 1, 11, 59));
// 11:59 -> 9: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();
await tester.pump(const Duration(milliseconds: 500)); await tester.pump(const Duration(milliseconds: 500));
expect(date, DateTime(2018, 1, 1, 9, 59)); expect(date, DateTime(2018, 1, 1, 9, 59));
// 9:59 -> 15: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();
await tester.pump(const Duration(milliseconds: 500)); await tester.pump(const Duration(milliseconds: 500));
......
...@@ -298,7 +298,7 @@ void main() { ...@@ -298,7 +298,7 @@ void main() {
); );
// Drag it by a bit but not enough to move to the next item. // 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. // The item that was in the center now moved a bit.
expect( expect(
...@@ -315,7 +315,7 @@ void main() { ...@@ -315,7 +315,7 @@ void main() {
expect(selectedItems.isEmpty, true); expect(selectedItems.isEmpty, true);
// Drag it by enough to move to the next item. // 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(); await tester.pumpAndSettle();
......
...@@ -117,7 +117,7 @@ void main() { ...@@ -117,7 +117,7 @@ void main() {
); );
// Drag down but not enough to trigger the refresh. // 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();
// The function is referenced once while passing into CupertinoSliverRefreshControl // The function is referenced once while passing into CupertinoSliverRefreshControl
...@@ -191,7 +191,7 @@ void main() { ...@@ -191,7 +191,7 @@ void main() {
); );
// Drag down but not enough to trigger the refresh. // 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();
await tester.pump(const Duration(milliseconds: 20)); await tester.pump(const Duration(milliseconds: 20));
await tester.pump(const Duration(milliseconds: 20)); await tester.pump(const Duration(milliseconds: 20));
...@@ -317,7 +317,7 @@ void main() { ...@@ -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(); await tester.pump();
// Let it start snapping back. // Let it start snapping back.
await tester.pump(const Duration(milliseconds: 50)); await tester.pump(const Duration(milliseconds: 50));
...@@ -394,7 +394,7 @@ void main() { ...@@ -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(); await tester.pump();
verify(mockHelper.builder( verify(mockHelper.builder(
...@@ -411,7 +411,7 @@ void main() { ...@@ -411,7 +411,7 @@ void main() {
Rect.fromLTRB(0.0, 0.0, 800.0, 150.0), 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(); await tester.pump();
// Refresh indicator still being told to layout the same way. // Refresh indicator still being told to layout the same way.
...@@ -474,7 +474,7 @@ void main() { ...@@ -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(); await tester.pump();
verify(mockHelper.builder( verify(mockHelper.builder(
any, any,
...@@ -549,7 +549,7 @@ void main() { ...@@ -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(); await tester.pump();
verify(mockHelper.builder( verify(mockHelper.builder(
any, any,
...@@ -639,7 +639,7 @@ void main() { ...@@ -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(); await tester.pump();
verify(mockHelper.refreshTask()); verify(mockHelper.refreshTask());
...@@ -670,7 +670,7 @@ void main() { ...@@ -670,7 +670,7 @@ void main() {
// Start another drag by an amount that would have been enough to // Start another drag by an amount that would have been enough to
// trigger another refresh if it were in the right state. // 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(); await tester.pump();
// Instead, it's still in the done state because the sliver never // Instead, it's still in the done state because the sliver never
...@@ -692,7 +692,7 @@ void main() { ...@@ -692,7 +692,7 @@ void main() {
); );
// Start another drag. It's now in drag mode. // 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(); await tester.pump();
verify(mockHelper.builder( verify(mockHelper.builder(
any, any,
...@@ -908,7 +908,7 @@ void main() { ...@@ -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(); await tester.pump();
verify(mockHelper.builder( verify(mockHelper.builder(
any, any,
...@@ -1101,7 +1101,7 @@ void main() { ...@@ -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(); await tester.pump();
expect( expect(
CupertinoSliverRefreshControl.state(tester.element(find.byType(LayoutBuilder))), CupertinoSliverRefreshControl.state(tester.element(find.byType(LayoutBuilder))),
......
...@@ -255,7 +255,6 @@ void main() { ...@@ -255,7 +255,6 @@ void main() {
return Center( return Center(
child: CupertinoSwitch( child: CupertinoSwitch(
value: value, value: value,
dragStartBehavior: DragStartBehavior.down,
onChanged: (bool newValue) { onChanged: (bool newValue) {
setState(() { setState(() {
value = newValue; value = newValue;
...@@ -270,21 +269,21 @@ void main() { ...@@ -270,21 +269,21 @@ void main() {
expect(value, isFalse); 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); 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); expect(value, isTrue);
await tester.pump(); 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); expect(value, isTrue);
await tester.pump(); 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); expect(value, isFalse);
}); });
......
...@@ -58,9 +58,6 @@ void main() { ...@@ -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 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 expect(didStartPan, isTrue); // 22 > 18
didStartPan = false; didStartPan = false;
// TODO(jslavitz): revert this testing change.
expect(updatedScrollDelta, const Offset(10.0, 20.0));
updatedScrollDelta = null;
expect(didEndPan, isFalse); expect(didEndPan, isFalse);
expect(didTap, isFalse); expect(didTap, isFalse);
...@@ -82,6 +79,58 @@ void main() { ...@@ -82,6 +79,58 @@ void main() {
tap.dispose(); 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) { testGesture('Should recognize drag', (GestureTester tester) {
final HorizontalDragGestureRecognizer drag = HorizontalDragGestureRecognizer() ..dragStartBehavior = DragStartBehavior.down; final HorizontalDragGestureRecognizer drag = HorizontalDragGestureRecognizer() ..dragStartBehavior = DragStartBehavior.down;
......
...@@ -102,7 +102,7 @@ void main() { ...@@ -102,7 +102,7 @@ void main() {
didStartPan = true; didStartPan = true;
}, },
onPanUpdate: (DragUpdateDetails details) { onPanUpdate: (DragUpdateDetails details) {
panDelta = details.delta; panDelta = panDelta == null ? details.delta : panDelta + details.delta;
}, },
onPanEnd: (DragEndDetails details) { onPanEnd: (DragEndDetails details) {
didEndPan = true; didEndPan = true;
......
...@@ -301,7 +301,7 @@ void main() { ...@@ -301,7 +301,7 @@ void main() {
await tester.drag(find.byType(ListView), const Offset(0.0, -250.0)); 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(); log.clear();
controller.dispose(); controller.dispose();
......
...@@ -65,7 +65,7 @@ void main() { ...@@ -65,7 +65,7 @@ void main() {
testWidgets('Holding scroll', (WidgetTester tester) async { testWidgets('Holding scroll', (WidgetTester tester) async {
await pumpTest(tester, TargetPlatform.iOS); 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); expect(getScrollOffset(tester), -200.0);
await tester.pump(); // trigger ballistic await tester.pump(); // trigger ballistic
await tester.pump(const Duration(milliseconds: 10)); await tester.pump(const Duration(milliseconds: 10));
......
...@@ -13,6 +13,12 @@ import 'finders.dart'; ...@@ -13,6 +13,12 @@ import 'finders.dart';
import 'test_async_utils.dart'; import 'test_async_utils.dart';
import 'test_pointer.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. /// Class that programmatically interacts with widgets.
/// ///
/// For a variant of this class suited specifically for unit tests, see /// For a variant of this class suited specifically for unit tests, see
...@@ -410,8 +416,27 @@ abstract class WidgetController { ...@@ -410,8 +416,27 @@ abstract class WidgetController {
/// ///
/// If you want the drag to end with a speed so that the gesture recognition /// 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. /// 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 /// Attempts a drag gesture consisting of a pointer down, a move by
...@@ -420,11 +445,78 @@ abstract class WidgetController { ...@@ -420,11 +445,78 @@ abstract class WidgetController {
/// If you want the drag to end with a speed so that the gesture recognition /// 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] /// system identifies the gesture as a fling, consider using [flingFrom]
/// instead. /// 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 { return TestAsyncUtils.guard<void>(() async {
final TestGesture gesture = await startGesture(startLocation, pointer: pointer); final TestGesture gesture = await startGesture(startLocation, pointer: pointer);
assert(gesture != null); 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(); 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