Update StretchingOverscrollIndicator for reversed scrollables (#89242)

* Update overscroll indicator for reverse

* Review feedback

* Review feedback
diff --git a/packages/flutter/lib/src/widgets/overscroll_indicator.dart b/packages/flutter/lib/src/widgets/overscroll_indicator.dart
index 3796963..2f893681 100644
--- a/packages/flutter/lib/src/widgets/overscroll_indicator.dart
+++ b/packages/flutter/lib/src/widgets/overscroll_indicator.dart
@@ -708,6 +708,28 @@
     return false;
   }
 
+  AlignmentDirectional _getAlignmentForAxisDirection(double overscroll) {
+    // Accounts for reversed scrollables by checking the AxisDirection
+    switch (widget.axisDirection) {
+      case AxisDirection.up:
+        return overscroll > 0
+            ? AlignmentDirectional.topCenter
+            : AlignmentDirectional.bottomCenter;
+      case AxisDirection.right:
+        return overscroll > 0
+            ? AlignmentDirectional.centerEnd
+            : AlignmentDirectional.centerStart;
+      case AxisDirection.down:
+        return overscroll > 0
+            ? AlignmentDirectional.bottomCenter
+            : AlignmentDirectional.topCenter;
+      case AxisDirection.left:
+        return overscroll > 0
+            ? AlignmentDirectional.centerStart
+            : AlignmentDirectional.centerEnd;
+    }
+  }
+
   @override
   void dispose() {
     _stretchController.dispose();
@@ -724,23 +746,20 @@
           final double stretch = _stretchController.value;
           double x = 1.0;
           double y = 1.0;
-          final AlignmentDirectional alignment;
 
           switch (widget.axis) {
             case Axis.horizontal:
               x += stretch;
-              alignment = (_lastOverscrollNotification?.overscroll ?? 0) > 0
-                  ? AlignmentDirectional.centerEnd
-                  : AlignmentDirectional.centerStart;
               break;
             case Axis.vertical:
               y += stretch;
-              alignment = (_lastOverscrollNotification?.overscroll ?? 0) > 0
-                  ? AlignmentDirectional.bottomCenter
-                  : AlignmentDirectional.topCenter;
               break;
           }
 
+          final AlignmentDirectional alignment = _getAlignmentForAxisDirection(
+            _lastOverscrollNotification?.overscroll ?? 0.0
+          );
+
           return Transform(
             alignment: alignment,
             transform: Matrix4.diagonal3Values(x, y, 1.0),
diff --git a/packages/flutter/test/widgets/overscroll_stretch_indicator_test.dart b/packages/flutter/test/widgets/overscroll_stretch_indicator_test.dart
index cd75044..f5bed56 100644
--- a/packages/flutter/test/widgets/overscroll_stretch_indicator_test.dart
+++ b/packages/flutter/test/widgets/overscroll_stretch_indicator_test.dart
@@ -16,16 +16,28 @@
     Key box2Key,
     Key box3Key,
     ScrollController controller, {
-    Axis? axis,
+    Axis axis = Axis.vertical,
+    bool reverse = false,
   }) {
+    final AxisDirection axisDirection;
+    switch (axis) {
+      case Axis.horizontal:
+        axisDirection = reverse ? AxisDirection.left : AxisDirection.right;
+        break;
+      case Axis.vertical:
+        axisDirection = reverse ? AxisDirection.up : AxisDirection.down;
+        break;
+    }
+
     return Directionality(
       textDirection: TextDirection.ltr,
       child: ScrollConfiguration(
         behavior: const ScrollBehavior().copyWith(overscroll: false),
         child: StretchingOverscrollIndicator(
-          axisDirection: axis == null ? AxisDirection.down : AxisDirection.right,
+          axisDirection: axisDirection,
           child: CustomScrollView(
-            scrollDirection: axis ?? Axis.vertical,
+            reverse: reverse,
+            scrollDirection: axis,
             controller: controller,
             slivers: <Widget>[
               SliverToBoxAdapter(child: Container(
@@ -118,6 +130,79 @@
     );
   });
 
+  testWidgets('Stretch overscroll works in reverse - vertical', (WidgetTester tester) async {
+    final Key box1Key = UniqueKey();
+    final Key box2Key = UniqueKey();
+    final Key box3Key = UniqueKey();
+    final ScrollController controller = ScrollController();
+    await tester.pumpWidget(
+      buildTest(box1Key, box2Key, box3Key, controller, reverse: true),
+    );
+
+    expect(find.byType(StretchingOverscrollIndicator), findsOneWidget);
+    expect(find.byType(GlowingOverscrollIndicator), findsNothing);
+    final RenderBox box1 = tester.renderObject(find.byKey(box1Key));
+    final RenderBox box2 = tester.renderObject(find.byKey(box2Key));
+    final RenderBox box3 = tester.renderObject(find.byKey(box3Key));
+
+    expect(controller.offset, 0.0);
+    expect(box1.localToGlobal(Offset.zero), const Offset(0.0, 350.0));
+    expect(box2.localToGlobal(Offset.zero), const Offset(0.0, 100.0));
+    expect(box3.localToGlobal(Offset.zero), const Offset(0.0, -150.0));
+
+    final TestGesture gesture = await tester.startGesture(tester.getCenter(find.byType(CustomScrollView)));
+    // Overscroll
+    await gesture.moveBy(const Offset(0.0, -200.0));
+    await tester.pumpAndSettle();
+    expect(box1.localToGlobal(Offset.zero).dy, lessThan(350.0));
+    expect(box2.localToGlobal(Offset.zero).dy, lessThan(100.0));
+    expect(box3.localToGlobal(Offset.zero).dy, lessThan(-150.0));
+    await expectLater(
+      find.byType(CustomScrollView),
+      matchesGoldenFile('overscroll_stretch.vertical.reverse.png'),
+    );
+  });
+
+  testWidgets('Stretch overscroll horizontally', (WidgetTester tester) async {
+    final Key box1Key = UniqueKey();
+    final Key box2Key = UniqueKey();
+    final Key box3Key = UniqueKey();
+    final ScrollController controller = ScrollController();
+    await tester.pumpWidget(
+        buildTest(
+          box1Key,
+          box2Key,
+          box3Key,
+          controller,
+          axis: Axis.horizontal,
+          reverse: true,
+        ),
+    );
+
+    expect(find.byType(StretchingOverscrollIndicator), findsOneWidget);
+    expect(find.byType(GlowingOverscrollIndicator), findsNothing);
+    final RenderBox box1 = tester.renderObject(find.byKey(box1Key));
+    final RenderBox box2 = tester.renderObject(find.byKey(box2Key));
+    final RenderBox box3 = tester.renderObject(find.byKey(box3Key));
+
+    expect(controller.offset, 0.0);
+    expect(box1.localToGlobal(Offset.zero), const Offset(500.0, 0.0));
+    expect(box2.localToGlobal(Offset.zero), const Offset(200.0, 0.0));
+    expect(box3.localToGlobal(Offset.zero), const Offset(-100.0, 0.0));
+
+    final TestGesture gesture = await tester.startGesture(tester.getCenter(find.byType(CustomScrollView)));
+    // Overscroll
+    await gesture.moveBy(const Offset(200.0, 0.0));
+    await tester.pumpAndSettle();
+    expect(box1.localToGlobal(Offset.zero).dx, greaterThan(500.0));
+    expect(box2.localToGlobal(Offset.zero).dx, greaterThan(200.0));
+    expect(box3.localToGlobal(Offset.zero).dx, greaterThan(-100.0));
+    await expectLater(
+      find.byType(CustomScrollView),
+      matchesGoldenFile('overscroll_stretch.horizontal.reverse.png'),
+    );
+  });
+
   testWidgets('Stretch overscroll horizontally', (WidgetTester tester) async {
     final Key box1Key = UniqueKey();
     final Key box2Key = UniqueKey();