Unverified Commit 922bbfee authored by Viren Khatri's avatar Viren Khatri Committed by GitHub

Adds `onReorderStart` and `onReorderEnd` arguments to `ReorderableList`. (#96049)

parent 3e4a7c0d
......@@ -64,6 +64,8 @@ class ReorderableListView extends StatefulWidget {
Key? key,
required List<Widget> children,
required this.onReorder,
this.onReorderStart,
this.onReorderEnd,
this.itemExtent,
this.prototypeItem,
this.proxyDecorator,
......@@ -131,6 +133,8 @@ class ReorderableListView extends StatefulWidget {
required this.itemBuilder,
required this.itemCount,
required this.onReorder,
this.onReorderStart,
this.onReorderEnd,
this.itemExtent,
this.prototypeItem,
this.proxyDecorator,
......@@ -168,6 +172,12 @@ class ReorderableListView extends StatefulWidget {
/// {@macro flutter.widgets.reorderable_list.onReorder}
final ReorderCallback onReorder;
/// {@macro flutter.widgets.reorderable_list.onReorderStart}
final void Function(int index)? onReorderStart;
/// {@macro flutter.widgets.reorderable_list.onReorderEnd}
final void Function(int index)? onReorderEnd;
/// {@macro flutter.widgets.reorderable_list.proxyDecorator}
final ReorderItemProxyDecorator? proxyDecorator;
......@@ -479,6 +489,8 @@ class _ReorderableListViewState extends State<ReorderableListView> {
prototypeItem: widget.prototypeItem,
itemCount: widget.itemCount,
onReorder: widget.onReorder,
onReorderStart: widget.onReorderStart,
onReorderEnd: widget.onReorderEnd,
proxyDecorator: widget.proxyDecorator ?? _proxyDecorator,
),
),
......
......@@ -114,6 +114,8 @@ class ReorderableList extends StatefulWidget {
required this.itemBuilder,
required this.itemCount,
required this.onReorder,
this.onReorderStart,
this.onReorderEnd,
this.itemExtent,
this.prototypeItem,
this.proxyDecorator,
......@@ -166,6 +168,34 @@ class ReorderableList extends StatefulWidget {
/// {@endtemplate}
final ReorderCallback onReorder;
/// {@template flutter.widgets.reorderable_list.onReorderStart}
/// A callback that is called when an item drag has started.
///
/// The index parameter of the callback is the index of the selected item.
///
/// See also:
///
/// * [onReorderEnd], which is a called when the dragged item is dropped.
/// * [onReorder], which reports that a list item has been dragged to a new
/// location.
/// {@endtemplate}
final void Function(int index)? onReorderStart;
/// {@template flutter.widgets.reorderable_list.onReorderEnd}
/// A callback that is called when the dragged item is dropped.
///
/// The index parameter of the callback is the index where the item is
/// dropped. Unlike [onReorder], this is called even when the list item is
/// dropped in the same location.
///
/// See also:
///
/// * [onReorderStart], which is a called when an item drag has started.
/// * [onReorder], which reports that a list item has been dragged to a new
/// location.
/// {@endtemplate}
final void Function(int index)? onReorderEnd;
/// {@template flutter.widgets.reorderable_list.proxyDecorator}
/// A callback that allows the app to add an animated decoration around
/// an item when it is being dragged.
......@@ -360,6 +390,8 @@ class ReorderableListState extends State<ReorderableList> {
itemBuilder: widget.itemBuilder,
itemCount: widget.itemCount,
onReorder: widget.onReorder,
onReorderStart: widget.onReorderStart,
onReorderEnd: widget.onReorderEnd,
proxyDecorator: widget.proxyDecorator,
),
),
......@@ -400,6 +432,8 @@ class SliverReorderableList extends StatefulWidget {
required this.itemBuilder,
required this.itemCount,
required this.onReorder,
this.onReorderStart,
this.onReorderEnd,
this.itemExtent,
this.prototypeItem,
this.proxyDecorator,
......@@ -419,6 +453,12 @@ class SliverReorderableList extends StatefulWidget {
/// {@macro flutter.widgets.reorderable_list.onReorder}
final ReorderCallback onReorder;
/// {@macro flutter.widgets.reorderable_list.onReorderStart}
final void Function(int)? onReorderStart;
/// {@macro flutter.widgets.reorderable_list.onReorderEnd}
final void Function(int)? onReorderEnd;
/// {@macro flutter.widgets.reorderable_list.proxyDecorator}
final ReorderItemProxyDecorator? proxyDecorator;
......@@ -640,6 +680,7 @@ class SliverReorderableListState extends State<SliverReorderableList> with Ticke
assert(_dragInfo == null);
final _ReorderableItemState item = _items[_dragIndex!]!;
item.dragging = true;
widget.onReorderStart?.call(_dragIndex!);
item.rebuild();
_dragStartTransitionComplete = false;
SchedulerBinding.instance.addPostFrameCallback((Duration duration) {
......@@ -702,6 +743,7 @@ class SliverReorderableListState extends State<SliverReorderableList> with Ticke
}
}
});
widget.onReorderEnd?.call(_insertIndex!);
}
void _dropCompleted() {
......
......@@ -1470,6 +1470,82 @@ void main() {
expect(items.take(8), orderedEquals(<int>[0, 1, 2, 3, 4, 5, 6, 7]));
});
testWidgets('ReorderableListView calls onReorderStart and onReorderEnd correctly', (WidgetTester tester) async {
final List<int> items = List<int>.generate(8, (int index) => index);
int? startIndex, endIndex;
final Finder item0 = find.textContaining('item 0');
void handleReorder(int fromIndex, int toIndex) {
if (toIndex > fromIndex) {
toIndex -= 1;
}
items.insert(toIndex, items.removeAt(fromIndex));
}
await tester.pumpWidget(MaterialApp(
home: ReorderableListView.builder(
itemBuilder: (BuildContext context, int index) {
return SizedBox(
key: ValueKey<int>(items[index]),
height: 100,
child: ReorderableDragStartListener(
index: index,
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text('item ${items[index]}'),
],
),
),
);
},
itemCount: items.length,
onReorder: handleReorder,
onReorderStart: (int index) {
startIndex = index;
},
onReorderEnd: (int index) {
endIndex = index;
},
),
));
TestGesture drag = await tester.startGesture(tester.getCenter(item0));
await tester.pump(kPressTimeout);
// Drag enough for move to start.
await drag.moveBy(const Offset(0, 20));
expect(startIndex, equals(0));
expect(endIndex, isNull);
// Move item0 from index 0 to index 3
await drag.moveBy(const Offset(0, 300));
await tester.pumpAndSettle();
await drag.up();
await tester.pumpAndSettle();
expect(endIndex, equals(3));
startIndex = null;
endIndex = null;
drag = await tester.startGesture(tester.getCenter(item0));
await tester.pump(kPressTimeout);
// Drag enough for move to start.
await drag.moveBy(const Offset(0, 20));
expect(startIndex, equals(2));
expect(endIndex, isNull);
// Move item0 from index 2 to index 0
await drag.moveBy(const Offset(0, -200));
await tester.pumpAndSettle();
await drag.up();
await tester.pumpAndSettle();
expect(endIndex, equals(0));
});
testWidgets('ReorderableListView throws an error when key is not passed to its children', (WidgetTester tester) async {
final Widget reorderableListView = ReorderableListView.builder(
itemBuilder: (BuildContext context, int index) {
......
......@@ -467,6 +467,130 @@ void main() {
expect(tester.getTopLeft(find.text('item 0')), const Offset(0, 500));
});
testWidgets('SliverReorderableList calls onReorderStart and onReorderEnd correctly', (WidgetTester tester) async {
final List<int> items = List<int>.generate(8, (int index) => index);
int? startIndex, endIndex;
final Finder item0 = find.textContaining('item 0');
await tester.pumpWidget(TestList(
items: items,
onReorderStart: (int index) {
startIndex = index;
},
onReorderEnd: (int index) {
endIndex = index;
},
));
TestGesture drag = await tester.startGesture(tester.getCenter(item0));
await tester.pump(kPressTimeout);
// Drag enough for move to start.
await drag.moveBy(const Offset(0, 20));
expect(startIndex, equals(0));
expect(endIndex, isNull);
// Move item0 from index 0 to index 3
await drag.moveBy(const Offset(0, 300));
await tester.pumpAndSettle();
await drag.up();
await tester.pumpAndSettle();
expect(endIndex, equals(3));
startIndex = null;
endIndex = null;
drag = await tester.startGesture(tester.getCenter(item0));
await tester.pump(kPressTimeout);
// Drag enough for move to start.
await drag.moveBy(const Offset(0, 20));
expect(startIndex, equals(2));
expect(endIndex, isNull);
// Move item0 from index 2 to index 0
await drag.moveBy(const Offset(0, -200));
await tester.pumpAndSettle();
await drag.up();
await tester.pumpAndSettle();
expect(endIndex, equals(0));
});
testWidgets('ReorderableList calls onReorderStart and onReorderEnd correctly', (WidgetTester tester) async {
final List<int> items = List<int>.generate(8, (int index) => index);
int? startIndex, endIndex;
final Finder item0 = find.textContaining('item 0');
void handleReorder(int fromIndex, int toIndex) {
if (toIndex > fromIndex) {
toIndex -= 1;
}
items.insert(toIndex, items.removeAt(fromIndex));
}
await tester.pumpWidget(MaterialApp(
home: ReorderableList(
itemCount: items.length,
itemBuilder: (BuildContext context, int index) {
return SizedBox(
key: ValueKey<int>(items[index]),
height: 100,
child: ReorderableDelayedDragStartListener(
index: index,
child: Text('item ${items[index]}'),
),
);
},
onReorder: handleReorder,
onReorderStart: (int index) {
startIndex = index;
},
onReorderEnd: (int index) {
endIndex = index;
},
),
));
TestGesture drag = await tester.startGesture(tester.getCenter(item0));
await tester.pump(kLongPressTimeout);
// Drag enough for move to start.
await drag.moveBy(const Offset(0, 20));
expect(startIndex, equals(0));
expect(endIndex, isNull);
// Move item0 from index 0 to index 3
await drag.moveBy(const Offset(0, 300));
await tester.pumpAndSettle();
await drag.up();
await tester.pumpAndSettle();
expect(endIndex, equals(3));
startIndex = null;
endIndex = null;
drag = await tester.startGesture(tester.getCenter(item0));
await tester.pump(kLongPressTimeout);
// Drag enough for move to start.
await drag.moveBy(const Offset(0, 20));
expect(startIndex, equals(2));
expect(endIndex, isNull);
// Move item0 from index 2 to index 0
await drag.moveBy(const Offset(0, -200));
await tester.pumpAndSettle();
await drag.up();
await tester.pumpAndSettle();
expect(endIndex, equals(0));
});
testWidgets('ReorderableList asserts on both non-null itemExtent and prototypeItem', (WidgetTester tester) async {
final List<int> numbers = <int>[0,1,2];
expect(() => ReorderableList(
......@@ -794,6 +918,8 @@ class TestList extends StatefulWidget {
this.proxyDecorator,
required this.items,
this.reverse = false,
this.onReorderStart,
this.onReorderEnd,
}) : super(key: key);
final List<int> items;
......@@ -801,6 +927,7 @@ class TestList extends StatefulWidget {
final Color? iconColor;
final ReorderItemProxyDecorator? proxyDecorator;
final bool reverse;
final void Function(int)? onReorderStart, onReorderEnd;
@override
State<TestList> createState() => _TestListState();
......@@ -849,6 +976,8 @@ class _TestListState extends State<TestList> {
});
},
proxyDecorator: widget.proxyDecorator,
onReorderStart: widget.onReorderStart,
onReorderEnd: widget.onReorderEnd,
),
],
);
......
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