Flutter(6)-动画(平移/缩放/旋转/Alpha)

本篇简单介绍下在Flutter中动画的实现以及示例平移、缩放、旋转和alpha动画。

1.动画相关知识介绍

在Flutter中Widget动画的组成有下面这些:

  • Animation:动画库中的一个核心类,它生成指导动画的值;
  • CurvedAnimation:将动画过程抽象为一个非线性曲线;
  • AnimationController:用来管理管理动画,常用的方法有forward():启动动画;reverse({double from}:倒放动画;reset():重置动画,将其设置到动画的开始位置;stop({ bool canceled = true }):停止动画。
  • Tween:AnimationController对象的范围从0.0到1.0。如果您需要不同的范围或不同的数据类型,则可以使用Tween来配置动画以生成不同的范围或数据类型的值。

在开发的过程中我们用AnimatedWidget与AnimatedBuilder可以更简练的方便我们对Widget进行动画的操作:

  • AnimatedWidget :使用它可以简化我们对动画的使用,在不使用AnimatedWidget的情况下需要手动调用动画的addListener(),并在回调中添加setState()才能看到动画效果,而使用AnimatedWidget我们就可以简化这一操作。
  • AnimatedBuilder: 用于构建动画的通用Widget,可以理解为是拆分动画的一个工具类,借助它我们可以将动画和widget进行分离,更加的好管理。
2.平移动画

我们直接用SlideTransition来一个平移动画示例:

import 'package:flutter/material.dart';

class BoxMoveAnimation extends StatefulWidget {
  BoxMoveAnimation({Key key, this.title}) : super(key: key);
  final String title;
  @override
  _BoxMoveAnimationState createState() => _BoxMoveAnimationState();
}

class _BoxMoveAnimationState extends State<BoxMoveAnimation> with TickerProviderStateMixin {

  AnimationController controller;
  Animation<Offset> animation;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text(widget.title)),
      body: Container(
        //SlideTransition 用于执行平移动画
        child: SlideTransition(
          position: animation,
          //将要执行动画的子view
          child: Container(
            width: 200,
            height: 200,
            color: Colors.red,
          ),
        ),
      ),
    );
  }

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    controller =
        AnimationController(duration: Duration(milliseconds: 2000), vsync: this);
    controller.addStatusListener((status) {
      if (status == AnimationStatus.completed) {
        //AnimationStatus.completed 动画在结束时停止的状态
        debugPrint('完成');
        controller.reverse();
      } else if (status == AnimationStatus.dismissed) {
        //AnimationStatus.dismissed 表示动画在开始时就停止的状态
        debugPrint('消失');
        controller.forward();
//        controller.dispose();
      }
    });
    animation =
        Tween(begin: Offset.zero, end: Offset(1.0, 0.0)).animate(controller);
    //开始执行动画
    controller.forward();
  }
}

看一下运行效果:


可以看到我们利用SlideTransition来做平移动画还是非常方便的,直接将animation设置为position属性就可以了。

3.缩放动画

直接用ScaleTransition来一个缩放动画的示例:

class BoxScaleAnimation extends StatefulWidget {
  BoxScaleAnimation({Key key, this.title}) : super(key: key);
  final String title;
  @override
  _BoxScaleAnimationState createState() => _BoxScaleAnimationState();
}

class _BoxScaleAnimationState extends State<BoxScaleAnimation> with TickerProviderStateMixin {
  AnimationController controller;

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    controller =
        AnimationController(duration: const Duration(seconds: 2), vsync: this);
    controller.addStatusListener((status) {
      if (status == AnimationStatus.completed) {
        print("completed");
        controller.reverse();
      } else if (status == AnimationStatus.dismissed) {
        print("dismissed");
        controller.forward();
      } else if (status == AnimationStatus.forward) {
        print("forward");
      } else if (status == AnimationStatus.reverse) {
        print("reverse");
      }
    });
    controller.forward();
  }

  @override
  Widget build(BuildContext context) {

    return Scaffold(
        appBar: AppBar(title: Text(widget.title)),
        body:Center(
          child: ScaleTransition(
            alignment: Alignment.center,
            scale: controller,
            child: Container(
              width: 200,
              height: 200,
              color: Colors.blue,
            ),
          ),
        ),
    );
  }

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

