Flutter 学习之旅(三十七) Flutter 动画(四)
2020-09-23 本文已影响0人
Tsm_2020
前面介绍AnimatedWidget的时候虽然省略了一部分代码,但是如果自己封装动画的话,还是非常麻烦,这就需要引入新的封装,ImplicitlyAnimatedWidget , ImplicitlyAnimatedWidgetState,
ImplicitlyAnimatedWidget
abstract class ImplicitlyAnimatedWidget extends StatefulWidget {
/// Initializes fields for subclasses.
///
/// The [curve] and [duration] arguments must not be null.
const ImplicitlyAnimatedWidget({
Key key,
this.curve = Curves.linear,
@required this.duration,
this.onEnd,
}) : assert(curve != null),
assert(duration != null),
super(key: key);
/// The curve to apply when animating the parameters of this container.
final Curve curve;
/// The duration over which to animate the parameters of this container.
final Duration duration;
/// Called every time an animation completes.
///
/// This can be useful to trigger additional actions (e.g. another animation)
/// at the end of the current animation.
final VoidCallback onEnd;
@override
ImplicitlyAnimatedWidgetState<ImplicitlyAnimatedWidget> createState();
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties.add(IntProperty('duration', duration.inMilliseconds, unit: 'ms'));
}
}
ImplicitlyAnimatedWidget 是一个抽象类,入参包括druation curve 和一个回调函数 onEnd
ImplicitlyAnimatedWidgetState
abstract class ImplicitlyAnimatedWidgetState<T extends ImplicitlyAnimatedWidget> extends State<T> with SingleTickerProviderStateMixin<T> {
/// The animation controller driving this widget's implicit animations.
@protected
AnimationController get controller => _controller;
AnimationController _controller;
/// The animation driving this widget's implicit animations.
Animation<double> get animation => _animation;
Animation<double> _animation;
@override
void initState() {
super.initState();
_controller = AnimationController(
duration: widget.duration,
debugLabel: kDebugMode ? widget.toStringShort() : null,
vsync: this,
);
_controller.addStatusListener((AnimationStatus status) {
switch (status) {
case AnimationStatus.completed:
if (widget.onEnd != null)
widget.onEnd();
break;
case AnimationStatus.dismissed:
case AnimationStatus.forward:
case AnimationStatus.reverse:
}
});
_updateCurve();
_constructTweens();
didUpdateTweens();
}
///更新控件
@override
void didUpdateWidget(T oldWidget) {
super.didUpdateWidget(oldWidget);
if (widget.curve != oldWidget.curve)
_updateCurve();
_controller.duration = widget.duration;
if (_constructTweens()) {
forEachTween((Tween<dynamic> tween, dynamic targetValue, TweenConstructor<dynamic> constructor) {
_updateTween(tween, targetValue);
return tween;
});
_controller
..value = 0.0
..forward();
didUpdateTweens();
}
}
//更新curve
void _updateCurve() {
if (widget.curve != null)
_animation = CurvedAnimation(parent: _controller, curve: widget.curve);
else
_animation = _controller;
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
//是否需要启动动画,
bool _shouldAnimateTween(Tween<dynamic> tween, dynamic targetValue) {
return targetValue != (tween.end ?? tween.begin);
}
//更新tween
void _updateTween(Tween<dynamic> tween, dynamic targetValue) {
if (tween == null)
return;
tween
..begin = tween.evaluate(_animation)
..end = targetValue;
}
//创建Tween
bool _constructTweens() {
bool shouldStartAnimation = false;
forEachTween((Tween<dynamic> tween, dynamic targetValue, TweenConstructor<dynamic> constructor) {
if (targetValue != null) {
tween ??= constructor(targetValue);
if (_shouldAnimateTween(tween, targetValue))
shouldStartAnimation = true;
} else {
tween = null;
}
return tween;
});
return shouldStartAnimation;
}
@protected
void forEachTween(TweenVisitor<dynamic> visitor);
@protected
void didUpdateTweens() { }
}
这里值得分析的方法是initState
_controller = AnimationController(
duration: widget.duration,
debugLabel: kDebugMode ? widget.toStringShort() : null,
vsync: this,
);
_controller.addStatusListener((AnimationStatus status) {
switch (status) {
case AnimationStatus.completed:
if (widget.onEnd != null)
widget.onEnd();
break;
case AnimationStatus.dismissed:
case AnimationStatus.forward:
case AnimationStatus.reverse:
}
});
_updateCurve();
_constructTweens();
didUpdateTweens();
先创建AnimationController ,添加状态监听,如果动画执行完毕,并且有onEnd 回调,则执行onEnd回调,更新Curve ,如果设置了Curve ,通过Curve 创建Animation,如果没设置,则将AnimationController 变为Animation<double>,_constructTweens 这个方法是最有意思的方法,
//是否会启用动画
bool _constructTweens() {
// 默认不启动
bool shouldStartAnimation = false;
//初始化Tween ,tween的职责就是规范动画的初始状态和结束状态,
//他的其实状态分为两种情况,
//1.tween 为空的时候,使用constructor 创建 这个是用户自定义的,begin 和end 用户定义
//2.tween不为空,更新tween,而他的开始值begin是 tween.transform(animation.value),end 是targetValue 也是用户定义的
//这也就解释了ImplicitlyAnimatedWidgetState中_updateTween 这个方法的作用
forEachTween((Tween<dynamic> tween, dynamic targetValue, TweenConstructor<dynamic> constructor) {
if (targetValue != null) {
tween ??= constructor(targetValue);
if (_shouldAnimateTween(tween, targetValue))
shouldStartAnimation = true;
} else {
tween = null;
}
return tween;
});
return shouldStartAnimation;
}
改变背景色例子
class TsmColorAnimatedDecorated extends ImplicitlyAnimatedWidget {
final BoxDecoration decoration;
final Widget child;
TsmColorAnimatedDecorated({
Key key,
@required this.decoration,
this.child,
Curve curve = Curves.linear, //动画曲线
@required Duration duration, // 正向动画执行时长
}) : super(
key: key,
curve: curve,
duration: duration,
);
@override
ImplicitlyAnimatedWidgetState<ImplicitlyAnimatedWidget> createState()=> _TsmColorAnimatedDecoratedState();
}
class _TsmColorAnimatedDecoratedState extends ImplicitlyAnimatedWidgetState<TsmColorAnimatedDecorated> {
DecorationTween _tween;
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return DecoratedBox(
decoration: _tween.transform(animation.value),
child: widget.child,
);
}
@override
void forEachTween(visitor) {
_tween = visitor(_tween, widget.decoration,
(value) => DecorationTween(begin: value));
}
}
使用了ImplicitlyAnimatedWidget 后代码就非常简单了,
Flutter 还定义了其他动画属性,方便大家使用
AnimatedPadding 在padding发生变化时会执行过渡动画到新状态
AnimatedPositioned 配合Stack一起使用,当定位状态发生变化时会执行过渡动画到新的状态。
AnimatedOpacity 在透明度opacity发生变化时执行过渡动画到新状态
AnimatedAlign 当alignment发生变化时会执行过渡动画到新的状态。
AnimatedContainer 当Container属性发生变化时会执行过渡动画到新的状态。
AnimatedDefaultTextStyle 当字体样式发生变化时,子组件中继承了该样式的文本组件会动态过渡到新样式。
我学习flutter的整个过程都记录在里面了
https://www.jianshu.com/c/36554cb4c804
最后附上demo 地址