Flutter 动画(Animation)官方文档地址:https://api.flutter.dev/flutter/animation/animation-library.html
一、隐式(全自动)动画
在Flutter中,隐式动画(Implicit Animation)是一种自动播放的动画效果,只要Widget发生改变,就会自动播放一个过渡动画。大部分以Animated开头的Widget都是带有隐式动画效果的控件。
- 只在自身属性产生变化时产生动画,当子控件属性发生变化时不会产生动画效果。
1. AnimatedContainer / AnimatedOpacity / AnimatedPadding
- duration:定义动画持续的时间。duration:Duration(seconds/milliseconds: VALUE),VALUE不能为小数,只能为整数。
- Curves:控制动画的播放效果。默认是线性(linear)
2. AnimatedSwitch
用于在不同的控件之间来回切换时填充动画效果。当AnimatedSwitch内部的子控件发生变化时,就会自动播放一个动画效果。
当在同一类控件之间切换时,不会产生动画效果。例如:两个内容不同的Text()来回切换,不会有动画效果。在这种情况下想要产生切换动画效果,需要添加关键字key:ValueKey("XXX")或者直接使用key:UniqueKey(),来标志其唯一性。
- transitionBuilder:来控制切换的动画效果。
AnimatedCrossFaded是优化版本的AnimatedSwitch
3. 补间动画(TweenAnimationBuilder)
在duration中规定的时间段内,会根据tween自动生成补间数值不停的调用builder,同时将该补间数值传递给builder中的value,从而渲染出动画效果。
- duration:动画持续的时间
- tween:单词between的简拼,需要定义 begin 和 end 属性,即动画开始和结束的两个关键帧,Flutter自动补全中间的。
- builder:需要return一个Widget,最终进行渲染。其中的value值会为tween中补间的数值。
其中tween中的begin只有在程序刚运行的时候,从begin运行到end,以后在改变数值,会从当前的数值运行到新定义的end,一开始定义的begin就不再使用。如果不传入begin,程序会自动将end的值赋值给begin。
二、显示控件
当我们需要创建无限循环的动画,而不是在两个值之间过渡的动画时,就需要使用显示控件来实现。大部分都是以Transaction结尾。例如RotationTransaction、SlideTransition、ScaleTransition。
1. RotationTransaction
需要定义一个Animated<double>来控制动画的运转。我们可以使用Flutter提供的AnimationController来实现。
- turns:控制旋转的属性,需要一个Animated<double>类型的值。
//SingleTickerProviderStateMixin:获取屏幕刷新的数据,满足不同设备的刷新频率
class _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin {
//定义AnimationController
AnimationController _controller;
//在该State开始时,初始化AnimationController
@override
void initState() {
super.initState();
_controller = AnimationController(//填充数值
duration: Duration(seconds: 1),//定义持续的时间
lowerBound:0.0,//定义填充数字的下限,默认为0.0
upperBound:1,//定义填充数值的上限,默认为1.0
vsync: this,//使用了SingleTickerProviderStateMixin,这里直接将vsync赋值为this
);
}
//在State结束时,释放,防止内存泄漏
@override
void dispose() {
// TODO: implement dispose
super.dispose();
_controller.dispose();
}
...
body: Center(
child: RotationTransition( //显示动画控件
turns: _controller,//使用controller进行控制
child: Center(
child: Container(
width: 200.0,
height: 200.0,
color: Colors.blueAccent,
child: Text(
'Hello World',
style: TextStyle(
fontSize: 50.0,
fontWeight: FontWeight.w800,
),
),
),
),
),
),
floatingActionButton: FloatingActionButton(
onPressed: (){
_controller.forward();//转动一次
//_controller.repeat();//不停的转动。0——>1;0——1
//_controller.repeat(reverse:true); //从0——>1;1——>0
//_controller.stop();//原地停止转动
//_controller.reset();//重置
},
...
}
}
改变AnimationController的范围除了可以使用lowerBound和UpperBound以外,还可以在XXXTransaction( )中使用controller的地方调用其drive方法。例如ScaleTransition:
child: ScaleTransition(
scale: _controller.drive(Tween(begin:0.5,end:2.0)),
//scale: Tween(begin:0.5,end:2.0)//另一种写法,方便叠加tween
// .chain(CurveTween(curve:Curves.elasticInOut))//添加一个弹性效果
// .chain(CurveTween(curve:Interval(0.8,1)))//在0.8到1区间内运动完成动画,其它时间不动
// .animate(_controller),
child: Center(
child: Container(
width: 200.0,
height: 200.0,
color: Colors.blueAccent,
child: Text(
'Hello World',
style: TextStyle(
fontSize: 50.0,
fontWeight: FontWeight.w800,
),
),
),
),
),
2. 自定义控件
使用AnimatedBuilder控件绘制。
AnimatedBuilder(
animation:_controller,//手动控制器
builder:(BuildContext context, Widget child){
return WIDGET(//child优化
),
}
child://此处的child为一个AnimatedBuilder的属性,将不需要更新的Widget放在这里,每次渲染时将跳过此部分内容
),
- animation:Animated<double>
- builder:每当animation发生变化时,builder就会每一帧重新绘制一次
可以通过AnimatedController来实现不是从0-1数值变化的动画,比如:将container的height从100——>300。
我们都知道,AnimationController中的控制器是一个在0——1之间变化的Animated<double>,对于Opacity这种在0——1之间变化的属性可以满足需求。但是对于像高度等变化范围并不局限于0——1之间的属性来说,就不能直接通过AnimationController来控制属性变化。我们可以通过Tween来确定一个区间,然后通过其evaluate函数来对AnimationController这个0-1之间的数值变换进行函数运算,从而可以得到一个不局限于0——1之间的区间。
child: Container(
width: 200.0,
height: Tween(begin: 200.0,end: 500.0).evaluate(_controller),
color: Colors.blueAccent,
child: Text(
'Hello World',
style: TextStyle(
fontSize: 50.0,
fontWeight: FontWeight.w800,
),
),
),
还可以有一下写法来让代码看起来更加整洁
...
Widget build(BuildContext context) {
final Animation heightAnimation = Tween(begin: 200.0,end: 300.0).animate(_controller);
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: RotationTransition(
turns: _controller,
child: Center(
child: Container(
width: 200.0,
height: heightAnimation.value,
color: Colors.blueAccent,
child: Text(
'Hello World',
style: TextStyle(
fontSize: 50.0,
fontWeight: FontWeight.w800,
),
),
),
),
),
),
floatingActionButton: FloatingActionButton(
onPressed: (){
_controller.stop();
},
tooltip: 'Increment',
child: Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
...
三、主动画(Hero)
在两个界面之间跳转的过程中对某元素添加动画效果。
对需要添加过渡效果的部分都添加上Hero标签
- tag:唯一的属性,只有需要过渡的两个部分具有相同的tag
四、CustomPainter