例子
所谓动画其实就是一系列连续变化的图片在极短的时间逐帧显示,在人眼看来就是动画了。这里我们举一个简单的例子先说明一下在Flutter中怎么运行一个动画:
import 'package:flutter/material.dart';
void main() {
runApp(MaterialApp(home: LogoAnim()));
}
class LogoAnim extends StatefulWidget {
_LogoAnimState createState() => _LogoAnimState();
}
class _LogoAnimState extends State<LogoAnim> with SingleTickerProviderStateMixin {
Animation<double> animation;
AnimationController controller;
@override
void initState() {
super.initState();
controller =
AnimationController(duration: const Duration(seconds: 2), vsync: this);
animation = Tween<double>(begin: 0, end: 300).animate(controller)
..addListener(() {
setState(() {
});
});
controller.forward(from: 0);
}
@override
Widget build(BuildContext context) {
return Center(
child: Container(
margin: EdgeInsets.symmetric(vertical: 10),
height: animation.value,
width: animation.value,
child: FlutterLogo(),
),
);
}
void dispose() {
controller.dispose();
super.dispose();
}
}
这个动画是在手机屏幕上由小到大渐变的显示一个Flutter标志。从上述代码中我们可以看到在Flutter中实现一个动画要做这么几件事。
- 首先施加动画的Widget是个StatefulWidget。其State要混入(mixin) SingleTickerProviderStateMixin。
- 在initState()里要加入和动画相关的初始化,这里我们实例化了两个类AnimationController和Animation。实例化AnimationController的时候我们传入了两个参数,一个是动画的时长,另一个是State自己,这里其实是利用到了混入的SingleTickerProviderStateMixin。实例化另一个Animation的时候,我们首先实例化的是一个Tween。这个类其实代表了从最小值到最大值的一个线性变化。所以实例化的时候要传入开始和结束值。然后调用animate()并传入之前的controller。这个调用会返回我们需要的Animation实例。显然我们需要知道动画的属性变化的时候的消息,所以这里会通过…addListener()给Animation实例注册回调。这个回调只做一件事,那就是调用setState()来更新UI。最后就是调用controller.forward()来启动动画。
- 注意在build()函数里我们构建widget的时候用到了animation.value。所以这里的链条就是动画在收到回调后会调用setState(),而从我们上篇文章知道setState之后在渲染流水线的构建阶段会走到build()来重建Widget。重建的时候就用到了发生变化以后的animation.value。这个一帧一帧的循环,我们的动画就动起来了。
- 最后在dispose()的时候要记得调用controller.dispose()释放资源。
接下来我们就深入Flutter源码来看一下动画是如何运行的。