Flutter开发·Flutter中动画的实现与使用

2,228 阅读4分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

Flutter中动画的核心类库是Animation,它并不是一个widget,Animation是一个抽象类,就相当于一个定时器,用来描述当前动画的开始,暂停,以及数值状态,与ui渲染没有任何关系,它不能直接控制ui页面上的组件的样式,组件只能通过获取它的状态来改变ui的状态。

AnimationController

介绍

AnimationController是动画的控制器,用来管理动画。 在AnimationController的构造方法中定义了如下主要参数:

  • duration:动画持续的时间
  • lowerBound:动画最小值,默认值0
  • upperBound:动画最大值,默认值1
  • vsync:为动画添加一个屏幕刷新的回调,每次屏幕刷新都会调用TickerCallback,目的是使用Ticker来驱动动画会防止屏幕外动画(动画的UI不在当前屏幕时,如锁屏时)消耗不必要的资源。因为Flutter中屏幕刷新时会通知Ticker,锁屏后屏幕会停止刷新,所以Ticker就不会再触发。最简单的做法是将SingleTickerProviderStateMixin添加到State的定义中。

使用

如下所示,声明一个AnimationController控制器对象,初始化中指定动画时长为5秒,不改变默认的最大最小值。为这个控制器添加listener监听,每次控制器的value发生改变时监听中都会收到回调。前面说Animation不负责ui的变化,所以这里要在监听中调用setState方法使得ui可以响应到控制器的数值变化。

代码
class _AnimationPageState extends State<AnimationPage> with SingleTickerProviderStateMixin{
  AnimationController _animationController;
  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    _animationController = AnimationController(duration:Duration(seconds: 5),vsync: this);
    _animationController.addListener(() {
      setState(() {
      });
    });
    _animationController.forward();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("动画"),),
      body: Center(
        child: Container(
          color: Colors.red,
          width: _animationController.value * 100,
          height: _animationController.value * 100,
        ),
      ),
    );
  }
}
效果

2021-10-06 16.10.30.gif

Tween

上面说使用AnimationController可以控制ui控件尺寸的变化,但是如果要是想让颜色等属性也发生变化应该如何实现呢?Flutter中提供了Tween对象来实现补间动画。 Tween类中提供了两个泛型参数beginend,也就是你可以指定你要进行变化的属性值,比如有很多Flutter中已经封装好的继承自Tween的补间动画类:ColorTween,SizeTween,BorderTween。当然如果想自定义的话,继承Tween实现lerp方法即可,该方法用来描述你想在动画时间内如何改变你所指定的属性值。 下面是直接使用ColorTween的一个例子,初始化tween后通过animate方法可以得到Animation对象,就可以在控件中通过获取Animation对象的value来不停地改变控件的属性,从而实现了一个控件由红到绿的变化。

代码
class _AnimationPageState extends State<AnimationPage> with SingleTickerProviderStateMixin{
  AnimationController _animationController;
  ColorTween _tween;
  Animation<Color> _animation;

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    _animationController = AnimationController(duration:Duration(seconds: 5),vsync: this);
    _tween = ColorTween(begin: Colors.red,end: Colors.green);
    _animationController.addListener(() {
      setState(() {
      });
    });
    _animation = _tween.animate(_animationController);
    _animationController.forward();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("动画"),),
      body: Center(
        child: Container(
          color: _animation.value,
          width: 300,
          height: 300,
        ),
      ),
    );
  }
}
效果

2021-10-06 16.39.49.gif

循环动画实现心跳效果

在一些情况的需求场景下,我们并不只是希望动画只执行一次,而是需要重复的进行循环动画,如下图实现一个心跳效果: 2021-10-06 17.16.29.gif

其实代码很简单,动画控制器中提供了一个addStatusListener方法来监听动画状态的变化,这里一共有4状态:

  • forward:动画开始正向执行
  • reverse:动画开始反向执行
  • completed:动画正向执行结束
  • dismissed:动画反向执行结束 根据这一方法,可以在监听中通过判断动画状态不断的正向、反向重复执行动画从而达到循环效果:
_animationController.addStatusListener((status) {
  if(_animationController.isCompleted){
    // 正向结束
    _animationController.reverse();
  }else if(_animationController.isDismissed){
    // 反向结束
    _animationController.forward();
  }
});

CurvedAnimation

上面说的都是线性的动画效果,CurvedAnimation则可以将动画过程定义为一个非线性曲线。AnimationController一样都是继承自Animation。Flutter中封装好了很多个曲线动画效果的Curve,开发者也可以自定义Curve效果。这里是一个bounceIn的例子:

_animationController = AnimationController(
    lowerBound: 0,
    upperBound: 1,
    duration: Duration(milliseconds: 800),
    vsync: this);
_animationController.addListener(() {
  setState(() {});
});
_curvedAnimation =
    CurvedAnimation(parent: _animationController, curve: Curves.bounceIn);
Image.asset(
  "assets/image/heart.png",
  width: _curvedAnimation.value * 300,
  height: _curvedAnimation.value * 300,
)

2021-10-06 17.45.59.gif

以上就是Flutter动画的简单实用,如有错误,还望指出。