美文网首页Flutter
Flutter动画 1 - 实现一个最简单的动画

Flutter动画 1 - 实现一个最简单的动画

作者: 神经骚栋 | 来源:发表于2021-05-06 19:12 被阅读0次

    简述


    动画效果会对提升用户体验有着显著效果,一个漂亮的动画效果会让人眼前一亮,反正就是用这舒服,如果没有动画效果的加持,整个App会显的非常生涩难用.对于Flutter的动画,作为小白的我也是最近才刚刚接触,所以这里就说一下Futter中如何使用动画.

    这一篇主要是来说如何在Flutter中实现一个最简单的动画,这里假设你已经对Flutter有一些基本的了解或者有一定的开发经验了.

    构建基础


    这里我们就以控制 Container组件中 margin 为示例.

    首先我们先创建一个StatefulWidget有状态组件,同时需要混合 SingleTickerProviderStateMixin,至于 SingleTickerProviderStateMixin 是什么,后面我们会讲到,具体代码如下所示.

    class FlutterAnimationWidget extends StatefulWidget {
      @override
      _FlutterAnimationWidgetState createState() => _FlutterAnimationWidgetState();
    }
    
    class _FlutterAnimationWidgetState extends State<FlutterAnimationWidget> with SingleTickerProviderStateMixin {
        ****
    }
    

    我们接着创建一个受控Container,放在一个纵向布局中,具体如下所示.关于触发按钮的布局这里就不给过叙述了,不是本文的重点.

    Container(
      width: 200,
      height: 50,
      color: Colors.orangeAccent,
      margin: EdgeInsets.only(top: 0),
    ),
    

    构建动画


    我们声明动画控制器 _animationController 和margin的top状态属性 _marginTop ,改造Container让margin受控于 _marginTop 的值.具体代码如下所示.

      double _marginTop;
      AnimationController _animationController;
    
    Container(
    
      ...
    
      margin: EdgeInsets.only(top: _marginTop),
    
      ...
    ),
    

    紧接着我们就需要实现 _animationController 了, 假设我们设定动画运行时间为 300 毫秒,top的移动范围为[0, 50],那么具体代码就如下所示.

    _marginTop = 0;
    _animationController = AnimationController(duration: Duration(milliseconds: 300), vsync: this)..addListener(() {
      setState(() {
        _marginTop = _animationController.value * 50.0;
      });
    });
    

    这里就要对上述代码进行说明一下, 首先是 AnimationController 这个动画控制类,动画控制类中的只能在给定的时间内线性生成 0 → 1 的数值(默认区间),所以我们需要映射成我们想要的数值就需要使用到 value 这个属性映射成我们想要的值,这里就是简单映射成 0 → 50.

    那么我们可不可以改动默认区间呢?当然是可以的,这就需要使用到 lowerBoundupperBound 了.那么上面的代码就可以如下示例,两者的效果是一样的.

    _animationController = AnimationController(duration: Duration(milliseconds: 300), lowerBound: 0, upperBound: 50, vsync: this)..addListener(() {
      setState(() {
        _marginTop = _animationController.value;
      });
    });
    

    那么 代码中 vsync 指向 this,又是什么意思呢?这个简单的来说就是使用Ticker(而不是Timer)来驱动动画会防止屏幕外动画(动画的UI不在当前屏幕时,如锁屏时)消耗不必要的资源.还记得文章一开始状态组件混合 SingleTickerProviderStateMixin 吗?就是这个作用.如果一个组件内存在多个动画控制器,则可以使用 TickerProviderStateMixin .

    如下是摘抄 <<Flutter 实战>> 中的对Ticker一段说明.

    Flutter应用在启动时都会绑定一个SchedulerBinding,通过SchedulerBinding可以给每一次屏幕刷新添加回调,而Ticker就是通过SchedulerBinding来添加屏幕刷新回调,这样一来,每次屏幕刷新都会调用TickerCallback。使用Ticker(而不是Timer)来驱动动画会防止屏幕外动画(动画的UI不在当前屏幕时,如锁屏时)消耗不必要的资源,因为Flutter中屏幕刷新时会通知到绑定的SchedulerBinding,而Ticker是受SchedulerBinding驱动的,由于锁屏后屏幕会停止刷新,所以Ticker就不会再触发。

    对于 addListener() 方法,是给 AnimationController 添加监听回调,并且每一帧都会回调一次.这个我们在里面调用 setState() 方法就能实现对状态组件UI的重建了.

    当然了,除了 addListener() 帧监听回调方法之外,还有 addStatusListener() 动画状态的监听,监听的值如下所示.

    状态 说明
    AnimationStatus.dismissed 动画从 controller.reverse() 反向执行 结束时会回调此方法
    AnimationStatus.forward 执行 controller.forward() 会回调此状态
    AnimationStatus.reverse 执行 controller.reverse() 会回调此状态
    AnimationStatus.completed 动画从 controller.forward() 正向执行 结束时会回调此方法

    使用的话,示例如下所示.

    _animationController.addStatusListener((status) { 
      if (status == AnimationStatus.completed) print("动画完成");
    });
    

    构建完成动画控制器,但是不要忘了最后需要进行释放操作.代码如下所示.

    @override
    void dispose() {
      _animationController.dispose();
      super.dispose();
    }
    

    执行动画


    指定动画比较简单.主要有如下几个方法.

    方法 说明
    forward() 正向开始执行动画
    reverse() 反向开始执行动画
    reset() 重置动画到初始状态
    dispose() 取消/停止动画

    这里我们就只需要在触发时间中调用 forward() 就可以了.具体示例如下所示.

    void startEasyAnimation() {
      _animationController.forward();
    }
    

    示例代码


    由于这一篇比较简单,所以我们就把示例代码贴到文章中了,需要的拿走.

    class FlutterAnimationWidget extends StatefulWidget {
      @override
      _FlutterAnimationWidgetState createState() => _FlutterAnimationWidgetState();
    }
    
    class _FlutterAnimationWidgetState extends State<FlutterAnimationWidget> with TickerProviderStateMixin {
      AnimationController _animationController;
      double _marginTop;
    
      @override
      void initState() {
        super.initState();
        _marginTop = 0;
        _animationController = AnimationController(duration: Duration(milliseconds: 300), lowerBound: 0, upperBound: 50, vsync: this)..addListener(() {
          setState(() {
            _marginTop = _animationController.value;
          });
        });
        _animationController.addStatusListener((status) {
          if (status == AnimationStatus.completed) print("动画完成");
        });
      }
    
      void startEasyAnimation() {
        _animationController.forward();
      }
    
      @override
      void dispose() {
        _animationController.dispose();
        super.dispose();
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          body: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: <Widget>[
                Container(
                  width: 200,
                  height: 50,
                  color: Colors.orangeAccent,
                  margin: EdgeInsets.only(top: _marginTop),
                ),
                FlatButton(
                  onPressed: startEasyAnimation,
                  child: Text(
                    "点击执行最简单动画",
                    style: TextStyle(color: Colors.black38),
                  ),
                ),
              ],
            ),
          ),
        );
      }
    }
    

    结语


    OK,使用Flutter实现一个最简单的动画就说道这里了,接下来,骚栋会发布更多Flutter动画相关文章,欢迎关注评论,感谢大家了.

    相关文章

      网友评论

        本文标题:Flutter动画 1 - 实现一个最简单的动画

        本文链接:https://www.haomeiwen.com/subject/boikfltx.html