Widget属性发生变化时会执行过渡动画的组件统称为”动画过渡组件“,而动画过渡组件最明显的一个特征就是它会在内部自管理AnimationController
。我们知道,为了方便使用者可以自定义动画的曲线、执行时长、方向等,在前面介绍过的动画封装方法中,通常都需要使用者自己提供一个AnimationController
对象来自定义这些属性值。但是,如此一来,使用者就必须得手动管理AnimationController
,这又会增加使用的复杂性。因此,如果也能将AnimationController
进行封装,则会大大提高动画组件的易用性。
自定义动画过渡组件
我们要实现一个AnimatedDecoratedBox
,它可以在decoration
属性发生变化时,从旧状态变成新状态的过程可以执行一个过渡动画。根据前面所学的知识,我们实现了一个AnimatedDecoratedBox1
组件:
/// @Author wywinstonwy
/// @Date 2022/1/16 10:15 上午
/// @Description:
import "package:flutter/material.dart";
class AnimatedDecoratedBox1 extends StatefulWidget {
final BoxDecoration decoration;
final Widget child;
final Duration duration;
final Curve ?curve;
final Duration? reverseDuration;
const AnimatedDecoratedBox1({
required this.decoration,
required this.child,
required this.duration,
this.curve,
this.reverseDuration});
@override
_AnimatedDecoratedBox1State createState() => _AnimatedDecoratedBox1State();
}
class _AnimatedDecoratedBox1State extends State<AnimatedDecoratedBox1> with SingleTickerProviderStateMixin{
AnimationController get controller=>_controller;
late AnimationController _controller;
late Animation<double> _animation;
late DecorationTween _tween;
@override
void initState() {
// TODO: implement initState
super.initState();
_controller = AnimationController(
duration: widget.duration,
reverseDuration: widget.reverseDuration,
vsync: this
);
_tween = DecorationTween(begin: widget.decoration);
_updateCurve();
}
void _updateCurve(){
_animation = CurvedAnimation(parent: _controller, curve: widget.curve!);
}
@override
void didUpdateWidget(covariant AnimatedDecoratedBox1 oldWidget) {
super.didUpdateWidget(oldWidget);
if(widget.curve != oldWidget) _updateCurve();
_controller.duration = widget.duration;
_controller.reverseDuration = widget.reverseDuration;
//正在执行过度动画
if(widget.decoration !=(_tween.end??_tween.begin)){
_tween
..begin =_tween.evaluate(_animation)
..end = widget.decoration;
_controller
..value=0.0
..forward();
}
}
@override
Widget build(BuildContext context) {
return AnimatedBuilder(
builder: (BuildContext context, Widget? child) {
return DecoratedBox(
decoration: _tween.animate(_animation).value);
},
animation: _animation,
child:widget.child,
);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
}
下面我们来使用AnimatedDecoratedBox1
来实现按钮点击后背景色从蓝色过渡到红色的效果:
class MyExcessiveAnimation extends StatefulWidget {
const MyExcessiveAnimation({Key? key}) : super(key: key);
@override
_MyExcessiveAnimationState createState() => _MyExcessiveAnimationState();
}
class _MyExcessiveAnimationState extends State<MyExcessiveAnimation> {
Color _decorationColor = Colors.blue;
var duration = Duration(seconds: 1);
// Curve curve = CurvedAnimation(parent: ) as Curve;
// Tween doubleTween = Tween<double>(begin: -200.0, end: 0.0);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: getAppBar("组件过度动画"),
body: Column(children: [
ElevatedButton(onPressed: (){
setState(() {
_decorationColor = Colors.red;
});
}, child: const Text('测试')),
Container(height: 44,
width: 200,
child: AnimatedDecoratedBox1(
decoration: BoxDecoration(color: _decorationColor),
child: ElevatedButton(onPressed: (){
setState(() {
_decorationColor = Colors.red;
});
},
child: const Text('AnimatedDecoratedBox',
style: TextStyle(color: Colors.black,fontSize: 14),
)
),
duration: duration,
curve: Curves.linear,
),
)
],)
,
);
}
}
点击后,按钮背景色会从蓝色向红色过渡,上图是过渡过程中的一帧,有点偏紫色,整个过渡动画结束后背景会变为红色。
demo完整代码:flutter_demo: flutter组件测试学习demo