Unverified Commit 6d3c5381 authored by Michael Goderbauer's avatar Michael Goderbauer Committed by GitHub

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

parent 4bffe3dd
......@@ -597,7 +597,9 @@ class AnimationController extends Animation<double>
/// [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 @@ class AnimationController extends Animation<double>
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 @@ class AnimationController extends Animation<double>
}
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 @@ class AnimationController extends Animation<double>
/// 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 @@ class AnimationController extends Animation<double>
'AnimationController methods should not be used after calling dispose.'
);
stop();
_direction = _AnimationDirection.forward;
return _startSimulation(simulation);
}
......@@ -812,8 +828,10 @@ class _InterpolationSimulation extends Simulation {
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 @@ class _RepeatingSimulation extends Simulation {
final double min;
final double max;
final bool reverse;
final _DirectionSetter directionSetter;
final double _periodInSeconds;
final double _initialT;
......@@ -836,8 +855,10 @@ class _RepeatingSimulation extends Simulation {
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);
}
}
......
......@@ -846,6 +846,69 @@ void main() {
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;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment