diff --git a/packages/flutter/lib/src/widgets/reorderable_list.dart b/packages/flutter/lib/src/widgets/reorderable_list.dart index 9158013aa84..60ff7f3dc90 100644 --- a/packages/flutter/lib/src/widgets/reorderable_list.dart +++ b/packages/flutter/lib/src/widgets/reorderable_list.dart @@ -758,9 +758,11 @@ class SliverReorderableListState extends State with Ticke void _dragEnd(_DragInfo item) { setState(() { - if (_insertIndex! < widget.itemCount - 1) { - // Find the location of the item we want to insert before + if (_insertIndex == item.index) { _finalDropPosition = _itemOffsetAt(_insertIndex! + (_reverse ? 1 : 0)); + } else if (_insertIndex! < widget.itemCount - 1) { + // Find the location of the item we want to insert before + _finalDropPosition = _itemOffsetAt(_insertIndex!); } else { // Inserting into the last spot on the list. If it's the only spot, put // it back where it was. Otherwise, grab the second to last and move diff --git a/packages/flutter/test/widgets/reorderable_list_test.dart b/packages/flutter/test/widgets/reorderable_list_test.dart index bb5f0b2a876..afce2894c5b 100644 --- a/packages/flutter/test/widgets/reorderable_list_test.dart +++ b/packages/flutter/test/widgets/reorderable_list_test.dart @@ -521,6 +521,57 @@ void main() { expect(tester.takeException(), isNull); }); + testWidgets('SliverReorderableList - properly animates the drop in a reversed list', (WidgetTester tester) async { + // Regression test for https://github.com/flutter/flutter/issues/110949 + final List items = List.generate(8, (int index) => index); + + Future pressDragRelease(Offset start, Offset delta) async { + final TestGesture drag = await tester.startGesture(start); + await tester.pump(kPressTimeout); + await drag.moveBy(delta); + await tester.pumpAndSettle(); + await drag.up(); + await tester.pump(); + } + + // The TestList is 800x600 SliverReorderableList with 8 items 800x100 each. + // Each item has a text widget with 'item $index' that can be moved by a + // press and drag gesture. For this test we are reversing the order so + // the first item is at the bottom. + await tester.pumpWidget(TestList(items: items, reverse: true)); + + expect(tester.getTopLeft(find.text('item 0')), const Offset(0, 500)); + expect(tester.getTopLeft(find.text('item 2')), const Offset(0, 300)); + + // Drag item 0 up and insert it between item 1 and item 2. It should + // smoothly animate. + await pressDragRelease(tester.getCenter(find.text('item 0')), const Offset(0, -50)); + expect(tester.getTopLeft(find.text('item 0')), const Offset(0, 450)); + expect(tester.getTopLeft(find.text('item 1')), const Offset(0, 500)); + expect(tester.getTopLeft(find.text('item 2')), const Offset(0, 300)); + + // After the first several frames we should be moving closer to the final position, + // not further away as was the case with the original bug. + await tester.pump(const Duration(milliseconds: 10)); + expect(tester.getTopLeft(find.text('item 0')).dy, lessThan(450)); + expect(tester.getTopLeft(find.text('item 0')).dy, greaterThan(400)); + + // Sample the middle (don't use exact values as it depends on the internal + // curve being used). + await tester.pump(const Duration(milliseconds: 125)); + expect(tester.getTopLeft(find.text('item 0')).dy, lessThan(450)); + expect(tester.getTopLeft(find.text('item 0')).dy, greaterThan(400)); + + // Sample the end of the animation. + await tester.pump(const Duration(milliseconds: 100)); + expect(tester.getTopLeft(find.text('item 0')).dy, lessThan(450)); + expect(tester.getTopLeft(find.text('item 0')).dy, greaterThan(400)); + + // Wait for it to finish, it should be back to the original position + await tester.pumpAndSettle(); + expect(tester.getTopLeft(find.text('item 0')), const Offset(0, 400)); + }); + testWidgets('SliverReorderableList - properly animates the drop at starting position in a reversed list', (WidgetTester tester) async { // Regression test for https://github.com/flutter/flutter/issues/84625 final List items = List.generate(8, (int index) => index);