Flutter中的动画功能强大且易于使用。接下来通过一个具体的实例,您将学到关于 Flutter 动画的一切。
难度:中级
今天,我们无法想象移动应用程序里面没有任何动画,当您从一页跳转到另一页时,或者点击一个按钮(如 InkWell)... 都会有一个动画。动画无处不在。
Flutter 使动画非常易于实现。
简而言之,这篇文章就是讨论这个话题的,尽管之前只有专家才能谈论,为了让这篇文章显得更有吸引力,我将挑战一下,仿照 Vitaly Rubtsov 在
Dribble 上传的一个 "Guillotine Menu (斩头菜单)"的一个动画效果,用 Flutter 一步步的实现这个效果。
image.png
本文的第一部分将介绍一下主要的理论知识和概念,第二部分将要实现上面的那个动画效果。
动画中的三大核心
为了能够实现动画效果,必须提供下面的三个元素:
Ticker
Animation
AnimationController
下面对这几个元素进行一下简单的介绍,更详细的在后面说明。
Ticker
简单来说,Ticker 这个类会在常规的一个时间区间里(大约每秒 60 次),发送一个信号,把这想象成你得手表,每秒都会滴答滴答的转。
当 Ticker 启动之后,自从第一个 tick 到来开始,每个到的 tick 都会回调 Ticker 的 callback 方法。
重要提示
尽管所有的 ticker 可能是在不同的时间里启动的,但是它们总是以同步的方式执行,这对于一些同步动画是很有用的。
Animation
Animation 其实没有什么特别的,只不过是一个可以随着动画的生命周期改变的一个值(有特定的类型),值随着动画时间的变化而变化的方式可以是线性的(例如1、2、3、4、5...),也可以更为复杂(参考后面的“Curves 曲线”)。
AnimationController
AnimationController 是一个可以控制一个或多个动画(开始,结束,重复)的控制器。换句话说,它让上面说的 Animation 值在一个指定的时间内,根据一个速度从一个最小值变化到最大。
AnimationController 类介绍
此类可控制动画。为了更加精确,我宁愿说“ 控制一个场景”,因为稍后我们将看到,几个不同的动画可以由同一个控制器来控制……
因此,使用这个AnimationController类,我们可以:
开始一个子动画,正向或者反向播放
停止一个子动画
为子动画设置一个具体的值
定义动画值的边界
以下伪代码可以展示这个类里面的不同的初始化参数
AnimationController controller = new AnimationController(
value: // the current value of the animation, usually 0.0 (= default)
lowerBound: // the lowest value of the animation, usually 0.0 (= default)
upperBound: // the highest value of the animation, usually 1.0 (= default)
duration: // the total duration of the whole animation (scene)
vsync: // the ticker provider
debugLabel: // a label to be used to identify the controller
// during debug session);
复制代码
在大多数情况下,初始化 AnimationController 时不会设计到 value,lowerBound,upperBound和debugLabel。
如何将 AnimationController 绑定到 Ticker 上
为了让动画正常工作,必须将 AnimationController 绑定到 Ticker 上。
通常情况下,你可以生成一个 Ticker 绑定到一个 StatefulWidget 实例上。
class _MyStateWidget extends State with SingleTickerProviderStateMixin {
AnimationController _controller; @override
void initState(){ super.initState();
_controller = new AnimationController(
duration: const Duration(milliseconds: 1000),
vsync: this,
);
} @override
void dispose(){
_controller.dispose(); super.dispose();
}
...
}
复制代码
第 2 行 这行代码告诉 Flutter ,你想要一个单 Ticker,这个 Ticker 链接到了 MyStateWidget 实例上。
8-10行
控制器的初始化。场景(子动画)的总持续时间设置为1000毫秒,并绑定到了 Ticker(vsync:this)。
隐式参数为:lowerBound = 0.0 和 upperBound = 1.0
16行
非常重要,当 MyStateWidget 这个页面的实例销毁时,您需要释放 controller。
TickerProviderStateMixin 还是 SingleTickerProviderStateMixin?
如果你有几个Animation Controller情况下,你想有不同的 Ticker, 只需要将 SingleTickerProviderStateMixin 替换为 TickerProviderStateMixin。
好的,我已经将控制器绑定到了 Ticker 上,但是它是工作的?
正是由于 ticker,每秒钟将会产生大约 60 个 tick,AnimationController 将根据 tick 在给定的时间里,线性的产生在最小值和最大值之间的值。
在这1000毫秒内产生的值的示例如下:
image.png
我们看到值在1000毫秒内从0.0(lowerBound)到1.0(upperBound)变化。生成了51个不同的值。
让我们扩展代码以查看如何使用它。
class _MyStateWidget extends State with SingleTickerProviderStateMixin {
AnimationController _controller; @override
void initState(){ super.initState();
_controller = new AnimationController(
duration: const Duration(milliseconds: 1000),
vsync: this,
);
_controller.addListener((){
setState((){});
});
_controller.forward();
} @override
void dispose(){
_controller.dispose(); super.dispose();
} @override
Widget build(BuildContext context){ final int percent = (_controller.value * 100.0).round(); return new Scaffold(
body: new Container(
child: new Center(
child: new Text('$percent%'),
),
),
);
}
}
复制代码
12 行 此行告诉控制器,每次其值更改时,我们都需要重建Widget(通过setState())
第15行
Widget初始化完成后,我们告诉控制器开始计数(forward() -> 从lowerBound到upperBound)
26行
我们检索控制器的值(_controller.value),并且在此示例中,此值的范围是0.0到1.0(也就是 0% 到 100%),我们得到此百分比的整数表达式,将其显示在页面的中心。
动画的概念
如我们所见, controller 可以以线性的方式返回彼此不同的小数值。
有的时候我们可能还有其他的需求如:
使用其