Flutter - 内置动画 API

3,637 阅读3分钟

Flutter 内置动画

上面的那些一般用起来不是很方便,即便是 AnimatedBuilder 还是要指定参与动画变化的 widget 属性,为了进一步方便我们,官方在 AnimatedWidget 的基础上封装了下面这些内置动画库,就是 android 中的那些,当然更多一些:

  • SlideTransition - 自身倍数位移动画
  • AlignTransition - 没找到资料
  • PositionedTransition - 缩放动画,限定父布局只能是 stack
  • FadeTransition - 透明度动画
  • ScaleTransition - 缩放动画,这个是 android 中的那种缩放动画,可以指定中心点
  • SizeTransition - 宽高动画,限制是每次只能执行一个维度的动画,宽和高一起不行,那就是缩放动画了
  • RotationTransition - 旋转动画,特点是其数值是 0-1 之间的,旋转90度 = 0.25

这些 transition 的特点就是,用一个参数接受 animation 动画,child 写 widget


FadeTransition

FadeTransition 是透明度动画,参数就是一个 opacity 指定动画,child 写 widget 就没了

animationController = AnimationController(
  duration: Duration(milliseconds: 300),
  vsync: this,
);

animation = CurvedAnimation(parent: animationController, curve: Curves.bounceInOut);
animation = Tween(begin: 0.0, end: 1.0).animate(animationController);
  Widget build(BuildContext context) {
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          FadeTransition(
            opacity: animation,
            child: Container(
              margin: EdgeInsets.only(bottom: 20),
              width: 300,
              height: 300,
              color: Colors.blueAccent,
            ),
          ),
          RaisedButton(
            child: Text("放大"),
            onPressed: () {
              animationController?.forward();
            },
          ),
        ],
      ),
    );
  }
}

SlideTransition

SlideTransition 位移动画,使用 offset 来承载x,y轴数据。需要注意的是 Offset(0.0, 0.0) 中的数值都是 倍数,也就是说 SlideTransition 只支持按 widget 本身长宽的倍数位移,不支持具体的数值的位移操作

  void initState() {
    super.initState();

    animationController = AnimationController(
      duration: Duration(milliseconds: 300),
      vsync: this,
    );

    animation = Tween(begin: Offset(0.0, 0.0), end: Offset(1.0, 1.0))
        .animate(animationController);
  }
  Widget build(BuildContext context) {
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          SlideTransition(
            position: animation,
            child: Container(
              margin: EdgeInsets.only(bottom: 20),
              width: 100,
              height: 100,
              color: Colors.blueAccent,
            ),
          ),
          RaisedButton(
            child: Text("放大"),
            onPressed: () {
              animationController?.forward();
            },
          ),
        ],
      ),
    );
  }

PositionedTransition

PositionedTransition 缩放动画,必须在 stack 中使用,其缩放数值使用 RelativeRect 包裹,前后变化的是距 stack 父布局左上右下4个角的距离:

    animation = RelativeRectTween(
            begin: RelativeRect.fromLTRB(0, 0, 0, 0),
            end: RelativeRect.fromLTRB(50, 200, 50, 200))
        .animate(animationController);

大家看个图:

基本上就是这个思路了,限制有一些,下面是例子代码,不是上面 gif 的代码:

  Animation<RelativeRect> animation;
  AnimationController animationController;
  CurvedAnimation curve;

  void initState() {
    super.initState();

    animationController = AnimationController(
      duration: Duration(milliseconds: 300),
      vsync: this,
    );

    curve = CurvedAnimation(parent: animationController, curve: Curves.bounceInOut);
    
    animation = RelativeRectTween(
            begin: RelativeRect.fromLTRB(0, 0, 0, 0),
            end: RelativeRect.fromLTRB(50, 200, 50, 200))
        .animate(curve);
  }
  Widget build(BuildContext context) {
    return Center(
      child: Stack(
        children: <Widget>[
          PositionedTransition(
            rect: animation,
            child: Container(
              width: 300,
              height: 300,
              color: Colors.blueAccent,
            ),
          ),
          Positioned(
            top: 20,
            left: 20,
            child: RaisedButton(
              child: Text("放大"),
              onPressed: () {
                animationController?.forward();
              },
            ),
          ),
        ],
      ),
    );
  }

最后大家注意啊,RelativeRectTween begin 的数值能影响 widget 初始显示时的宽高大小


ScaleTransition

ScaleTransition 这才是传统的缩放动画,使用 alignment: Alignment.topLeft 指定缩放中心点

  Animation<double> animation;
  AnimationController animationController;
  CurvedAnimation curve;

  @override
  void initState() {
    super.initState();

    animationController = AnimationController(
      duration: Duration(milliseconds: 300),
      vsync: this,
    );

    curve =
        CurvedAnimation(parent: animationController, curve: Curves.bounceInOut);
    animation = Tween(
      begin: 1.0,
      end: 0.3,
    ).animate(curve);
  }

  Widget build(BuildContext context) {
    return Center(
      child: Stack(
        children: <Widget>[
          ScaleTransition(
            alignment: Alignment.topLeft,
            scale: animation,
            child: Container(
              width: 300,
              height: 300,
              color: Colors.blueAccent,
            ),
          ),
          Positioned(
            top: 20,
            left: 20,
            child: RaisedButton(
              child: Text("放大"),
              onPressed: () {
                animationController?.forward();
              },
            ),
          ),
        ],
      ),
    );
  }

SizeTransition

SizeTransition 宽高动画,限制是每次只能设定一个维度,不能宽和高一起,优点是宽高的变化不会引起内容的变形

axis: Axis.horizontal 就是指定动画作用与宽还是高

  Widget build(BuildContext context) {
    return Center(
      child: Stack(
        children: <Widget>[
          SizeTransition(
            axis: Axis.horizontal,
            sizeFactor: animation,
            child: Container(
              color: Colors.blueAccent,
              child: Icon(Icons.access_alarm, size: 300),
            ),
          ),
          Positioned(
            top: 20,
            left: 20,
            child: RaisedButton(
              child: Text("放大"),
              onPressed: () {
                animationController?.forward();
              },
            ),
          ),
        ],
      ),
    );
  }

RotationTransition

RotationTransition 旋转动画,特点是其数值是 0-1 之间的,旋转90度 = 0.25,依然可以设置原点

animation = Tween(
  begin: 0.0,
  end: 0.25,
).animate(curve);
  Widget build(BuildContext context) {
    return Center(
      child: Stack(
        children: <Widget>[
          RotationTransition(
            turns: animation,
            alignment: Alignment.center,
            child: Container(
              color: Colors.blueAccent,
              child: Icon(Icons.access_alarm, size: 300),
            ),
          ),
          Positioned(
            top: 20,
            left: 20,
            child: RaisedButton(
              child: Text("放大"),
              onPressed: () {
                animationController?.forward();
              },
            ),
          ),
        ],
      ),
    );
  }