Flutter复杂动画不会写?来看看flutter_steps_animation

1,429 阅读4分钟

写在前面

简单动画开发,这部分内容是我对Flutter动画相关API的分析和思考,以及为什么会有flutter_steps_animation

只想学习flutter_steps_animation的读者可以直接翻到 复杂动画开发 开始阅读

简单动画开发

官方示例

class Spinner extends StatefulWidget {
  @override
  _SpinnerState createState() => _SpinnerState();
}

class _SpinnerState extends State<Spinner> with SingleTickerProviderStateMixin {
  AnimationController _controller;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
      duration: const Duration(seconds: 10),
      vsync: this,
    );
  }

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

  @override
  Widget build(BuildContext context) {
    return AnimatedBuilder(
      animation: _controller,
      child: Container(
        width: 200.0,
        height: 200.0,
        color: Colors.green,
        child: const Center(
          child: Text('Wee'),
        ),
      ),
      builder: (BuildContext context, Widget child) {
        return Transform.rotate(
          angle: _controller.value * 2.0 * math.pi,
          child: child,
        );
      },
    );
  }
}

效果如图

官方demo

除了AnimatedBuilder之外,还有

等等,被进一步封装的Widget

看上去还挺强大,甚至还想扣一波666?但是现实世界真的这么简单吗?

Widget功能单一

有没有注意到,以上所有这些Widget都只能完成一个单独的动画?

从上面的例子扩展,如果我还想要颜色同步(同步的意思是指多个动画使用相同的曲线,相同的周期)变化,还想要周期性的缩放,甚至还想改下文字的透明度?就像这样:

修改后demo

读者可以自行尝试实现这样的动画,我的结论是:如果写法不做大的调整,可读性/可维护性会非常差

问题在哪

一个Widget只做一件事,这是Flutter framework 设计的基本理念之一,这当然很好,它带来了非常棒的分层设计,以及小白用户的较好体验,但是矛盾也发生在这里:

框架设计者与我们这些App开发者所理解的“一件事”是不一样的

对App开发者而言,在业务逻辑上做一件事,实现多个完全不同步的动画,也只是一件事,但官方现阶段显然没有想好如何提供这样的Widget,这也是我开发flutter_steps_animation的原因。

所以到正题了,我来介绍我做了哪些工作😁

复杂动画开发

实际的例子

这是我所开发的一个弹窗动画

整个动画只使用了一个AnimationBuilder

当然工程代码就不能给你们看了233

回来看看刚才的demo

修改后demo

来看看使用flutter_steps_animation框架后的代码实现:

class Spinner extends StatefulWidget {
  @override
  _SpinnerState createState() => _SpinnerState();
}

class _SpinnerState extends State<Spinner> with SingleTickerProviderStateMixin {
  StepsAnimation stepsAnimation;

  @override
  void initState() {
    super.initState();
    final builder = StepsAnimationBuilder();
    //可以添加多个OneStepAnimationBuilder,总持续时间为每个步骤相加
    builder.addStepBuilder(_multipleAnimationBuilder());
    stepsAnimation = builder.animation(this);
  }

  OneStepAnimationBuilder _multipleAnimationBuilder() {
    //每一个step可以添加各种各样,乃至相同key的动画
    //但是一个step只有一个创建Widget的方法
    //如果动画有widget层面的较大变化,应分为多个step
    final builder = MultipleAnimationBuilder(
        //该step持续时间10s
        duration: Duration(seconds: 10),
        buildAnimation: (context, map) {
          return Transform.scale(
            scale: map['scale'].value,
            child: Transform.rotate(
              angle: map['angle'].value,
              child: Container(
                width: 200.0,
                height: 200.0,
                color: map['color'].value,
                child: Center(
                  child: Opacity(
                    opacity: map['opacity'].value,
                    child: Text('Wee'),
                  ),
                ),
              ),
            ),
          );
        });
    builder
        //旋转角度动画,10s旋转一周
        .addAnimatable(
            animatable: Tween<double>(begin: 0, end: 2 * math.pi),
            from: Duration.zero,
            duration: Duration(seconds: 10),
            key: 'angle')
        //缩放动画,从1s开始,持续2s
        .addAnimatable(
            animatable: Tween<double>(begin: 0.8, end: 1.3),
            from: Duration(seconds: 1),
            duration: Duration(seconds: 2),
            key: 'scale')
        //第二个缩放动画,从7s开始,持续2s
        .addAnimatable(
            animatable: Tween<double>(begin: 1.3, end: 0.8),
            from: Duration(seconds: 7),
            duration: Duration(seconds: 2),
            key: 'scale')
        //颜色变化动画,10s从绿色变化到蓝灰色
        .addAnimatable(
            animatable: ColorTween(begin: Colors.green, end: Colors.blueGrey),
            from: Duration.zero,
            duration: Duration(seconds: 10),
            key: 'color')
        //文字透明度动画,从0s开始,持续5s,从不透明到透明
        .addAnimatable(
            animatable: Tween<double>(begin: 1, end: 0),
            from: Duration(seconds: 0),
            duration: Duration(seconds: 5),
            key: 'opacity')
        //第二个文字透明度动画,从5s开始,持续5s,从透明到不透明
        .addAnimatable(
            animatable: Tween<double>(begin: 0, end: 1),
            from: Duration(seconds: 5),
            duration: Duration(seconds: 5),
            key: 'opacity');
    return builder;
  }

  @override
  void dispose() {
    stepsAnimation.controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: () {
        stepsAnimation.controller.forward();
      },
      child: AnimatedBuilder(
        animation: stepsAnimation.controller,
        builder: stepsAnimation.builder,
      ),
    );
  }
}

对开发过动画的同学来说都很简单吧?

flutter_steps_animation

解决什么问题

解决复杂的、多步骤的动画,实现困难,以及可读性、可维护性差的问题

特性

本框架为你提供这些特性:

  • 分步骤(step)构建动画
  • 单步骤中添加多个完全独立的动画
  • 重复key值的动画(持续时间上不能相互覆盖)
  • 整合多个步骤在同一个Controller

不同的Builder

基于代码可读性的考虑,目前添加有这些Builder:

  • StepsAnimationBuilder

    这就是最核心的Builder了,使用方法见上面的demo代码

  • NoneAnimationBuilder

    事实上没有动画,只是想在多个步骤的动画间,拖拖时间

    事实上也可以通过延长上一个步骤的持续时间做到,主要目的是代码可读性更高

  • SingleAnimationBuilder

    只有单个动画的Step,在动画的某些简单步骤中,使用MultipleAnimation是杀鸡用牛刀,同样是为了代码可读性更高

  • MultipleAnimationBuilder

    核心的动画构造Builder,使用方法同样见上面的demo代码

用起来把少年

目前的版本是1.1.0,最新版本在pub查看

dependencies:
  flutter_steps_animation: ^1.1.0

最后

欢迎Star, 欢迎PR,有Issue也请务必提出

项目地址