Skip to content

Commit bbc17fa

Browse files
authored
Fix: Flicker when reorderable list doesn't change its position (#151026)
Fix: Flicker when no update in index of dragged item Resolves #150843 This PR ensures that even if we move dragged item anywhere in list and comeback to initial position, we smoothly adjust to that position.
1 parent 2812d46 commit bbc17fa

File tree

2 files changed

+70
-2
lines changed

2 files changed

+70
-2
lines changed

packages/flutter/lib/src/widgets/reorderable_list.dart

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -871,7 +871,7 @@ class SliverReorderableListState extends State<SliverReorderableList> with Ticke
871871
// Find the new index for inserting the item being dragged.
872872
int newIndex = _insertIndex!;
873873
for (final _ReorderableItemState item in _items.values) {
874-
if (item.index == _dragIndex! || !item.mounted) {
874+
if ((_reverse && item.index == _dragIndex!) || !item.mounted) {
875875
continue;
876876
}
877877

@@ -902,7 +902,13 @@ class SliverReorderableListState extends State<SliverReorderableList> with Ticke
902902
newIndex = item.index;
903903
}
904904
} else {
905-
if (itemStart <= proxyItemStart && proxyItemStart <= itemMiddle) {
905+
if (item.index == _dragIndex!) {
906+
// If end of the proxy is not in ending half of item,
907+
// we don't process, because it's original dragged item.
908+
if (itemMiddle <= proxyItemEnd && proxyItemEnd <= itemEnd) {
909+
newIndex = _dragIndex!;
910+
}
911+
} else if (itemStart <= proxyItemStart && proxyItemStart <= itemMiddle) {
906912
// The start of the proxy is in the beginning half of the item, so
907913
// we should swap the item with the gap and we are done looking for
908914
// the new index.

packages/flutter/test/widgets/reorderable_list_test.dart

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -717,6 +717,68 @@ void main() {
717717
expect(tester.getTopLeft(find.text('item 0')), const Offset(0, 500));
718718
});
719719

720+
testWidgets('SliverReorderableList - properly animates the drop at starting position', (WidgetTester tester) async {
721+
// Regression test for https://github.com/flutter/flutter/issues/150843
722+
final List<int> items = List<int>.generate(3, (int index) => index);
723+
724+
// The TestList is 300x100 SliverReorderableList with 3 items 100x100 each.
725+
// Each item has a text widget with 'item $index' that can be moved by a
726+
// press and drag gesture. For this test the first item is at the top
727+
await tester.pumpWidget(TestList(items: items));
728+
729+
expect(tester.getTopLeft(find.text('item 0')), Offset.zero);
730+
expect(tester.getTopLeft(find.text('item 1')), const Offset(0, 100));
731+
732+
// Drag item 0 downwards and then upwards.
733+
final TestGesture drag = await tester.startGesture(tester.getCenter(find.text('item 0')));
734+
await tester.pump(kPressTimeout);
735+
await drag.moveBy(const Offset(0, 100));
736+
await tester.pumpAndSettle();
737+
await drag.moveBy(const Offset(0, -110));
738+
await tester.pump();
739+
expect(tester.getTopLeft(find.text('item 0')), const Offset(0, -10));
740+
expect(tester.getTopLeft(find.text('item 1')), const Offset(0, 100));
741+
742+
// Now leave the drag, it should go to index 0.
743+
await drag.up();
744+
await tester.pump();
745+
746+
// It should not go to index 1 and come back
747+
await tester.pump(const Duration(milliseconds: 200));
748+
expect(tester.getTopLeft(find.text('item 0')).dy, lessThan(50));
749+
});
750+
751+
testWidgets('SliverReorderableList - properly animates the drop at starting position with reverse:true', (WidgetTester tester) async {
752+
// Regression test for https://github.com/flutter/flutter/issues/150843
753+
final List<int> items = List<int>.generate(3, (int index) => index);
754+
755+
// The TestList is 300x100 SliverReorderableList with 3 items 100x100 each.
756+
// Each item has a text widget with 'item $index' that can be moved by a
757+
// press and drag gesture. For this test the first item is at the top
758+
await tester.pumpWidget(TestList(items: items,reverse: true));
759+
760+
expect(tester.getTopLeft(find.text('item 2')), const Offset(0, 300.0));
761+
expect(tester.getTopLeft(find.text('item 1')), const Offset(0, 400.0));
762+
763+
// Drag item 2 downwards and then upwards.
764+
final TestGesture drag = await tester.startGesture(tester.getCenter(find.text('item 2')));
765+
await tester.pump(kPressTimeout);
766+
await drag.moveBy(const Offset(0, 100));
767+
await tester.pumpAndSettle();
768+
await drag.moveBy(const Offset(0, -110));
769+
await tester.pump();
770+
expect(tester.getTopLeft(find.text('item 2')), const Offset(0, 290));
771+
expect(tester.getTopLeft(find.text('item 1')), const Offset(0, 300));
772+
773+
// Now leave the drag, it should go to index 1.
774+
await drag.up();
775+
await tester.pump();
776+
777+
// It should not go to index 0 and come back
778+
await tester.pump(const Duration(milliseconds: 200));
779+
expect(tester.getTopLeft(find.text('item 2')).dy, greaterThan(350));
780+
});
781+
720782
testWidgets('SliverReorderableList calls onReorderStart and onReorderEnd correctly', (WidgetTester tester) async {
721783
final List<int> items = List<int>.generate(8, (int index) => index);
722784
int? startIndex, endIndex;

0 commit comments

Comments
 (0)