Unverified Commit ee0b67dd authored by Gabriel Gava's avatar Gabriel Gava Committed by GitHub

Exposed EdgeDraggingAutoScroller velocityScalar to ReorderableList (#124459)

Exposed EdgeDraggingAutoScroller velocityScalar to ReorderableList
parent 895879c1
......@@ -83,6 +83,7 @@ class ReorderableListView extends StatefulWidget {
this.keyboardDismissBehavior = ScrollViewKeyboardDismissBehavior.manual,
this.restorationId,
this.clipBehavior = Clip.hardEdge,
this.autoScrollerVelocityScalar,
}) : assert(
itemExtent == null || prototypeItem == null,
'You can only pass itemExtent or prototypeItem, not both',
......@@ -148,6 +149,7 @@ class ReorderableListView extends StatefulWidget {
this.keyboardDismissBehavior = ScrollViewKeyboardDismissBehavior.manual,
this.restorationId,
this.clipBehavior = Clip.hardEdge,
this.autoScrollerVelocityScalar,
}) : assert(itemCount >= 0),
assert(
itemExtent == null || prototypeItem == null,
......@@ -259,6 +261,9 @@ class ReorderableListView extends StatefulWidget {
/// {@macro flutter.widgets.list_view.prototypeItem}
final Widget? prototypeItem;
/// {@macro flutter.widgets.EdgeDraggingAutoScroller.velocityScalar}
final double? autoScrollerVelocityScalar;
@override
State<ReorderableListView> createState() => _ReorderableListViewState();
}
......@@ -428,6 +433,7 @@ class _ReorderableListViewState extends State<ReorderableListView> {
onReorderStart: widget.onReorderStart,
onReorderEnd: widget.onReorderEnd,
proxyDecorator: widget.proxyDecorator ?? _proxyDecorator,
autoScrollerVelocityScalar: widget.autoScrollerVelocityScalar,
),
),
if (widget.footer != null)
......
......@@ -133,6 +133,7 @@ class ReorderableList extends StatefulWidget {
this.keyboardDismissBehavior = ScrollViewKeyboardDismissBehavior.manual,
this.restorationId,
this.clipBehavior = Clip.hardEdge,
this.autoScrollerVelocityScalar,
}) : assert(itemCount >= 0),
assert(
itemExtent == null || prototypeItem == null,
......@@ -255,6 +256,9 @@ class ReorderableList extends StatefulWidget {
/// {@macro flutter.widgets.list_view.prototypeItem}
final Widget? prototypeItem;
/// {@macro flutter.widgets.EdgeDraggingAutoScroller.velocityScalar}
final double? autoScrollerVelocityScalar;
/// The state from the closest instance of this class that encloses the given
/// context.
///
......@@ -400,6 +404,7 @@ class ReorderableListState extends State<ReorderableList> {
onReorderStart: widget.onReorderStart,
onReorderEnd: widget.onReorderEnd,
proxyDecorator: widget.proxyDecorator,
autoScrollerVelocityScalar: widget.autoScrollerVelocityScalar,
),
),
],
......@@ -445,6 +450,7 @@ class SliverReorderableList extends StatefulWidget {
this.itemExtent,
this.prototypeItem,
this.proxyDecorator,
this.autoScrollerVelocityScalar,
}) : assert(itemCount >= 0),
assert(
itemExtent == null || prototypeItem == null,
......@@ -478,6 +484,9 @@ class SliverReorderableList extends StatefulWidget {
/// {@macro flutter.widgets.list_view.prototypeItem}
final Widget? prototypeItem;
/// {@macro flutter.widgets.EdgeDraggingAutoScroller.velocityScalar}
final double? autoScrollerVelocityScalar;
@override
SliverReorderableListState createState() => SliverReorderableListState();
......@@ -613,7 +622,8 @@ class SliverReorderableListState extends State<SliverReorderableList> with Ticke
_autoScroller?.stopAutoScroll();
_autoScroller = EdgeDraggingAutoScroller(
_scrollable,
onScrollViewScrolled: _handleScrollableAutoScrolled
onScrollViewScrolled: _handleScrollableAutoScrolled,
velocityScalar: widget.autoScrollerVelocityScalar,
);
}
}
......@@ -624,6 +634,15 @@ class SliverReorderableListState extends State<SliverReorderableList> with Ticke
if (widget.itemCount != oldWidget.itemCount) {
cancelReorder();
}
if (widget.autoScrollerVelocityScalar != oldWidget.autoScrollerVelocityScalar) {
_autoScroller?.stopAutoScroll();
_autoScroller = EdgeDraggingAutoScroller(
_scrollable,
onScrollViewScrolled: _handleScrollableAutoScrolled,
velocityScalar: widget.autoScrollerVelocityScalar,
);
}
}
@override
......
......@@ -160,8 +160,8 @@ class EdgeDraggingAutoScroller {
EdgeDraggingAutoScroller(
this.scrollable, {
this.onScrollViewScrolled,
this.velocityScalar = _kDefaultAutoScrollVelocityScalar,
});
double? velocityScalar,
}): velocityScalar = velocityScalar ?? _kDefaultAutoScrollVelocityScalar;
// An eyeballed value for a smooth scrolling experience.
static const double _kDefaultAutoScrollVelocityScalar = 7;
......@@ -176,10 +176,14 @@ class EdgeDraggingAutoScroller {
/// in between each scroll.
final VoidCallback? onScrollViewScrolled;
/// {@template flutter.widgets.EdgeDraggingAutoScroller.velocityScalar}
/// The velocity scalar per pixel over scroll.
///
/// It represents how the velocity scale with the over scroll distance. The
/// auto-scroll velocity = <distance of overscroll> * velocityScalar.
///
/// Defaults to 7 if not set or set to null.
/// {@endtemplate}
final double velocityScalar;
late Rect _dragTargetRelatedToScrollOrigin;
......
......@@ -1779,6 +1779,88 @@ void main() {
expect(item1Height, 30.0);
expect(item2Height, 30.0);
});
testWidgets('ReorderableListView auto scrolls speed is configurable', (WidgetTester tester) async {
Future<void> pumpFor({
required Duration duration,
Duration interval = const Duration(milliseconds: 50),
}) async {
await tester.pump();
int times = (duration.inMilliseconds / interval.inMilliseconds).ceil();
while (times > 0) {
await tester.pump(interval + const Duration(milliseconds: 1));
await tester.idle();
times--;
}
}
Future<double> pumpListAndDrag({required double autoScrollerVelocityScalar}) async {
final List<int> items = List<int>.generate(10, (int index) => index);
final ScrollController scrollController = ScrollController();
await tester.pumpWidget(
MaterialApp(
home: ReorderableListView.builder(
itemBuilder: (BuildContext context, int index) {
return Container(
key: ValueKey<int>(items[index]),
height: 100,
color: items[index].isOdd ? Colors.red : Colors.green,
child: ReorderableDragStartListener(
index: index,
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text('item ${items[index]}'),
const Icon(Icons.drag_handle),
],
),
),
);
},
itemCount: items.length,
onReorder: (int fromIndex, int toIndex) {},
scrollController: scrollController,
autoScrollerVelocityScalar: autoScrollerVelocityScalar,
),
),
);
expect(scrollController.offset, 0);
final Finder item = find.text('item 0');
final TestGesture drag = await tester.startGesture(tester.getCenter(item));
// Drag just enough to touch the edge but not surpass it, so the
// auto scroller is not yet triggered
await drag.moveBy(const Offset(0, 500));
await pumpFor(duration: const Duration(milliseconds: 200));
expect(scrollController.offset, 0);
// Now drag a little bit more so the auto scroller triggers
await drag.moveBy(const Offset(0, 50));
await pumpFor(
duration: const Duration(milliseconds: 600),
interval: Duration(milliseconds: (1000 / autoScrollerVelocityScalar).round()),
);
return scrollController.offset;
}
const double fastVelocityScalar = 20;
final double offsetForFastScroller = await pumpListAndDrag(autoScrollerVelocityScalar: fastVelocityScalar);
// Reset widget tree
await tester.pumpWidget(const SizedBox());
const double slowVelocityScalar = 5;
final double offsetForSlowScroller = await pumpListAndDrag(autoScrollerVelocityScalar: slowVelocityScalar);
expect(offsetForFastScroller / offsetForSlowScroller, fastVelocityScalar / slowVelocityScalar);
});
}
Future<void> longPressDrag(WidgetTester tester, Offset start, Offset end) async {
......
......@@ -1223,6 +1223,91 @@ void main() {
expect(item0, findsNothing);
});
testWidgets('SliverReorderableList auto scrolls speed is configurable', (WidgetTester tester) async {
Future<void> pumpFor({
required Duration duration,
Duration interval = const Duration(milliseconds: 50),
}) async {
await tester.pump();
int times = (duration.inMilliseconds / interval.inMilliseconds).ceil();
while (times > 0) {
await tester.pump(interval + const Duration(milliseconds: 1));
await tester.idle();
times--;
}
}
Future<double> pumpListAndDrag({required double autoScrollerVelocityScalar}) async {
final List<int> items = List<int>.generate(10, (int index) => index);
final ScrollController scrollController = ScrollController();
await tester.pumpWidget(
MaterialApp(
home: CustomScrollView(
controller: scrollController,
slivers: <Widget>[
SliverReorderableList(
itemBuilder: (BuildContext context, int index) {
return Container(
key: ValueKey<int>(items[index]),
height: 100,
color: items[index].isOdd ? Colors.red : Colors.green,
child: ReorderableDragStartListener(
index: index,
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text('item ${items[index]}'),
const Icon(Icons.drag_handle),
],
),
),
);
},
itemCount: items.length,
onReorder: (int fromIndex, int toIndex) {},
autoScrollerVelocityScalar: autoScrollerVelocityScalar,
),
],
),
),
);
expect(scrollController.offset, 0);
final Finder item = find.text('item 0');
final TestGesture drag = await tester.startGesture(tester.getCenter(item));
// Drag just enough to touch the edge but not surpass it, so the
// auto scroller is not yet triggered
await drag.moveBy(const Offset(0, 500));
await pumpFor(duration: const Duration(milliseconds: 200));
expect(scrollController.offset, 0);
// Now drag a little bit more so the auto scroller triggers
await drag.moveBy(const Offset(0, 50));
await pumpFor(
duration: const Duration(milliseconds: 600),
interval: Duration(milliseconds: (1000 / autoScrollerVelocityScalar).round()),
);
return scrollController.offset;
}
const double fastVelocityScalar = 20;
final double offsetForFastScroller = await pumpListAndDrag(autoScrollerVelocityScalar: fastVelocityScalar);
// Reset widget tree
await tester.pumpWidget(const SizedBox());
const double slowVelocityScalar = 5;
final double offsetForSlowScroller = await pumpListAndDrag(autoScrollerVelocityScalar: slowVelocityScalar);
expect(offsetForFastScroller / offsetForSlowScroller, fastVelocityScalar / slowVelocityScalar);
});
}
class TestList extends StatelessWidget {
......@@ -1235,6 +1320,7 @@ class TestList extends StatelessWidget {
this.reverse = false,
this.onReorderStart,
this.onReorderEnd,
this.autoScrollerVelocityScalar,
});
final List<int> items;
......@@ -1243,6 +1329,7 @@ class TestList extends StatelessWidget {
final ReorderItemProxyDecorator? proxyDecorator;
final bool reverse;
final void Function(int)? onReorderStart, onReorderEnd;
final double? autoScrollerVelocityScalar;
@override
Widget build(BuildContext context) {
......@@ -1288,6 +1375,7 @@ class TestList extends StatelessWidget {
proxyDecorator: proxyDecorator,
onReorderStart: onReorderStart,
onReorderEnd: onReorderEnd,
autoScrollerVelocityScalar: autoScrollerVelocityScalar,
),
],
);
......
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