虽然Ffltter有大量令人惊叹的包来为你的应用程序创建动画效果,但也有一些内置的方法可以手动创建更微调的动画。
前提条件
对于我们的跨屏幕动画,我假设你已经知道如何创建基本的路线,只是为了保持简洁。如果您现在还不满意,可以查看Docs here]。
♪线性动画
我们动画的三个主要部分是控制我们的时间的‘Ticker’,注册我们的持续时间等参数的‘控制器’,然后我们想要改变的值。在呈现我们的小部件之前,在我们的initState
中,我们可以设置控制器的参数,设置它的方向,并添加一个侦听器来重置每次更改的小部件状态。
默认情况下,控制器会在我们为持续时间设置的时间内将更改从0移动到1,我们可以在监听程序中打印我们的Controler.value
来观看这种情况的发生。我们可以通过设置upper
或lower
属性来更改默认的起始值和结束值。
1[label main.dart]
2class MyHomePage extends StatefulWidget {
3 @override
4 _MyHomePageState createState() => _MyHomePageState();
5}
6
7class _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin {
8 AnimationController controller;
9
10 void initState() {
11 super.initState();
12 controller = AnimationController(
13 duration: Duration(seconds: 1),
14 vsync: this); // Links this controller to this widget so it won't run if the parent widget isn't rendered, to save on resources.
15
16 controller.forward();
17 controller.addListener(() => setState(() {}));
18 }
19}
要使用我们的动画,我们只需要将任何我们想要的设置,如不透明度,设置为Controler.value
。
1@override
2 Widget build(BuildContext context) {
3 return Scaffold(
4 body: Center(
5 child: Opacity(
6 opacity: controller.value,
7 child: Container(width: 50, height: 50, color: Colors.red),
8 ),
9 ),
10 );
11 }
曲线动画
代替无聊的线性动画,我们可以使用不同的曲线变化来控制控制器的变化。要做到这一点,我们可以使用CurvedAnimation
来创建一种包装在我们原来的控制器。这个包装器将接受它的父对象、我们的控制器和我们想要应用的曲线。当使用曲线动画时,除了0和1之外,我们不能有下限和上限,所以当我们应用它时,我们可以乘以它的值。在docs中有一个非常广泛的选项列表。
另一个有用的方法是addStatusListener
,它可以让我们了解它的生命周期。每当它的值达到其上限或下限时,我们的控制器都会触发完成或解除事件。我们可以使用它的Forward
和Reverse
方法来无限循环我们的动画。
1class _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin {
2 AnimationController controller;
3 Animation animation;
4
5 void initState() {
6 super.initState();
7 controller = AnimationController(
8 duration: Duration(seconds: 1),
9 vsync: this);
10 animation = CurvedAnimation(parent: controller, curve: Curves.slowMiddle);
11
12 controller.forward();
13 animation.addListener(() => setState(() {}));
14
15 controller.addStatusListener((status) {
16 if (status == AnimationStatus.completed) controller.reverse(from: 400);
17 else if (status == AnimationStatus.dismissed) controller.forward();
18 });
19 }
20
21 @override
22 Widget build(BuildContext context) {
23 return Scaffold(
24 body: Center(
25 child: Container(
26 margin: EdgeInsets.only(bottom: animation.value * 400),
27 width: 50,
28 height: 50,
29 color: Colors.red),
30 ),
31 );
32 }
33}
十几岁的孩子
不只是处理数值,我们还可以使用补间(Tweens,介于之间的缩写)处理其他范围的内容,比如颜色。就像我们的曲线动画一样,它将充当控制器或动画的包装,我们可以将颜色设置为它的值。
1[label main.dart]
2class _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin {
3 AnimationController controller;
4 Animation animation;
5 Animation changeColor;
6
7 void initState() {
8 super.initState();
9 controller =
10 AnimationController(duration: Duration(seconds: 1), vsync: this);
11 animation = CurvedAnimation(parent: controller, curve: Curves.slowMiddle);
12 changeColor = ColorTween(begin: Colors.red, end: Colors.blue).animate(animation);
13 controller.forward();
14 animation.addListener(() => setState(() {}));
15
16 controller.addStatusListener((status) {
17 if (status == AnimationStatus.completed) controller.reverse(from: 400);
18 else if (status == AnimationStatus.dismissed) controller.forward();
19 });
20 }
21
22 @override
23 Widget build(BuildContext context) {
24 return Scaffold(
25 body: Center(
26 child: Container(
27 margin: EdgeInsets.only(bottom: animation.value * 400),
28 width: 50,
29 height: 50,
30 color: changeColor.value),
31 ),
32 );
33 }
34}
跨屏
另一个很棒的技术是当我们想要在不同屏幕上的小部件之间进行转换时,使用Hero小部件。我们只需要用匹配的标签将每个组件包装在它自己的Hero小部件中。标签必须是唯一的,并且屏幕上任何时候都不能有多个标签。
1[label firstScreen]
2Widget build(BuildContext context) {
3 return Scaffold(
4 body: Column(
5 mainAxisAlignment: MainAxisAlignment.spaceBetween,
6 children: <Widget>[
7 SizedBox(height: 1),
8 Row(
9 mainAxisAlignment: MainAxisAlignment.spaceAround,
10 children: <Widget>[
11 Hero(tag: 'icon', child: Icon(Icons.add)),
12 ]),
13 Navbar()
14 ]));
15}
1[label secondScreen]
2Widget build(BuildContext context) {
3 return Scaffold(
4 body: Column(
5 mainAxisAlignment: MainAxisAlignment.spaceBetween,
6 children: <Widget>[
7 SizedBox(height: 1),
8 Row(
9 mainAxisAlignment: MainAxisAlignment.spaceAround,
10 children: <Widget>[
11 SizedBox(width: 1),
12 Hero(
13 tag: 'icon',
14 child: Icon(Icons.add, color: Colors.red, size: 75)),
15 ]),
16 Navbar()
17 ]),
18 );
19}
结论
对于过去的大多数前端网络技术,动画被留在了事后的思考中,但Ffltter团队在开发这个令人惊叹的框架时做了令人惊叹的工作,将动画留在了脑海中。在动画方面,我们真正触及的只是扑扑功能的皮毛。