Fix `ReorderableList` items jumping when drag direction reverses mid-animation (#173241)

When rapidly dragging items in a ReorderableList before animations
complete, items would jump to their expected positions rather than
smoothly transitioning.

## Problem

<p align="center">
<img
src="https://github.com/user-attachments/assets/0efb250c-2960-4942-959f-59eccc20cefb"
alt="demo2" width="250">
</p>

The issue occurs when a reorder animation is interrupted by starting a
new drag operation. The interrupted animation would reset to the
starting position of the original animation rather than capturing the
current animated position, causing a visual jump.

## Solution

This PR fixes the issue by:
1. Storing the previous target offset before updating to a new target
2. When an animation is interrupted, calculating the actual current
position based on the animation's progress
3. Using this calculated position as the new starting point for the next
animation

<p align="center">
<img
src="https://github.com/user-attachments/assets/c24e4834-1c6b-41c1-8f44-17b4c79e0993"
alt="demo2" width="250">
</p>

This PR is part of a series of `ReorderableList` enhancements I've
developed for [TimeFinder](https://timefinder.app):
- #172740
- #172380
- #172739
- #172738

Fixes #173243

## Code Changes

```dart
// Before
_startOffset = offset;

// After  
final double currentAnimValue = Curves.easeInOut.transform(_offsetAnimation\!.value);
final Offset currentPosition = Offset.lerp(_startOffset, previousTarget, currentAnimValue)\!;
_startOffset = currentPosition;
```

This ensures smooth transitions without visual jumping, making rapid
reordering gestures feel more responsive and natural.

## Pre-launch Checklist

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [Tree Hygiene] page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [x] I listed at least one issue that this PR fixes in the description
above.
- [x] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [x] I followed the [breaking change policy] and added [migration
guide] as needed.
- [x] All existing and new tests are passing.
- [x] The analyzer (`flutter analyze --flutter-repo`) does not report
any problems on my PR.
- [x] I am willing to follow-up on review comments in a timely manner.

[Contributor Guide]:
https://github.com/flutter/flutter/blob/master/docs/contributing/Tree-hygiene.md#overview
[Tree Hygiene]:
https://github.com/flutter/flutter/blob/master/docs/contributing/Tree-hygiene.md
[Flutter Style Guide]:
https://github.com/flutter/flutter/blob/master/docs/contributing/Style-guide-for-Flutter-repo.md
[Features we expect every widget to implement]:
https://github.com/flutter/flutter/blob/master/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement
[test-exempt]:
https://github.com/flutter/flutter/blob/master/docs/contributing/Tree-hygiene.md#tests
[breaking change policy]:
https://github.com/flutter/flutter/blob/master/docs/contributing/Tree-hygiene.md#handling-breaking-changes
[migration guide]:
https://github.com/flutter/flutter/blob/master/docs/contributing/Tree-hygiene.md#create-a-migration-guide
diff --git a/packages/flutter/lib/src/widgets/reorderable_list.dart b/packages/flutter/lib/src/widgets/reorderable_list.dart
index d1f5e58..6a78dd8 100644
--- a/packages/flutter/lib/src/widgets/reorderable_list.dart
+++ b/packages/flutter/lib/src/widgets/reorderable_list.dart
@@ -1233,6 +1233,7 @@
       newTargetOffset = Offset.zero;
     }
     if (newTargetOffset != _targetOffset) {
+      final Offset previousTarget = _targetOffset;
       _targetOffset = newTargetOffset;
       if (animate) {
         if (_offsetAnimation == null) {
@@ -1248,7 +1249,14 @@
                 })
                 ..forward();
         } else {
-          _startOffset = offset;
+          // Animation interrupted - calculate current position from previous animation
+          final double currentAnimValue = Curves.easeInOut.transform(_offsetAnimation!.value);
+          final Offset currentPosition = Offset.lerp(
+            _startOffset,
+            previousTarget,
+            currentAnimValue,
+          )!;
+          _startOffset = currentPosition;
           _offsetAnimation!.forward(from: 0.0);
         }
       } else {
diff --git a/packages/flutter/test/widgets/reorderable_list_test.dart b/packages/flutter/test/widgets/reorderable_list_test.dart
index 0b93550..62cdf4e 100644
--- a/packages/flutter/test/widgets/reorderable_list_test.dart
+++ b/packages/flutter/test/widgets/reorderable_list_test.dart
@@ -870,6 +870,51 @@
     },
   );
 
+  testWidgets('ReorderableList animation jumping on interruption', (WidgetTester tester) async {
+    // Regression test for https://github.com/flutter/flutter/issues/173243
+    final List<int> items = List<int>.generate(3, (int index) => index);
+
+    await tester.pumpWidget(TestList(items: items));
+    await tester.pumpAndSettle();
+
+    expect(tester.getTopLeft(find.text('item 0')), Offset.zero);
+    expect(tester.getTopLeft(find.text('item 1')), const Offset(0, 100));
+
+    final TestGesture drag = await tester.startGesture(tester.getCenter(find.text('item 0')));
+    await tester.pump(kPressTimeout);
+
+    // Drag item 0 down past the swap threshold at 50px.
+    for (int i = 0; i < 6; i++) {
+      await drag.moveBy(const Offset(0, 10));
+      await tester.pump(const Duration(milliseconds: 50));
+    }
+
+    final double item1YBeforeInterrupt = tester.getCenter(find.text('item 1')).dy;
+
+    // Drag back to trigger swap reversal.
+    await drag.moveBy(const Offset(0, -10));
+    await tester.pump(const Duration(milliseconds: 50));
+
+    final double item1YAfterInterrupt = tester.getCenter(find.text('item 1')).dy;
+
+    // Position should not jump when animation is interrupted.
+    expect(
+      item1YAfterInterrupt,
+      closeTo(item1YBeforeInterrupt, 5.0),
+      reason:
+          'Animation jumping detected! Position changed from '
+          '$item1YBeforeInterrupt to $item1YAfterInterrupt',
+    );
+
+    for (int i = 0; i < 5; i++) {
+      await drag.moveBy(const Offset(0, -10));
+      await tester.pump(const Duration(milliseconds: 50));
+    }
+
+    await drag.up();
+    await tester.pumpAndSettle();
+  });
+
   testWidgets('SliverReorderableList calls onReorderStart and onReorderEnd correctly', (
     WidgetTester tester,
   ) async {