Fix AnimationStatus for repeat(reverse: true) and animateWith (#37739)

diff --git a/packages/flutter/lib/src/animation/animation_controller.dart b/packages/flutter/lib/src/animation/animation_controller.dart
index 408df4ee..29d0fa5 100644
--- a/packages/flutter/lib/src/animation/animation_controller.dart
+++ b/packages/flutter/lib/src/animation/animation_controller.dart
@@ -597,7 +597,9 @@
   /// [AnimationController] when no explicit value is set for [min] and [max].
   ///
   /// With [reverse] set to true, instead of always starting over at [min]
-  /// the value will alternate between [min] and [max] values on each repeat.
+  /// the starting value will alternate between [min] and [max] values on each
+  /// repeat. The [status] will be reported as [AnimationStatus.reverse] when
+  /// the animation runs from [max] to [min].
   ///
   /// Returns a [TickerFuture] that never completes. The [TickerFuture.orCancel] future
   /// completes with an error when the animation is stopped (e.g. with [stop]).
@@ -623,7 +625,16 @@
     assert(max >= min);
     assert(max <= upperBound && min >= lowerBound);
     assert(reverse != null);
-    return animateWith(_RepeatingSimulation(_value, min, max, reverse, period));
+    stop();
+    return _startSimulation(_RepeatingSimulation(_value, min, max, reverse, period, _directionSetter));
+  }
+
+  void _directionSetter(_AnimationDirection direction) {
+    _direction = direction;
+    _status = (_direction == _AnimationDirection.forward) ?
+      AnimationStatus.forward :
+      AnimationStatus.reverse;
+    _checkStatusChanged();
   }
 
   /// Drives the animation with a critically damped spring (within [lowerBound]
@@ -656,7 +667,8 @@
     }
     final Simulation simulation = SpringSimulation(_kFlingSpringDescription, value, target, velocity * scale)
       ..tolerance = _kFlingTolerance;
-    return animateWith(simulation);
+    stop();
+    return _startSimulation(simulation);
   }
 
   /// Drives the animation according to the given simulation.
@@ -666,6 +678,9 @@
   /// The most recently returned [TickerFuture], if any, is marked as having been
   /// canceled, meaning the future never completes and its [TickerFuture.orCancel]
   /// derivative future completes with a [TickerCanceled] error.
+  ///
+  /// The [status] is always [AnimationStatus.forward] for the entire duration
+  /// of the simulation.
   TickerFuture animateWith(Simulation simulation) {
     assert(
       _ticker != null,
@@ -673,6 +688,7 @@
       'AnimationController methods should not be used after calling dispose.'
     );
     stop();
+    _direction = _AnimationDirection.forward;
     return _startSimulation(simulation);
   }
 
@@ -812,8 +828,10 @@
   bool isDone(double timeInSeconds) => timeInSeconds > _durationInSeconds;
 }
 
+typedef _DirectionSetter = void Function(_AnimationDirection direction);
+
 class _RepeatingSimulation extends Simulation {
-  _RepeatingSimulation(double initialValue, this.min, this.max, this.reverse, Duration period)
+  _RepeatingSimulation(double initialValue, this.min, this.max, this.reverse, Duration period, this.directionSetter)
       : _periodInSeconds = period.inMicroseconds / Duration.microsecondsPerSecond,
         _initialT = (max == min) ? 0.0 : (initialValue / (max - min)) * (period.inMicroseconds / Duration.microsecondsPerSecond) {
     assert(_periodInSeconds > 0.0);
@@ -823,6 +841,7 @@
   final double min;
   final double max;
   final bool reverse;
+  final _DirectionSetter directionSetter;
 
   final double _periodInSeconds;
   final double _initialT;
@@ -836,8 +855,10 @@
     final bool _isPlayingReverse = (totalTimeInSeconds ~/ _periodInSeconds) % 2 == 1;
 
     if (reverse && _isPlayingReverse) {
+      directionSetter(_AnimationDirection.reverse);
       return ui.lerpDouble(max, min, t);
     } else {
+      directionSetter(_AnimationDirection.forward);
       return ui.lerpDouble(min, max, t);
     }
   }
diff --git a/packages/flutter/test/animation/animation_controller_test.dart b/packages/flutter/test/animation/animation_controller_test.dart
index 34c86fa..15796ee 100644
--- a/packages/flutter/test/animation/animation_controller_test.dart
+++ b/packages/flutter/test/animation/animation_controller_test.dart
@@ -846,6 +846,69 @@
     expect(() => controller.stop(), throwsAssertionError);
     expect(() => controller.forward(), throwsAssertionError);
     expect(() => controller.reverse(), throwsAssertionError);
-
   });
+
+  test('Simulations run forward', () {
+    final List<AnimationStatus> statuses = <AnimationStatus>[];
+    final AnimationController controller = AnimationController(
+      vsync: const TestVSync(),
+      duration: const Duration(seconds: 1),
+    )..addStatusListener((AnimationStatus status) {
+      statuses.add(status);
+    });
+
+    controller.animateWith(TestSimulation());
+    tick(const Duration(milliseconds: 0));
+    tick(const Duration(seconds: 2));
+    expect(statuses, <AnimationStatus>[AnimationStatus.forward]);
+  });
+
+  test('Simulations run forward even after a reverse run', () {
+    final List<AnimationStatus> statuses = <AnimationStatus>[];
+    final AnimationController controller = AnimationController(
+      vsync: const TestVSync(),
+      duration: const Duration(seconds: 1),
+    )..addStatusListener((AnimationStatus status) {
+      statuses.add(status);
+    });
+    controller.reverse(from: 1.0);
+    tick(const Duration(milliseconds: 0));
+    tick(const Duration(seconds: 2));
+    expect(statuses, <AnimationStatus>[AnimationStatus.completed, AnimationStatus.reverse, AnimationStatus.dismissed]);
+    statuses.clear();
+
+    controller.animateWith(TestSimulation());
+    tick(const Duration(milliseconds: 0));
+    tick(const Duration(seconds: 2));
+    expect(statuses, <AnimationStatus>[AnimationStatus.forward]);
+  });
+
+  test('Repeating animation with reverse: true report as forward and reverse', () {
+    final List<AnimationStatus> statuses = <AnimationStatus>[];
+    final AnimationController controller = AnimationController(
+      vsync: const TestVSync(),
+      duration: const Duration(seconds: 1),
+    )..addStatusListener((AnimationStatus status) {
+      statuses.add(status);
+    });
+
+    controller.repeat(reverse: true);
+    tick(const Duration(milliseconds: 0));
+    tick(const Duration(milliseconds: 999));
+    expect(statuses, <AnimationStatus>[AnimationStatus.forward]);
+    statuses.clear();
+    tick(const Duration(seconds: 1));
+    expect(statuses, <AnimationStatus>[AnimationStatus.reverse]);
+  });
+}
+
+class TestSimulation extends Simulation {
+  @override
+  double dx(double time) => time;
+
+  @override
+  bool isDone(double time) => false;
+
+  @override
+  double x(double time) => time;
 }