Add reverseDuration to AnimationController (#32730)
This adds a reverseDuration parameter to AnimationController so that the animation has a different duration when going in reverse as it does going forward.
This commit is contained in:
parent
f330804baf
commit
64d1097e53
@ -231,6 +231,7 @@ class AnimationController extends Animation<double>
|
|||||||
AnimationController({
|
AnimationController({
|
||||||
double value,
|
double value,
|
||||||
this.duration,
|
this.duration,
|
||||||
|
this.reverseDuration,
|
||||||
this.debugLabel,
|
this.debugLabel,
|
||||||
this.lowerBound = 0.0,
|
this.lowerBound = 0.0,
|
||||||
this.upperBound = 1.0,
|
this.upperBound = 1.0,
|
||||||
@ -264,6 +265,7 @@ class AnimationController extends Animation<double>
|
|||||||
AnimationController.unbounded({
|
AnimationController.unbounded({
|
||||||
double value = 0.0,
|
double value = 0.0,
|
||||||
this.duration,
|
this.duration,
|
||||||
|
this.reverseDuration,
|
||||||
this.debugLabel,
|
this.debugLabel,
|
||||||
@required TickerProvider vsync,
|
@required TickerProvider vsync,
|
||||||
this.animationBehavior = AnimationBehavior.preserve,
|
this.animationBehavior = AnimationBehavior.preserve,
|
||||||
@ -300,8 +302,17 @@ class AnimationController extends Animation<double>
|
|||||||
Animation<double> get view => this;
|
Animation<double> get view => this;
|
||||||
|
|
||||||
/// The length of time this animation should last.
|
/// The length of time this animation should last.
|
||||||
|
///
|
||||||
|
/// If [reverseDuration] is specified, then [duration] is only used when going
|
||||||
|
/// [forward]. Otherwise, it specifies the duration going in both directions.
|
||||||
Duration duration;
|
Duration duration;
|
||||||
|
|
||||||
|
/// The length of time this animation should last when going in [reverse].
|
||||||
|
///
|
||||||
|
/// The value of [duration] us used if [reverseDuration] is not specified or
|
||||||
|
/// set to null.
|
||||||
|
Duration reverseDuration;
|
||||||
|
|
||||||
Ticker _ticker;
|
Ticker _ticker;
|
||||||
|
|
||||||
/// Recreates the [Ticker] with the new [TickerProvider].
|
/// Recreates the [Ticker] with the new [TickerProvider].
|
||||||
@ -429,7 +440,7 @@ class AnimationController extends Animation<double>
|
|||||||
assert(() {
|
assert(() {
|
||||||
if (duration == null) {
|
if (duration == null) {
|
||||||
throw FlutterError(
|
throw FlutterError(
|
||||||
'AnimationController.forward() called with no default Duration.\n'
|
'AnimationController.forward() called with no default duration.\n'
|
||||||
'The "duration" property should be set, either in the constructor or later, before '
|
'The "duration" property should be set, either in the constructor or later, before '
|
||||||
'calling the forward() function.'
|
'calling the forward() function.'
|
||||||
);
|
);
|
||||||
@ -460,10 +471,10 @@ class AnimationController extends Animation<double>
|
|||||||
/// reached at the end of the animation.
|
/// reached at the end of the animation.
|
||||||
TickerFuture reverse({ double from }) {
|
TickerFuture reverse({ double from }) {
|
||||||
assert(() {
|
assert(() {
|
||||||
if (duration == null) {
|
if (duration == null && reverseDuration == null) {
|
||||||
throw FlutterError(
|
throw FlutterError(
|
||||||
'AnimationController.reverse() called with no default Duration.\n'
|
'AnimationController.reverse() called with no default duration or reverseDuration.\n'
|
||||||
'The "duration" property should be set, either in the constructor or later, before '
|
'The "duration" or "reverseDuration" property should be set, either in the constructor or later, before '
|
||||||
'calling the reverse() function.'
|
'calling the reverse() function.'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -541,11 +552,11 @@ class AnimationController extends Animation<double>
|
|||||||
Duration simulationDuration = duration;
|
Duration simulationDuration = duration;
|
||||||
if (simulationDuration == null) {
|
if (simulationDuration == null) {
|
||||||
assert(() {
|
assert(() {
|
||||||
if (this.duration == null) {
|
if ((this.duration == null && _direction == _AnimationDirection.reverse && reverseDuration == null) || this.duration == null) {
|
||||||
throw FlutterError(
|
throw FlutterError(
|
||||||
'AnimationController.animateTo() called with no explicit Duration and no default Duration.\n'
|
'AnimationController.animateTo() called with no explicit duration and no default duration or reverseDuration.\n'
|
||||||
'Either the "duration" argument to the animateTo() method should be provided, or the '
|
'Either the "duration" argument to the animateTo() method should be provided, or the '
|
||||||
'"duration" property should be set, either in the constructor or later, before '
|
'"duration" and/or "reverseDuration" property should be set, either in the constructor or later, before '
|
||||||
'calling the animateTo() function.'
|
'calling the animateTo() function.'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -553,7 +564,11 @@ class AnimationController extends Animation<double>
|
|||||||
}());
|
}());
|
||||||
final double range = upperBound - lowerBound;
|
final double range = upperBound - lowerBound;
|
||||||
final double remainingFraction = range.isFinite ? (target - _value).abs() / range : 1.0;
|
final double remainingFraction = range.isFinite ? (target - _value).abs() / range : 1.0;
|
||||||
simulationDuration = this.duration * remainingFraction;
|
final Duration directionDuration =
|
||||||
|
(_direction == _AnimationDirection.reverse && reverseDuration != null)
|
||||||
|
? reverseDuration
|
||||||
|
: this.duration;
|
||||||
|
simulationDuration = directionDuration * remainingFraction;
|
||||||
} else if (target == value) {
|
} else if (target == value) {
|
||||||
// Already at target, don't animate.
|
// Already at target, don't animate.
|
||||||
simulationDuration = Duration.zero;
|
simulationDuration = Duration.zero;
|
||||||
|
@ -76,6 +76,7 @@ class RenderAnimatedSize extends RenderAligningShiftedBox {
|
|||||||
RenderAnimatedSize({
|
RenderAnimatedSize({
|
||||||
@required TickerProvider vsync,
|
@required TickerProvider vsync,
|
||||||
@required Duration duration,
|
@required Duration duration,
|
||||||
|
Duration reverseDuration,
|
||||||
Curve curve = Curves.linear,
|
Curve curve = Curves.linear,
|
||||||
AlignmentGeometry alignment = Alignment.center,
|
AlignmentGeometry alignment = Alignment.center,
|
||||||
TextDirection textDirection,
|
TextDirection textDirection,
|
||||||
@ -88,6 +89,7 @@ class RenderAnimatedSize extends RenderAligningShiftedBox {
|
|||||||
_controller = AnimationController(
|
_controller = AnimationController(
|
||||||
vsync: vsync,
|
vsync: vsync,
|
||||||
duration: duration,
|
duration: duration,
|
||||||
|
reverseDuration: reverseDuration,
|
||||||
)..addListener(() {
|
)..addListener(() {
|
||||||
if (_controller.value != _lastValue)
|
if (_controller.value != _lastValue)
|
||||||
markNeedsLayout();
|
markNeedsLayout();
|
||||||
@ -120,6 +122,14 @@ class RenderAnimatedSize extends RenderAligningShiftedBox {
|
|||||||
_controller.duration = value;
|
_controller.duration = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The duration of the animation when running in reverse.
|
||||||
|
Duration get reverseDuration => _controller.reverseDuration;
|
||||||
|
set reverseDuration(Duration value) {
|
||||||
|
if (value == _controller.reverseDuration)
|
||||||
|
return;
|
||||||
|
_controller.reverseDuration = value;
|
||||||
|
}
|
||||||
|
|
||||||
/// The curve of the animation.
|
/// The curve of the animation.
|
||||||
Curve get curve => _animation.curve;
|
Curve get curve => _animation.curve;
|
||||||
set curve(Curve value) {
|
set curve(Curve value) {
|
||||||
|
@ -124,6 +124,7 @@ class AnimatedCrossFade extends StatefulWidget {
|
|||||||
this.alignment = Alignment.topCenter,
|
this.alignment = Alignment.topCenter,
|
||||||
@required this.crossFadeState,
|
@required this.crossFadeState,
|
||||||
@required this.duration,
|
@required this.duration,
|
||||||
|
this.reverseDuration,
|
||||||
this.layoutBuilder = defaultLayoutBuilder,
|
this.layoutBuilder = defaultLayoutBuilder,
|
||||||
}) : assert(firstChild != null),
|
}) : assert(firstChild != null),
|
||||||
assert(secondChild != null),
|
assert(secondChild != null),
|
||||||
@ -154,6 +155,11 @@ class AnimatedCrossFade extends StatefulWidget {
|
|||||||
/// The duration of the whole orchestrated animation.
|
/// The duration of the whole orchestrated animation.
|
||||||
final Duration duration;
|
final Duration duration;
|
||||||
|
|
||||||
|
/// The duration of the whole orchestrated animation when running in reverse.
|
||||||
|
///
|
||||||
|
/// If not supplied, this defaults to [duration].
|
||||||
|
final Duration reverseDuration;
|
||||||
|
|
||||||
/// The fade curve of the first child.
|
/// The fade curve of the first child.
|
||||||
///
|
///
|
||||||
/// Defaults to [Curves.linear].
|
/// Defaults to [Curves.linear].
|
||||||
@ -232,6 +238,8 @@ class AnimatedCrossFade extends StatefulWidget {
|
|||||||
super.debugFillProperties(properties);
|
super.debugFillProperties(properties);
|
||||||
properties.add(EnumProperty<CrossFadeState>('crossFadeState', crossFadeState));
|
properties.add(EnumProperty<CrossFadeState>('crossFadeState', crossFadeState));
|
||||||
properties.add(DiagnosticsProperty<AlignmentGeometry>('alignment', alignment, defaultValue: Alignment.topCenter));
|
properties.add(DiagnosticsProperty<AlignmentGeometry>('alignment', alignment, defaultValue: Alignment.topCenter));
|
||||||
|
properties.add(IntProperty('duration', duration.inMilliseconds, unit: 'ms'));
|
||||||
|
properties.add(IntProperty('reverseDuration', reverseDuration?.inMilliseconds, unit: 'ms', defaultValue: null));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -243,7 +251,11 @@ class _AnimatedCrossFadeState extends State<AnimatedCrossFade> with TickerProvid
|
|||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_controller = AnimationController(duration: widget.duration, vsync: this);
|
_controller = AnimationController(
|
||||||
|
duration: widget.duration,
|
||||||
|
reverseDuration: widget.reverseDuration,
|
||||||
|
vsync: this,
|
||||||
|
);
|
||||||
if (widget.crossFadeState == CrossFadeState.showSecond)
|
if (widget.crossFadeState == CrossFadeState.showSecond)
|
||||||
_controller.value = 1.0;
|
_controller.value = 1.0;
|
||||||
_firstAnimation = _initAnimation(widget.firstCurve, true);
|
_firstAnimation = _initAnimation(widget.firstCurve, true);
|
||||||
@ -274,6 +286,8 @@ class _AnimatedCrossFadeState extends State<AnimatedCrossFade> with TickerProvid
|
|||||||
super.didUpdateWidget(oldWidget);
|
super.didUpdateWidget(oldWidget);
|
||||||
if (widget.duration != oldWidget.duration)
|
if (widget.duration != oldWidget.duration)
|
||||||
_controller.duration = widget.duration;
|
_controller.duration = widget.duration;
|
||||||
|
if (widget.reverseDuration != oldWidget.reverseDuration)
|
||||||
|
_controller.reverseDuration = widget.reverseDuration;
|
||||||
if (widget.firstCurve != oldWidget.firstCurve)
|
if (widget.firstCurve != oldWidget.firstCurve)
|
||||||
_firstAnimation = _initAnimation(widget.firstCurve, true);
|
_firstAnimation = _initAnimation(widget.firstCurve, true);
|
||||||
if (widget.secondCurve != oldWidget.secondCurve)
|
if (widget.secondCurve != oldWidget.secondCurve)
|
||||||
@ -347,6 +361,7 @@ class _AnimatedCrossFadeState extends State<AnimatedCrossFade> with TickerProvid
|
|||||||
child: AnimatedSize(
|
child: AnimatedSize(
|
||||||
alignment: widget.alignment,
|
alignment: widget.alignment,
|
||||||
duration: widget.duration,
|
duration: widget.duration,
|
||||||
|
reverseDuration: widget.reverseDuration,
|
||||||
curve: widget.sizeCurve,
|
curve: widget.sizeCurve,
|
||||||
vsync: this,
|
vsync: this,
|
||||||
child: widget.layoutBuilder(topChild, topKey, bottomChild, bottomKey),
|
child: widget.layoutBuilder(topChild, topKey, bottomChild, bottomKey),
|
||||||
|
@ -20,6 +20,7 @@ class AnimatedSize extends SingleChildRenderObjectWidget {
|
|||||||
this.alignment = Alignment.center,
|
this.alignment = Alignment.center,
|
||||||
this.curve = Curves.linear,
|
this.curve = Curves.linear,
|
||||||
@required this.duration,
|
@required this.duration,
|
||||||
|
this.reverseDuration,
|
||||||
@required this.vsync,
|
@required this.vsync,
|
||||||
}) : super(key: key, child: child);
|
}) : super(key: key, child: child);
|
||||||
|
|
||||||
@ -52,6 +53,12 @@ class AnimatedSize extends SingleChildRenderObjectWidget {
|
|||||||
/// size.
|
/// size.
|
||||||
final Duration duration;
|
final Duration duration;
|
||||||
|
|
||||||
|
/// The duration when transitioning this widget's size to match the child's
|
||||||
|
/// size when going in reverse.
|
||||||
|
///
|
||||||
|
/// If not specified, defaults to [duration].
|
||||||
|
final Duration reverseDuration;
|
||||||
|
|
||||||
/// The [TickerProvider] for this widget.
|
/// The [TickerProvider] for this widget.
|
||||||
final TickerProvider vsync;
|
final TickerProvider vsync;
|
||||||
|
|
||||||
@ -60,6 +67,7 @@ class AnimatedSize extends SingleChildRenderObjectWidget {
|
|||||||
return RenderAnimatedSize(
|
return RenderAnimatedSize(
|
||||||
alignment: alignment,
|
alignment: alignment,
|
||||||
duration: duration,
|
duration: duration,
|
||||||
|
reverseDuration: reverseDuration,
|
||||||
curve: curve,
|
curve: curve,
|
||||||
vsync: vsync,
|
vsync: vsync,
|
||||||
textDirection: Directionality.of(context),
|
textDirection: Directionality.of(context),
|
||||||
@ -71,8 +79,17 @@ class AnimatedSize extends SingleChildRenderObjectWidget {
|
|||||||
renderObject
|
renderObject
|
||||||
..alignment = alignment
|
..alignment = alignment
|
||||||
..duration = duration
|
..duration = duration
|
||||||
|
..reverseDuration = reverseDuration
|
||||||
..curve = curve
|
..curve = curve
|
||||||
..vsync = vsync
|
..vsync = vsync
|
||||||
..textDirection = Directionality.of(context);
|
..textDirection = Directionality.of(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
||||||
|
super.debugFillProperties(properties);
|
||||||
|
properties.add(DiagnosticsProperty<AlignmentGeometry>('alignment', alignment, defaultValue: Alignment.topCenter));
|
||||||
|
properties.add(IntProperty('duration', duration.inMilliseconds, unit: 'ms'));
|
||||||
|
properties.add(IntProperty('reverseDuration', reverseDuration?.inMilliseconds, unit: 'ms', defaultValue: null));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -150,6 +150,7 @@ class AnimatedSwitcher extends StatefulWidget {
|
|||||||
Key key,
|
Key key,
|
||||||
this.child,
|
this.child,
|
||||||
@required this.duration,
|
@required this.duration,
|
||||||
|
this.reverseDuration,
|
||||||
this.switchInCurve = Curves.linear,
|
this.switchInCurve = Curves.linear,
|
||||||
this.switchOutCurve = Curves.linear,
|
this.switchOutCurve = Curves.linear,
|
||||||
this.transitionBuilder = AnimatedSwitcher.defaultTransitionBuilder,
|
this.transitionBuilder = AnimatedSwitcher.defaultTransitionBuilder,
|
||||||
@ -177,11 +178,20 @@ class AnimatedSwitcher extends StatefulWidget {
|
|||||||
/// The duration of the transition from the old [child] value to the new one.
|
/// The duration of the transition from the old [child] value to the new one.
|
||||||
///
|
///
|
||||||
/// This duration is applied to the given [child] when that property is set to
|
/// This duration is applied to the given [child] when that property is set to
|
||||||
/// a new child. The same duration is used when fading out. Changing
|
/// a new child. The same duration is used when fading out, unless
|
||||||
/// [duration] will not affect the durations of transitions already in
|
/// [reverseDuration] is set. Changing [duration] will not affect the
|
||||||
/// progress.
|
/// durations of transitions already in progress.
|
||||||
final Duration duration;
|
final Duration duration;
|
||||||
|
|
||||||
|
/// The duration of the transition from the new [child] value to the old one.
|
||||||
|
///
|
||||||
|
/// This duration is applied to the given [child] when that property is set to
|
||||||
|
/// a new child. Changing [reverseDuration] will not affect the durations of
|
||||||
|
/// transitions already in progress.
|
||||||
|
///
|
||||||
|
/// If not set, then the value of [duration] is used by default.
|
||||||
|
final Duration reverseDuration;
|
||||||
|
|
||||||
/// The animation curve to use when transitioning in a new [child].
|
/// The animation curve to use when transitioning in a new [child].
|
||||||
///
|
///
|
||||||
/// This curve is applied to the given [child] when that property is set to a
|
/// This curve is applied to the given [child] when that property is set to a
|
||||||
@ -272,6 +282,13 @@ class AnimatedSwitcher extends StatefulWidget {
|
|||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
||||||
|
super.debugFillProperties(properties);
|
||||||
|
properties.add(IntProperty('duration', duration.inMilliseconds, unit: 'ms'));
|
||||||
|
properties.add(IntProperty('reverseDuration', reverseDuration?.inMilliseconds, unit: 'ms', defaultValue: null));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _AnimatedSwitcherState extends State<AnimatedSwitcher> with TickerProviderStateMixin {
|
class _AnimatedSwitcherState extends State<AnimatedSwitcher> with TickerProviderStateMixin {
|
||||||
@ -333,6 +350,7 @@ class _AnimatedSwitcherState extends State<AnimatedSwitcher> with TickerProvider
|
|||||||
return;
|
return;
|
||||||
final AnimationController controller = AnimationController(
|
final AnimationController controller = AnimationController(
|
||||||
duration: widget.duration,
|
duration: widget.duration,
|
||||||
|
reverseDuration: widget.reverseDuration,
|
||||||
vsync: this,
|
vsync: this,
|
||||||
);
|
);
|
||||||
final Animation<double> animation = CurvedAnimation(
|
final Animation<double> animation = CurvedAnimation(
|
||||||
|
@ -227,6 +227,7 @@ abstract class ImplicitlyAnimatedWidget extends StatefulWidget {
|
|||||||
Key key,
|
Key key,
|
||||||
this.curve = Curves.linear,
|
this.curve = Curves.linear,
|
||||||
@required this.duration,
|
@required this.duration,
|
||||||
|
this.reverseDuration,
|
||||||
}) : assert(curve != null),
|
}) : assert(curve != null),
|
||||||
assert(duration != null),
|
assert(duration != null),
|
||||||
super(key: key);
|
super(key: key);
|
||||||
@ -237,6 +238,12 @@ abstract class ImplicitlyAnimatedWidget extends StatefulWidget {
|
|||||||
/// The duration over which to animate the parameters of this container.
|
/// The duration over which to animate the parameters of this container.
|
||||||
final Duration duration;
|
final Duration duration;
|
||||||
|
|
||||||
|
/// The duration over which to animate the parameters of this container when
|
||||||
|
/// the animation is going in the reverse direction.
|
||||||
|
///
|
||||||
|
/// Defaults to [duration] if not specified.
|
||||||
|
final Duration reverseDuration;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
ImplicitlyAnimatedWidgetState<ImplicitlyAnimatedWidget> createState();
|
ImplicitlyAnimatedWidgetState<ImplicitlyAnimatedWidget> createState();
|
||||||
|
|
||||||
@ -244,6 +251,7 @@ abstract class ImplicitlyAnimatedWidget extends StatefulWidget {
|
|||||||
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
|
||||||
super.debugFillProperties(properties);
|
super.debugFillProperties(properties);
|
||||||
properties.add(IntProperty('duration', duration.inMilliseconds, unit: 'ms'));
|
properties.add(IntProperty('duration', duration.inMilliseconds, unit: 'ms'));
|
||||||
|
properties.add(IntProperty('reverseDuration', reverseDuration?.inMilliseconds, unit: 'ms', defaultValue: null));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -280,6 +288,7 @@ abstract class ImplicitlyAnimatedWidgetState<T extends ImplicitlyAnimatedWidget>
|
|||||||
super.initState();
|
super.initState();
|
||||||
_controller = AnimationController(
|
_controller = AnimationController(
|
||||||
duration: widget.duration,
|
duration: widget.duration,
|
||||||
|
reverseDuration: widget.reverseDuration,
|
||||||
debugLabel: kDebugMode ? '${widget.toStringShort()}' : null,
|
debugLabel: kDebugMode ? '${widget.toStringShort()}' : null,
|
||||||
vsync: this,
|
vsync: this,
|
||||||
);
|
);
|
||||||
@ -294,6 +303,7 @@ abstract class ImplicitlyAnimatedWidgetState<T extends ImplicitlyAnimatedWidget>
|
|||||||
if (widget.curve != oldWidget.curve)
|
if (widget.curve != oldWidget.curve)
|
||||||
_updateCurve();
|
_updateCurve();
|
||||||
_controller.duration = widget.duration;
|
_controller.duration = widget.duration;
|
||||||
|
_controller.reverseDuration = widget.reverseDuration;
|
||||||
if (_constructTweens()) {
|
if (_constructTweens()) {
|
||||||
forEachTween((Tween<dynamic> tween, dynamic targetValue, TweenConstructor<dynamic> constructor) {
|
forEachTween((Tween<dynamic> tween, dynamic targetValue, TweenConstructor<dynamic> constructor) {
|
||||||
_updateTween(tween, targetValue);
|
_updateTween(tween, targetValue);
|
||||||
@ -489,6 +499,7 @@ class AnimatedContainer extends ImplicitlyAnimatedWidget {
|
|||||||
this.child,
|
this.child,
|
||||||
Curve curve = Curves.linear,
|
Curve curve = Curves.linear,
|
||||||
@required Duration duration,
|
@required Duration duration,
|
||||||
|
Duration reverseDuration,
|
||||||
}) : assert(margin == null || margin.isNonNegative),
|
}) : assert(margin == null || margin.isNonNegative),
|
||||||
assert(padding == null || padding.isNonNegative),
|
assert(padding == null || padding.isNonNegative),
|
||||||
assert(decoration == null || decoration.debugAssertIsValid()),
|
assert(decoration == null || decoration.debugAssertIsValid()),
|
||||||
@ -503,7 +514,7 @@ class AnimatedContainer extends ImplicitlyAnimatedWidget {
|
|||||||
? constraints?.tighten(width: width, height: height)
|
? constraints?.tighten(width: width, height: height)
|
||||||
?? BoxConstraints.tightFor(width: width, height: height)
|
?? BoxConstraints.tightFor(width: width, height: height)
|
||||||
: constraints,
|
: constraints,
|
||||||
super(key: key, curve: curve, duration: duration);
|
super(key: key, curve: curve, duration: duration, reverseDuration: reverseDuration);
|
||||||
|
|
||||||
/// The [child] contained by the container.
|
/// The [child] contained by the container.
|
||||||
///
|
///
|
||||||
@ -645,9 +656,10 @@ class AnimatedPadding extends ImplicitlyAnimatedWidget {
|
|||||||
this.child,
|
this.child,
|
||||||
Curve curve = Curves.linear,
|
Curve curve = Curves.linear,
|
||||||
@required Duration duration,
|
@required Duration duration,
|
||||||
|
Duration reverseDuration,
|
||||||
}) : assert(padding != null),
|
}) : assert(padding != null),
|
||||||
assert(padding.isNonNegative),
|
assert(padding.isNonNegative),
|
||||||
super(key: key, curve: curve, duration: duration);
|
super(key: key, curve: curve, duration: duration, reverseDuration: reverseDuration);
|
||||||
|
|
||||||
/// The amount of space by which to inset the child.
|
/// The amount of space by which to inset the child.
|
||||||
final EdgeInsetsGeometry padding;
|
final EdgeInsetsGeometry padding;
|
||||||
@ -716,8 +728,9 @@ class AnimatedAlign extends ImplicitlyAnimatedWidget {
|
|||||||
this.child,
|
this.child,
|
||||||
Curve curve = Curves.linear,
|
Curve curve = Curves.linear,
|
||||||
@required Duration duration,
|
@required Duration duration,
|
||||||
|
Duration reverseDuration,
|
||||||
}) : assert(alignment != null),
|
}) : assert(alignment != null),
|
||||||
super(key: key, curve: curve, duration: duration);
|
super(key: key, curve: curve, duration: duration, reverseDuration: reverseDuration);
|
||||||
|
|
||||||
/// How to align the child.
|
/// How to align the child.
|
||||||
///
|
///
|
||||||
@ -816,9 +829,10 @@ class AnimatedPositioned extends ImplicitlyAnimatedWidget {
|
|||||||
this.height,
|
this.height,
|
||||||
Curve curve = Curves.linear,
|
Curve curve = Curves.linear,
|
||||||
@required Duration duration,
|
@required Duration duration,
|
||||||
|
Duration reverseDuration,
|
||||||
}) : assert(left == null || right == null || width == null),
|
}) : assert(left == null || right == null || width == null),
|
||||||
assert(top == null || bottom == null || height == null),
|
assert(top == null || bottom == null || height == null),
|
||||||
super(key: key, curve: curve, duration: duration);
|
super(key: key, curve: curve, duration: duration, reverseDuration: reverseDuration);
|
||||||
|
|
||||||
/// Creates a widget that animates the rectangle it occupies implicitly.
|
/// Creates a widget that animates the rectangle it occupies implicitly.
|
||||||
///
|
///
|
||||||
@ -829,13 +843,14 @@ class AnimatedPositioned extends ImplicitlyAnimatedWidget {
|
|||||||
Rect rect,
|
Rect rect,
|
||||||
Curve curve = Curves.linear,
|
Curve curve = Curves.linear,
|
||||||
@required Duration duration,
|
@required Duration duration,
|
||||||
|
Duration reverseDuration,
|
||||||
}) : left = rect.left,
|
}) : left = rect.left,
|
||||||
top = rect.top,
|
top = rect.top,
|
||||||
width = rect.width,
|
width = rect.width,
|
||||||
height = rect.height,
|
height = rect.height,
|
||||||
right = null,
|
right = null,
|
||||||
bottom = null,
|
bottom = null,
|
||||||
super(key: key, curve: curve, duration: duration);
|
super(key: key, curve: curve, duration: duration, reverseDuration: reverseDuration);
|
||||||
|
|
||||||
/// The widget below this widget in the tree.
|
/// The widget below this widget in the tree.
|
||||||
///
|
///
|
||||||
@ -967,9 +982,10 @@ class AnimatedPositionedDirectional extends ImplicitlyAnimatedWidget {
|
|||||||
this.height,
|
this.height,
|
||||||
Curve curve = Curves.linear,
|
Curve curve = Curves.linear,
|
||||||
@required Duration duration,
|
@required Duration duration,
|
||||||
|
Duration reverseDuration,
|
||||||
}) : assert(start == null || end == null || width == null),
|
}) : assert(start == null || end == null || width == null),
|
||||||
assert(top == null || bottom == null || height == null),
|
assert(top == null || bottom == null || height == null),
|
||||||
super(key: key, curve: curve, duration: duration);
|
super(key: key, curve: curve, duration: duration, reverseDuration: reverseDuration);
|
||||||
|
|
||||||
/// The widget below this widget in the tree.
|
/// The widget below this widget in the tree.
|
||||||
///
|
///
|
||||||
@ -1121,8 +1137,9 @@ class AnimatedOpacity extends ImplicitlyAnimatedWidget {
|
|||||||
@required this.opacity,
|
@required this.opacity,
|
||||||
Curve curve = Curves.linear,
|
Curve curve = Curves.linear,
|
||||||
@required Duration duration,
|
@required Duration duration,
|
||||||
|
Duration reverseDuration,
|
||||||
}) : assert(opacity != null && opacity >= 0.0 && opacity <= 1.0),
|
}) : assert(opacity != null && opacity >= 0.0 && opacity <= 1.0),
|
||||||
super(key: key, curve: curve, duration: duration);
|
super(key: key, curve: curve, duration: duration, reverseDuration: reverseDuration);
|
||||||
|
|
||||||
/// The widget below this widget in the tree.
|
/// The widget below this widget in the tree.
|
||||||
///
|
///
|
||||||
@ -1196,12 +1213,13 @@ class AnimatedDefaultTextStyle extends ImplicitlyAnimatedWidget {
|
|||||||
this.maxLines,
|
this.maxLines,
|
||||||
Curve curve = Curves.linear,
|
Curve curve = Curves.linear,
|
||||||
@required Duration duration,
|
@required Duration duration,
|
||||||
|
Duration reverseDuration,
|
||||||
}) : assert(style != null),
|
}) : assert(style != null),
|
||||||
assert(child != null),
|
assert(child != null),
|
||||||
assert(softWrap != null),
|
assert(softWrap != null),
|
||||||
assert(overflow != null),
|
assert(overflow != null),
|
||||||
assert(maxLines == null || maxLines > 0),
|
assert(maxLines == null || maxLines > 0),
|
||||||
super(key: key, curve: curve, duration: duration);
|
super(key: key, curve: curve, duration: duration, reverseDuration: reverseDuration);
|
||||||
|
|
||||||
/// The widget below this widget in the tree.
|
/// The widget below this widget in the tree.
|
||||||
///
|
///
|
||||||
@ -1311,6 +1329,7 @@ class AnimatedPhysicalModel extends ImplicitlyAnimatedWidget {
|
|||||||
this.animateShadowColor = true,
|
this.animateShadowColor = true,
|
||||||
Curve curve = Curves.linear,
|
Curve curve = Curves.linear,
|
||||||
@required Duration duration,
|
@required Duration duration,
|
||||||
|
Duration reverseDuration,
|
||||||
}) : assert(child != null),
|
}) : assert(child != null),
|
||||||
assert(shape != null),
|
assert(shape != null),
|
||||||
assert(clipBehavior != null),
|
assert(clipBehavior != null),
|
||||||
@ -1320,7 +1339,7 @@ class AnimatedPhysicalModel extends ImplicitlyAnimatedWidget {
|
|||||||
assert(shadowColor != null),
|
assert(shadowColor != null),
|
||||||
assert(animateColor != null),
|
assert(animateColor != null),
|
||||||
assert(animateShadowColor != null),
|
assert(animateShadowColor != null),
|
||||||
super(key: key, curve: curve, duration: duration);
|
super(key: key, curve: curve, duration: duration, reverseDuration: reverseDuration);
|
||||||
|
|
||||||
/// The widget below this widget in the tree.
|
/// The widget below this widget in the tree.
|
||||||
///
|
///
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
import 'dart:ui' as ui;
|
import 'dart:ui' as ui;
|
||||||
|
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/physics.dart';
|
import 'package:flutter/physics.dart';
|
||||||
import 'package:flutter/semantics.dart';
|
import 'package:flutter/semantics.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
@ -140,6 +141,69 @@ void main() {
|
|||||||
expect(controller.value, equals(0.0));
|
expect(controller.value, equals(0.0));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('Forward and reverse with different durations', () {
|
||||||
|
AnimationController controller = AnimationController(
|
||||||
|
duration: const Duration(milliseconds: 100),
|
||||||
|
reverseDuration: const Duration(milliseconds: 50),
|
||||||
|
vsync: const TestVSync(),
|
||||||
|
);
|
||||||
|
|
||||||
|
controller.forward();
|
||||||
|
tick(const Duration(milliseconds: 10));
|
||||||
|
tick(const Duration(milliseconds: 30));
|
||||||
|
expect(controller.value, closeTo(0.2, precisionErrorTolerance));
|
||||||
|
tick(const Duration(milliseconds: 60));
|
||||||
|
expect(controller.value, closeTo(0.5, precisionErrorTolerance));
|
||||||
|
tick(const Duration(milliseconds: 90));
|
||||||
|
expect(controller.value, closeTo(0.8, precisionErrorTolerance));
|
||||||
|
tick(const Duration(milliseconds: 120));
|
||||||
|
expect(controller.value, closeTo(1.0, precisionErrorTolerance));
|
||||||
|
controller.stop();
|
||||||
|
|
||||||
|
controller.reverse();
|
||||||
|
tick(const Duration(milliseconds: 210));
|
||||||
|
tick(const Duration(milliseconds: 220));
|
||||||
|
expect(controller.value, closeTo(0.8, precisionErrorTolerance));
|
||||||
|
tick(const Duration(milliseconds: 230));
|
||||||
|
expect(controller.value, closeTo(0.6, precisionErrorTolerance));
|
||||||
|
tick(const Duration(milliseconds: 240));
|
||||||
|
expect(controller.value, closeTo(0.4, precisionErrorTolerance));
|
||||||
|
tick(const Duration(milliseconds: 260));
|
||||||
|
expect(controller.value, closeTo(0.0, precisionErrorTolerance));
|
||||||
|
controller.stop();
|
||||||
|
|
||||||
|
// Swap which duration is longer.
|
||||||
|
controller = AnimationController(
|
||||||
|
duration: const Duration(milliseconds: 50),
|
||||||
|
reverseDuration: const Duration(milliseconds: 100),
|
||||||
|
vsync: const TestVSync(),
|
||||||
|
);
|
||||||
|
|
||||||
|
controller.forward();
|
||||||
|
tick(const Duration(milliseconds: 10));
|
||||||
|
tick(const Duration(milliseconds: 30));
|
||||||
|
expect(controller.value, closeTo(0.4, precisionErrorTolerance));
|
||||||
|
tick(const Duration(milliseconds: 60));
|
||||||
|
expect(controller.value, closeTo(1.0, precisionErrorTolerance));
|
||||||
|
tick(const Duration(milliseconds: 90));
|
||||||
|
expect(controller.value, closeTo(1.0, precisionErrorTolerance));
|
||||||
|
controller.stop();
|
||||||
|
|
||||||
|
controller.reverse();
|
||||||
|
tick(const Duration(milliseconds: 210));
|
||||||
|
tick(const Duration(milliseconds: 220));
|
||||||
|
expect(controller.value, closeTo(0.9, precisionErrorTolerance));
|
||||||
|
tick(const Duration(milliseconds: 230));
|
||||||
|
expect(controller.value, closeTo(0.8, precisionErrorTolerance));
|
||||||
|
tick(const Duration(milliseconds: 240));
|
||||||
|
expect(controller.value, closeTo(0.7, precisionErrorTolerance));
|
||||||
|
tick(const Duration(milliseconds: 260));
|
||||||
|
expect(controller.value, closeTo(0.5, precisionErrorTolerance));
|
||||||
|
tick(const Duration(milliseconds: 310));
|
||||||
|
expect(controller.value, closeTo(0.0, precisionErrorTolerance));
|
||||||
|
controller.stop();
|
||||||
|
});
|
||||||
|
|
||||||
test('Forward only from value', () {
|
test('Forward only from value', () {
|
||||||
final AnimationController controller = AnimationController(
|
final AnimationController controller = AnimationController(
|
||||||
duration: const Duration(milliseconds: 100),
|
duration: const Duration(milliseconds: 100),
|
||||||
|
@ -2,10 +2,15 @@
|
|||||||
// Use of this source code is governed by a BSD-style license that can be
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
import 'dart:ui' as ui;
|
||||||
|
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:flutter/animation.dart';
|
import 'package:flutter/animation.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
|
import '../scheduler/scheduler_tester.dart';
|
||||||
|
|
||||||
class BogusCurve extends Curve {
|
class BogusCurve extends Curve {
|
||||||
@override
|
@override
|
||||||
double transform(double t) => 100.0;
|
double transform(double t) => 100.0;
|
||||||
@ -15,6 +20,8 @@ void main() {
|
|||||||
setUp(() {
|
setUp(() {
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
WidgetsBinding.instance.resetEpoch();
|
WidgetsBinding.instance.resetEpoch();
|
||||||
|
ui.window.onBeginFrame = null;
|
||||||
|
ui.window.onDrawFrame = null;
|
||||||
});
|
});
|
||||||
|
|
||||||
test('toString control test', () {
|
test('toString control test', () {
|
||||||
@ -235,6 +242,102 @@ void main() {
|
|||||||
expect(() { curved.value; }, throwsFlutterError);
|
expect(() { curved.value; }, throwsFlutterError);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('CurvedAnimation running with different forward and reverse durations.', () {
|
||||||
|
final AnimationController controller = AnimationController(
|
||||||
|
duration: const Duration(milliseconds: 100),
|
||||||
|
reverseDuration: const Duration(milliseconds: 50),
|
||||||
|
vsync: const TestVSync(),
|
||||||
|
);
|
||||||
|
final CurvedAnimation curved = CurvedAnimation(parent: controller, curve: Curves.linear, reverseCurve: Curves.linear);
|
||||||
|
|
||||||
|
controller.forward();
|
||||||
|
tick(const Duration(milliseconds: 0));
|
||||||
|
tick(const Duration(milliseconds: 10));
|
||||||
|
expect(curved.value, closeTo(0.1, precisionErrorTolerance));
|
||||||
|
tick(const Duration(milliseconds: 20));
|
||||||
|
expect(curved.value, closeTo(0.2, precisionErrorTolerance));
|
||||||
|
tick(const Duration(milliseconds: 30));
|
||||||
|
expect(curved.value, closeTo(0.3, precisionErrorTolerance));
|
||||||
|
tick(const Duration(milliseconds: 40));
|
||||||
|
expect(curved.value, closeTo(0.4, precisionErrorTolerance));
|
||||||
|
tick(const Duration(milliseconds: 50));
|
||||||
|
expect(curved.value, closeTo(0.5, precisionErrorTolerance));
|
||||||
|
tick(const Duration(milliseconds: 60));
|
||||||
|
expect(curved.value, closeTo(0.6, precisionErrorTolerance));
|
||||||
|
tick(const Duration(milliseconds: 70));
|
||||||
|
expect(curved.value, closeTo(0.7, precisionErrorTolerance));
|
||||||
|
tick(const Duration(milliseconds: 80));
|
||||||
|
expect(curved.value, closeTo(0.8, precisionErrorTolerance));
|
||||||
|
tick(const Duration(milliseconds: 90));
|
||||||
|
expect(curved.value, closeTo(0.9, precisionErrorTolerance));
|
||||||
|
tick(const Duration(milliseconds: 100));
|
||||||
|
expect(curved.value, closeTo(1.0, precisionErrorTolerance));
|
||||||
|
controller.reverse();
|
||||||
|
tick(const Duration(milliseconds: 110));
|
||||||
|
expect(curved.value, closeTo(1.0, precisionErrorTolerance));
|
||||||
|
tick(const Duration(milliseconds: 120));
|
||||||
|
expect(curved.value, closeTo(0.8, precisionErrorTolerance));
|
||||||
|
tick(const Duration(milliseconds: 130));
|
||||||
|
expect(curved.value, closeTo(0.6, precisionErrorTolerance));
|
||||||
|
tick(const Duration(milliseconds: 140));
|
||||||
|
expect(curved.value, closeTo(0.4, precisionErrorTolerance));
|
||||||
|
tick(const Duration(milliseconds: 150));
|
||||||
|
expect(curved.value, closeTo(0.2, precisionErrorTolerance));
|
||||||
|
tick(const Duration(milliseconds: 160));
|
||||||
|
expect(curved.value, closeTo(0.0, precisionErrorTolerance));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('ReverseAnimation running with different forward and reverse durations.', () {
|
||||||
|
final AnimationController controller = AnimationController(
|
||||||
|
duration: const Duration(milliseconds: 100),
|
||||||
|
reverseDuration: const Duration(milliseconds: 50),
|
||||||
|
vsync: const TestVSync(),
|
||||||
|
);
|
||||||
|
final ReverseAnimation reversed = ReverseAnimation(
|
||||||
|
CurvedAnimation(
|
||||||
|
parent: controller,
|
||||||
|
curve: Curves.linear,
|
||||||
|
reverseCurve: Curves.linear,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
controller.forward();
|
||||||
|
tick(const Duration(milliseconds: 0));
|
||||||
|
tick(const Duration(milliseconds: 10));
|
||||||
|
expect(reversed.value, closeTo(0.9, precisionErrorTolerance));
|
||||||
|
tick(const Duration(milliseconds: 20));
|
||||||
|
expect(reversed.value, closeTo(0.8, precisionErrorTolerance));
|
||||||
|
tick(const Duration(milliseconds: 30));
|
||||||
|
expect(reversed.value, closeTo(0.7, precisionErrorTolerance));
|
||||||
|
tick(const Duration(milliseconds: 40));
|
||||||
|
expect(reversed.value, closeTo(0.6, precisionErrorTolerance));
|
||||||
|
tick(const Duration(milliseconds: 50));
|
||||||
|
expect(reversed.value, closeTo(0.5, precisionErrorTolerance));
|
||||||
|
tick(const Duration(milliseconds: 60));
|
||||||
|
expect(reversed.value, closeTo(0.4, precisionErrorTolerance));
|
||||||
|
tick(const Duration(milliseconds: 70));
|
||||||
|
expect(reversed.value, closeTo(0.3, precisionErrorTolerance));
|
||||||
|
tick(const Duration(milliseconds: 80));
|
||||||
|
expect(reversed.value, closeTo(0.2, precisionErrorTolerance));
|
||||||
|
tick(const Duration(milliseconds: 90));
|
||||||
|
expect(reversed.value, closeTo(0.1, precisionErrorTolerance));
|
||||||
|
tick(const Duration(milliseconds: 100));
|
||||||
|
expect(reversed.value, closeTo(0.0, precisionErrorTolerance));
|
||||||
|
controller.reverse();
|
||||||
|
tick(const Duration(milliseconds: 110));
|
||||||
|
expect(reversed.value, closeTo(0.0, precisionErrorTolerance));
|
||||||
|
tick(const Duration(milliseconds: 120));
|
||||||
|
expect(reversed.value, closeTo(0.2, precisionErrorTolerance));
|
||||||
|
tick(const Duration(milliseconds: 130));
|
||||||
|
expect(reversed.value, closeTo(0.4, precisionErrorTolerance));
|
||||||
|
tick(const Duration(milliseconds: 140));
|
||||||
|
expect(reversed.value, closeTo(0.6, precisionErrorTolerance));
|
||||||
|
tick(const Duration(milliseconds: 150));
|
||||||
|
expect(reversed.value, closeTo(0.8, precisionErrorTolerance));
|
||||||
|
tick(const Duration(milliseconds: 160));
|
||||||
|
expect(reversed.value, closeTo(1.0, precisionErrorTolerance));
|
||||||
|
});
|
||||||
|
|
||||||
test('TweenSequence', () {
|
test('TweenSequence', () {
|
||||||
final AnimationController controller = AnimationController(
|
final AnimationController controller = AnimationController(
|
||||||
vsync: const TestVSync(),
|
vsync: const TestVSync(),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user