> 文章列表 > Flutter Animation 动画

Flutter Animation 动画

Flutter Animation 动画

前言 :

在Flutter 中,做动画离不开这么一个类,那就是

Animation

这个类如往常一样,也是一个抽象类。

abstract class Animation<T> extends Listenable implements ValueListenable<T>

整个animation.dart 文件只有两百多行代码,其中包含了大量的注释。

这句话应该就可以看出Tween 的重要性了。

一 Animation 

了解一下这个类中的方法吧。

1 addListener

每当动画的值改变的时候,动画就会通知所有通过addListener 添加的监听器

2 addStatusListener

简而言之,就是动画的状态发生改变的时候,会通知所有通过addStatusListener 添加的监听器 。

  • 当动画的状态发生变化时,会通知所有通过 addStatusListener 添加的监听器。

  • 通常情况下,动画会从 dismissed 状态开始,表示它处于变化区间的开始点。

  • 举例来说,从 0.0 到 1.0 的动画在 dismissed 状态时的值应该是 0.0。

  • 动画进行的下一状态可能是 forward(比如从 0.0 到 1.0)或者 reverse(比如从 1.0 到 0.0)。

  • 最终,如果动画到达其区间的结束点(比如 1.0),则动画会变成 completed 状态。

3 removeListener

4 removeStatusListener

二  AnimationController

AnimationController 这个是继承Anamation的,里面有几个属性比较重要

值得注意的是 vsync 参数是必传的,Flutter的渲染闭环,每次渲染一帧画面之前都需要一个vsync信号,所以需要将SingleTickerProviderStateMixin 混入到State 类中才能初始化AnimationController

  AnimationController({double? value,this.duration,this.reverseDuration,this.debugLabel,this.lowerBound = 0.0,this.upperBound = 1.0,this.animationBehavior = AnimationBehavior.normal,required TickerProvider vsync,}) : assert(lowerBound != null),assert(upperBound != null),assert(upperBound >= lowerBound),assert(vsync != null),_direction = _AnimationDirection.forward {_ticker = vsync.createTicker(_tick);_internalSetValue(value ?? lowerBound);}

三  CurvedAnimation

这个类可以将AnimationController 和 Curve 结合起来,生成一个新的Animation 对象,有什么用呢?这个地方可以设置动画的速率。

它也是继承与Animation 

  CurvedAnimation({required this.parent,required this.curve,this.reverseCurve,}) : assert(parent != null),assert(curve != null) {_updateCurveDirection(parent.status);parent.addStatusListener(_updateCurveDirection);}

这个Curve 类型的对象有一些常量,可以直接调用

四 Tween

Tween 动画,又称为补间动画,有两个关键参数,begin 和 end

这玩意有什么用呢?

它主要是弥补 AnimationController 动画值只能为 double 类型的不足,,所以需要不同类型的变化值,那么就可以使用 Tween 。

类型多种多样

ColorTween  ,TextStyleTween,DecorationTween

 _colorAnimation =ColorTween(begin: primaryColor[100], end: primaryColor[900]).animate(_animationController!)..addListener(() {setState(() {});})..addStatusListener((status) {if (status == AnimationStatus.completed) {_animationController!.reverse();} else if (status == AnimationStatus.dismissed) {_animationController!.forward();}});
 //字体样式动画_textStyleAnimation = TextStyleTween(begin: TextStyle(color: Colors.orange, fontSize: 15),end: TextStyle(color: Colors.redAccent, fontSize: 30)).animate(_animationController!)..addListener(() {setState(() {});});
  _sizeAnimation =Tween(begin: Size(50, 50), end: Size(200, 200)).animate(_controller!)..addListener(() {setState(() {});})..addStatusListener((status) {if (status == AnimationStatus.completed) {_controller!.reverse();}});_demo2() {return Container(width: _sizeAnimation!.value.width,height: _sizeAnimation!.value.height,color: Colors.blueAccent,);}

五  使用

上面把该介绍的都介绍了,那么如何使用呢 ?

就是如下方式使用