看一下运行效果:


4.旋转动画

RotationTransition来一个缩放动画的示例:

class BoxRotationAnimation extends StatefulWidget {
  BoxRotationAnimation({Key key, this.title}) : super(key: key);
  final String title;
  @override
  _BoxRotationAnimationState createState() => _BoxRotationAnimationState();
}

class _BoxRotationAnimationState extends State<BoxRotationAnimation> with TickerProviderStateMixin {

  AnimationController controller;
  Animation animation;

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    controller =
        AnimationController(duration: const Duration(seconds: 3), vsync: this);
    animation =
        Tween(begin: 0.0, end: 0.25).animate(controller);
    controller.addStatusListener((status) {
      if (status == AnimationStatus.completed) {
        print("completed");
        controller.reverse();
      } else if (status == AnimationStatus.dismissed) {
        print("dismissed");
        controller.forward();
      } else if (status == AnimationStatus.forward) {
        print("forward");
      } else if (status == AnimationStatus.reverse) {
        print("reverse");
      }
    });
    controller.forward();
  }

  @override
  Widget build(BuildContext context) {

    return Scaffold(
      appBar: AppBar(title: Text(widget.title)),
      body:Center(
        child: RotationTransition(
          alignment: Alignment.center,
          turns: animation,
          child: Container(
            width: 160,
            height: 160,
            color: Colors.green,
            child: Center(
              child: Text('Box'),
            ),
          ),
        ),
      ),
    );
  }

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

看一下运行效果:


旋转的动画中我们对动画的旋转的值进行了限制,默认的情况下旋转角度是360°,代码限制到了0.25也就是90°。就像前面说的Animation用来生成指导动画的值。

5.Opacity动画

FadeTransition来一个Opacity动画的示例:

class BoxOpacityAnimation extends StatefulWidget {
  BoxOpacityAnimation({Key key, this.title}) : super(key: key);
  final String title;
  @override
  _BoxOpacityAnimationState createState() => _BoxOpacityAnimationState();
}

class _BoxOpacityAnimationState extends State<BoxOpacityAnimation> with TickerProviderStateMixin {

  AnimationController controller;
  Animation animation;

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    controller =
        AnimationController(duration: const Duration(seconds: 2), vsync: this);
    animation =
        Tween(begin: 0.0, end: 1.0).animate(controller);
    controller.addStatusListener((status) {
      if (status == AnimationStatus.completed) {
        print("completed");
        controller.reverse();
      } else if (status == AnimationStatus.dismissed) {
        print("dismissed");
        controller.forward();
      } else if (status == AnimationStatus.forward) {
        print("forward");
      } else if (status == AnimationStatus.reverse) {
        print("reverse");
      }
    });
    controller.forward();
  }

  @override
  Widget build(BuildContext context) {

    return Scaffold(
      appBar: AppBar(title: Text(widget.title)),
      body:Center(
        child: FadeTransition(
          opacity: animation,
          child: Container(
            width: 160,
            height: 160,
            color: Colors.green,
            child: Center(
              child: Text('Box'),
            ),
          ),
        ),
      ),
    );
  }

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

看一下运行效果:


通过以上示例,我们会发现官方给我们封装的AnimatedWidget的确非常的方便,能够满足我们一般的动画需求,这里必须列一下Flutter中文网的动画介绍,里面的Hero 动画是一个非常好的示例,系统封装的动画Widget非常好用,可以自己去看看,Animatedbuilder的使用可以点击这里直接看到,文中所有的代码都可以在Github:BoxJ/Flutter-daydayup中下载,本篇代码的文件夹是boxdemo_006,欢迎一起交流!

推荐阅读更多精彩内容