Flutter 动画及示例

2,454 阅读4分钟

动画类型

动画类型:

  • 补间(Tween)动画

    “介于两者之间”的简称。在补间动画中,定义了开始点和结束点、时间线以及定义转换时间和速度的曲线。然后由框架计算如何从开始点过渡到结束点。

  • 基于物理的动画

    在基于物理的动画中,运动被模拟为与真实世界的行为相似。例如,当你掷球时,它在何处落地,取决于抛球速度有多快、球有多重、距离地面有多远。 类似地,将连接在弹簧上的球落下(并弹起)与连接到绳子上的球放下的方式也是不同。

动画常见表现形式

  • 动画列表或网格
    • 在网格或列表中添加或删除元素时应用动画
  • 共享元素转换(Hero)
    • 实现路由(页面)之间的共享元素过渡动画
  • 交错动画
    • 交错动画由一个动画序列或重叠的动画组成
    • 一个AnimationController控制所有动画

动画库中的一些主要类

Animation

动画系统的主要构件, 接收特定类型的值,该值可以在动画的生命周期内更改。可以从该对象读取动画的当前值,并监听该值的更改。

  • addListener(VoidCallback listener)

    监听Animation的值发生改变,如果要取动画当前的值,调用animation.value即可

  • addStatusListener(AnimationStatusListener listener)

    监听动画的状态改变, AnimationStatus有四种状态:dismissed、forward、reverse、completed

Animation<double> _animation = Tween(begin: 0.0, end: 350.0).animate(_animationController)
  ..addListener(() {
    this.setState(() {});
  })
  ..addStatusListener(
    (AnimationStatus status) {
      if (status == AnimationStatus.dismissed) {
        
      }...
    },
  );

AnimationController

创建一个动画,离不开控制器AnimationController。

  • 它会产生数值 0 -- 1 区间内的数值。
  • 控制动画 reverse(), forward(), animateTo(), stop()
  • 继承Animation<double>
  • 切记路由销毁时需要释放动画资源
AnimationController _animationController = AnimationController(
  duration: Duration(milliseconds: _duration),
  vsync: this,
);

Tween

如上面的代码, Tween接收begin和end两个参数,不仅可以是数值类型,也可以使用ColorTween, RectTween, BorderTween, AlignmentTween等接收Color, Rect, Border, Alignment等类型。

  • animate()

    animate方法传入Animation<double>,返回Animation<T>类型

  • 继承Animatable<T>

ColorTween _colorTween = ColorTween(
  begin: Color(0xFFC1C1C1),
  end: Color(0xFFC19426),
);

Curves

动画过程可以是匀速的、匀加速的或者先加速后减速等。通过Curve(曲线)来描述动画过程,我们把匀速动画称为线性的(Curves.linear), 而非匀速动画称为非线性的。

AnimationController _controller = AnimationController(
  duration: Duration(milliseconds: _duration),
  vsync: this,
);
Animation _curvedAnimation = CurvedAnimation(
  parent: _controller,
  curve: Curves.easeIn,
);
Animation<int> alpha = IntTween(begin: 0, end: 255).animate(_curvedAnimation);

Ticker

当创建AnimationController时, 需要传递一个vsync参数,它接收一个TickerProvider类型的对象,它的主要职责是创建Ticker.

通常我们使用State with SingleTickerProviderStateMixin,然后将State对象作为vsync的值。

class AnimationDialog extends StatefulWidget {
  @override
  AnimationDialogState createState() => AnimationDialogState();
}

class AnimationDialogState extends State<AnimationDialog>
    with SingleTickerProviderStateMixin {
    /// Animation Controller
    AnimationController _animationController = AnimationController(
        duration: Duration(milliseconds: 500),
        vsync: this,
    );
}

AnimatedWidget

使用Animation的时候,一般使用addListener更新当前的State

animation.addListener(() {
  setState(() {
    // 
  });
});

这就是AnimatedWidget的作用,AnimatedWidget继承StatefulWidget,并接收Listenable对象,而Animation是继承Listenable的。

使用方法:目标Widget继承AnimatedWidget,接收Animation参数。

class AnimatedLogo extends AnimatedWidget {
  /// Animation
  final Animation<double> animation;

  AnimatedLogo({Key key, this.animation})
      : super(key: key, listenable: animation);

  Widget build(BuildContext context) {
    return Center(
      child: Container(
        margin: EdgeInsets.symmetric(vertical: 10.0),
        height: animation.value,
        width: animation.value,
        child: FlutterLogo(),
      ),
    );
  }
}

AnimatedBuilder

在使用AnimatedWidget时,需要把目标Widget继承AnimatedWidget,这样写多少有点麻烦。如果封装一个Common类,接收animation对象和child,这就有了AnimatedWidget. Flutter中正是通过这样的方式封装了很多动画,如:FadeTransition、ScaleTransition、SizeTransition、RotationTransition等,很多时候都可以复用这些过渡类。

class GrowTransition extends StatelessWidget {
  GrowTransition({this.child, this.animation});

  final Widget child;
  final Animation<double> animation;

  Widget build(BuildContext context) {
    return new Center(
      child: new AnimatedBuilder(
          animation: animation,
          builder: (BuildContext context, Widget child) {
            return new Container(
                height: animation.value, 
                width: animation.value, 
                child: child
            );
          },
          child: child
      ),
    );
  }
}

Transform

Transform可以对子组件实现一些特效。

详情请参考官方介绍: book.flutterchina.club/chapter5/tr…

Container(
  color: Colors.black,
  child: new Transform(
    alignment: Alignment.topRight, //相对于坐标系原点的对齐方式
    transform: new Matrix4.skewY(0.3), //沿Y轴倾斜0.3弧度
    child: new Container(
      padding: const EdgeInsets.all(8.0),
      color: Colors.deepOrange,
      child: const Text('Apartment for rent!'),
    ),
  ),
);

  • rotate

    Transform.rotate可以对子组件进行旋转变换。

  • scale

    Transform.scale可以对子组件进行缩小或放大。

  • translate

    Transform.translate接收一个offset参数,可以在绘制时沿x、y轴对子组件平移一定的距离。

动画示例:

卡片弹出动画对话框

实现方案:高度在持续变化的对话框

扩展:对话框可以从底部、上部、中间等位置出现。

主要代码:

Container(
    height: _animation?.value ?? 0.0,
    alignment: Alignment.bottomCenter,
    child: SingleChildScrollView(
      child: Container(
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: <Widget>[
            _buildCardWidget(),
            _buildBottomWidget(),
          ],
        ),
      ),
    ),
  );

Animation Dialog

参考网址:

动画·《Flutter实战》:book.flutterchina.club/chapter9/an…

以上代码地址:github.com/smiling1990…