import 'package:flutter/material.dart';class StudyAnimationPage extends StatefulWidget {const StudyAnimationPage({super.key});@overrideState<StudyAnimationPage> createState() => _StudyAnimationPageState();
}class _StudyAnimationPageState extends State<StudyAnimationPage>with SingleTickerProviderStateMixin {// 控制器AnimationController? _controller;CurvedAnimation? _curvedAnimation;// 动画Animation? _colorAnimation;// size 动画Animation<Size>? _sizeAnimation;@overridevoid initState() {// TODO: implement initStatesuper.initState();// 这个this 必须在方法里面写才不报错//1  初始化控制器_controller =AnimationController(vsync: this, duration: Duration(seconds: 2));// 2 这里可以设置执行动画的速率_curvedAnimation =CurvedAnimation(parent: _controller!, curve: Curves.easeInOutCirc);// 3 动画_colorAnimation =ColorTween(begin: Colors.redAccent, end: Colors.blueAccent).animate(_controller!)..addListener(() {setState(() {});})..addStatusListener((status) {if (status == AnimationStatus.completed) {_controller!.reverse();}});_sizeAnimation =Tween(begin: Size(50, 50), end: Size(200, 200)).animate(_controller!)..addListener(() {setState(() {});})..addStatusListener((status) {if (status == AnimationStatus.completed) {_controller!.reverse();}});}@overrideWidget build(BuildContext context) {print("build方法被调用了哈哈哈");return Scaffold(appBar: AppBar(title: Text("data"),),body: Container(width: double.infinity,height: double.infinity,child: Column(children: [_demo1(), _demo2()],),),floatingActionButton: FloatingActionButton(onPressed: () {_controller!.forward();},child: Icon(Icons.open_in_browser),),);}_demo1() {return Container(color: _colorAnimation!.value,width: 100,height: 100,);}_demo2() {return Container(width: _sizeAnimation!.value.width,height: _sizeAnimation!.value.height,color: Colors.blueAccent,);}
}

以上的代码还有一个问题,因为监听动画的值不停的改变,那么setState就会不停的执行,略显繁重,所以就可以 使用AnimatedWidget

六  AnimatedWidget

创建一个Widget 继承与AnimatedWidget ,然后Widget 的build 就不会随着动画值的改变一直执行,而是执行我们自己创建的Widget 中的build

class MyAnimatedBox extends AnimatedWidget {// 这里传递 一个animation 过来 传递给父类MyAnimatedBox(Animation anim) : super(listenable: anim);@overrideWidget build(BuildContext context) {Animation sizeAnimation = listenable as Animation;// TODO: implement buildreturn Container(width: sizeAnimation.value.width,height: sizeAnimation.value.height,color: Colors.blueAccent,);}
}

调用

_demo2() {

return MyAnimatedBox(_sizeAnimation!);

}

当然还有一些问题:

1 每一次使用都要创建一个类,这有点麻烦

2 如果我们构建的Widget 有子类,那么子类依然会重复的build

so 怎么解决呢 ??

七  AnimatedBuilder

1  不用显式的去添加帧监听器,然后再调用setState() 了,这个好处和AnimatedWidget是一样的。

2 更好的性能:因为动画每一帧需要构建的 widget 的范围缩小了,如果没有builder,setState()将会在父组件上下文中调用,这将会导致父组件的build方法重新调用;而有了builder之后,只会导致动画widget自身的build重新调用,避免不必要的rebuild。

  _demo3() {return AnimatedBuilder(animation: _sizeAnimation!,// child: child,builder: (BuildContext context, Widget? child) {return Container(width: _sizeAnimation!.value.width,height: _sizeAnimation!.value.height,color: Colors.blueAccent,);},);}

八  交织动画

什么事交织动画,顾名思义,就是多个动画交织在一起。通过多个Tween 生成多个Animation 对象。

// 动画

Animation? _colorAnimation;

// size 动画

Animation<Size>? _sizeAnimation;

// 透明度动画

Animation<double>? _opacityAnimation;

// 旋转动画

Animation<double>? _rotationAnim;

// 3 动画

_colorAnimation =

ColorTween(begin: Colors.redAccent, end: Colors.blueAccent)

.animate(_controller!)

..addListener(() {

setState(() {});

});

// ..addStatusListener((status) {

// if (status == AnimationStatus.completed) {

// _controller!.reverse();

// }

// });

_sizeAnimation =

Tween(begin: Size(50, 50), end: Size(200, 200)).animate(_controller!);

_opacityAnimation = Tween(begin: 0.3, end: 1.0).animate(_controller!);

_rotationAnim = Tween(begin: 0.0, end: 2 * pi).animate(_controller!);

  /* 多个动画交织在一起* 1 大小变化的动画* 2 颜色变化的动画* 3 透明度变化的动画* 4 旋转的动画* */_demo4() {return Opacity(opacity: _opacityAnimation!.value,child: Transform(alignment: Alignment.center,transform: Matrix4.rotationZ(_rotationAnim!.value),child: Container(color: _colorAnimation!.value,width: _sizeAnimation!.value.width,height: _sizeAnimation!.value.height,),),);}