Adds onReorderStart
and onReorderEnd
arguments to ReorderableList
. (#96049)
This commit is contained in:
parent
3e4a7c0d0d
commit
922bbfeed7
@ -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,
|
||||
),
|
||||
],
|
||||
);
|
||||
|
Loading…
x
Reference in New Issue
Block a user