flutter学习--动画

对于android来说,动画分为属性、帧、补间动画三大类,巧妙的使用动画,能够极大的提升用户的体验。对于flutter来说,作为强大的UI开发工具,也提供了动画的基本实现。

一、动画常用的类

  • Animation : flutter动画库中的核心类,继承Listenable。插入用于指导与动画的值。提供了动画执行的监听方法,用于实时更新界面。
    在这里插入图片描述
  • AnimationController : 管理Animation ,可以通过duration设置动画执行的时间。通过vsync获取每一帧刷新的通知。
    在这里插入图片描述
  • CurvedAnimation : 定义动画的曲线,parent 为动画管理类AnimationController ,curve 为动画执行曲线状态
    在这里插入图片描述
  • Tween : 为动画对象插入一个范围值,从哪里开始,到哪里结束。begin为起点,end为终点。
    在这里插入图片描述

二、基础动画

1、实现方式1
class AnimPageRoute extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    // TODO: implement createState
    return AnimPageState();
  }
}
class AnimPageState extends State<AnimPageRoute>
    with SingleTickerProviderStateMixin {
  Animation _animation;
  AnimationController _controller;
  Tween<double> _tween;

  @override
  void initState() {
    // TODO: implement initState
    super.initState();

    //管理Animation
    _controller =
        AnimationController(duration: Duration(seconds: 3), vsync: this);

    //设置起点和终点
    _tween = Tween(begin: 0, end: 300);

    //得到动画  CurvedAnimation设置动画曲线
    _animation = _tween.animate(
        CurvedAnimation(parent: _controller, curve: Curves.fastOutSlowIn));

    //必须要setState才能刷新动画
    _animation.addListener(() {
      setState(() {});
    });

    //开始执行
    _controller.forward();
  }

这里一定要设置 _animation.addListener(),在回调中调用setState()方法,才能更新ui。效果如下:
在这里插入图片描述

2、实现方式2–AnimatedWidget

上面每次刷新界面都要调用addListener()回调,这样做很麻烦,所以系统为我们提供了AnimatedWidget类。它其实内部还是调用了这个回调,只是封装了一下,精简源码如下:
在这里插入图片描述

class AnimPageState extends State<AnimPageRoute>
    with SingleTickerProviderStateMixin {
  Animation _animation;
  AnimationController _controller;
  Tween<double> _tween;

  @override
  void initState() {
    // TODO: implement initState
    super.initState();

    //管理Animation
    _controller =
        AnimationController(duration: Duration(seconds: 3), vsync: this);

    //设置起点和终点
    _tween = Tween(begin: 0, end: 300);

    //得到动画  CurvedAnimation设置动画曲线
    _animation = _tween.animate(
        CurvedAnimation(parent: _controller, curve: Curves.fastOutSlowIn));

    //开始执行
    _controller.forward();
  }

  @override
  Widget build(BuildContext context) {
    return AnimPageWidget(_animation);
  }
}

//使用AnimatedWidget
class AnimPageWidget extends AnimatedWidget {

  AnimPageWidget(Animation animation) : super(listenable: animation);

  @override
  Widget build(BuildContext context) {
    Animation _animation = listenable;
    // TODO: implement build
    return Scaffold(
        appBar: AppBar(
          title: Text('动画执行AnimPageWidget'),
        ),
        body: Center(
          child: SizedBox(
            width: _animation.value,
            height: _animation.value,
            child: Image.asset('assets/images/ic_launcher.png'),
          ),
        ));
  }
}
3、实现方式3–AnimatedBuilder

源码如下:
在这里插入图片描述
两个必传参数

  • animation :要执行的动画
  • builder :在这里插入图片描述
    实现方式如下:
class AnimPageState extends State<AnimatedBuilder>
    with SingleTickerProviderStateMixin {
  Animation _animation;
  AnimationController _controller;
  Tween<double> _tween;

  @override
  void initState() {
    // TODO: implement initState
    super.initState();

    //管理Animation
    _controller =
        AnimationController(duration: Duration(seconds: 3), vsync: this);

    //设置起点和终点
    _tween = Tween(begin: 0, end: 300);

    //得到动画  CurvedAnimation设置动画曲线
    _animation = _tween.animate(
        CurvedAnimation(parent: _controller, curve: Curves.fastOutSlowIn));

    //开始执行
    _controller.forward();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('动画执行AnimatedBuilder'),
      ),
      body: AnimatedBuilder(
          animation: _animation,
          builder: (context, child) {
            return Center(
              child: SizedBox(
                width: _animation.value,
                height: _animation.value,
                child: Image.asset('assets/images/ic_launcher.png'),
              ),
            );
          }),
    );
  }
}
4、实现方式4–基础动画封装

这里实现缩放动画的封装

//缩放动画
class ScaleAnimWidget extends StatelessWidget {
  final Animation animation;
  final Widget childWidget;

  ScaleAnimWidget({@required this.animation, @required this.childWidget});

  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return AnimatedBuilder(
      animation: animation,
      builder: (context, child) {//这个child就是childWidget
        return SizedBox(
          width: animation.value,
          height: animation.value,
          child: child,
        );
      },
      child: childWidget,
    );
  }
}
5、实现方式5–同步动画

所谓的同步动画,就是多个动画效果一起执行,有点动画集的意思。以下代码实现了缩放和透明度的同步效果。

class AnimPageState extends State<AnimPageRoute>
    with SingleTickerProviderStateMixin {
  Animation _animation;
  AnimationController _controller;
  Tween<double> _tween;
  Tween<double> _opacity;

  @override
  void initState() {
    // TODO: implement initState
    super.initState();

    //管理Animation
    _controller =
        AnimationController(duration: Duration(seconds: 3), vsync: this);

    //设置起点和终点
    _tween = Tween(begin: 0, end: 300);

    //透明度变化
    _opacity = Tween(begin: 0, end: 1);

    //得到动画  CurvedAnimation设置动画曲线
    _animation = CurvedAnimation(parent: _controller, curve: Curves.linear);

    //开始执行
    _controller.forward();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('动画执行AnimatedBuilder封装'),
      ),
      body: AnimatedBuilder(
        animation: _animation,
        builder: (context, child) {
          return Center(
            child: Opacity(
              opacity: _opacity.evaluate(_animation),
              child: SizedBox(
                width: _tween.evaluate(_animation),
                height: _tween.evaluate(_animation),
                child: Image.asset('assets/images/ic_launcher.png'),
              ),
            ),
          );
        },
      ),
    );
  }
}

_tween控制大小变化,_opacity 控制透明度变化。

三、路由切换动画

路由管理中,页面可以设置切换动画,Material组件中提供了MaterialPageRoute组件。ios提供了CupertinoPageRoute,自定义中使用PageRouteBuilder实现。对于MaterialPageRoute,android页面跳转是从右往左切换界面的,ios是从下往上切换界面的。下面将使用PageRouteBuilder自定义界面切换动画。

class RoutePageA extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Scaffold(
      appBar: AppBar(
        title: Text('RoutePageA--pushNamed'),
      ),
      body: Center(
        child: RaisedButton(
          //异步等待结果回调
          onPressed: () {
            var result = Navigator.push(context, PageRouteBuilder(
                pageBuilder: (context, animation, secondaryAnimation) {
              return FadeTransition(
                opacity: animation,
                child: AnimPageRoute(),
              );
            }));
            print(result);
          },
          child: Text('跳转到下一界面启动动画'),
        ),
      ),
    );
  }
}

AnimPageRoute()就是下一个界面。

四、Hero动画

Hero是指在页面之间可以进行飞行的Widget,相当于转场动画,在路由切换时,有一个共同的widget可以在新旧路由间进行切换。代码实现如下:

class RoutePageA extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Scaffold(
      appBar: AppBar(
        title: Text('hero动画'),
      ),
      body: Center(
        child: InkWell(
          //异步等待结果回调
          onTap: () {
            var result =
                Navigator.push(context, MaterialPageRoute(builder: (context) {
              return RoutePageB();
            }));
            print(result);
          },
          child: Hero(
              tag: 'pic',
              child: ClipOval(
                  child: Container(
                width: 50,
                height: 50,
                color: Colors.red,
              ))),
        ),
      ),
    );
  }
}

class RoutePageB extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Scaffold(
        appBar: AppBar(
          title: Text('hero动画'),
        ),
        body: Center(
          child: Hero(
              tag: 'pic',
              child: ClipOval(
                  child: Container(
                width: 100,
                height: 100,
                color: Colors.red,
              ))),
        ));
  }
}

A路由到B路由,有一个Hero动画,其中执行动画的widget必须被Hero包裹起来,使用相同的tag。效果如下:
在这里插入图片描述

五、交织动画

比如我们要想实现一个动画序列,某个时间段执行什么样的动画,这样就可以借助交织动画实现。

  • 交织动画需要多个动画对象
  • 一个AnimationController控制所有的动画对象
  • 每一个动画对象都有个执行时间间隔
1、动画集
class Stagger extends StatelessWidget {
  AnimationController controller;

  //透明度、宽高动画
  Animation<double> opacity, width, height;

  //内边距动画
  Animation<EdgeInsets> padding;

  //圆角动画
  Animation<BorderRadius> border;

  //颜色渐变动画
  Animation<Color> color;

  Stagger({Key key, this.controller})
      : opacity = Tween(begin: 0.0, end: 1.0).animate(CurvedAnimation(
            parent: controller,
            curve: Interval(0.00, 0.125, curve: Curves.linear))),
        width = Tween(begin: 0.0, end: 100.0).animate(CurvedAnimation(
            parent: controller,
            curve: Interval(0.125, 0.250, curve: Curves.linear))),
        height = Tween(begin: 0.0, end: 100.0).animate(CurvedAnimation(
            parent: controller,
            curve: Interval(0.250, 0.350, curve: Curves.linear))),
        padding = EdgeInsetsTween(
                begin: EdgeInsets.only(bottom: 5),
                end: EdgeInsets.only(bottom: 15))
            .animate(CurvedAnimation(
                parent: controller,
                curve: Interval(0.350, 0.500, curve: Curves.linear))),
        border = BorderRadiusTween(
                begin: BorderRadius.circular(5), end: BorderRadius.circular(25))
            .animate(CurvedAnimation(
                parent: controller,
                curve: Interval(0.500, 0.750, curve: Curves.linear))),
        color = ColorTween(begin: Colors.blue, end: Colors.orange).animate(
            CurvedAnimation(
                parent: controller,
                curve: Interval(0.750, 1, curve: Curves.linear))),
        super(key: key);

  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return AnimatedBuilder(
      animation: controller,
      builder: (context, child) {
        return Container(
          padding: padding.value,
          alignment: Alignment.bottomCenter,
          child: Opacity(
            opacity: opacity.value,
            child: Container(
              width: width.value,
              height: height.value,
              decoration: BoxDecoration(
                color: color.value,
                borderRadius: border.value,
                border: Border.all(width: 3, color: Colors.blue),
              ),
            ),
          ),
        );
      },
    );
  }
}

定义若干动画,并指定每个动画执行的时间。

2、AnimationController
class StaggerF extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    // TODO: implement createState
    return StaggerS();
  }
}

class StaggerS extends State<StaggerF> with TickerProviderStateMixin {
  AnimationController _controller;

  //开始动画
  _startAnim() async {
    await _controller.forward().orCancel;
    await _controller.reverse().orCancel;
  }

  @override
  Widget build(BuildContext context) {
    // TODO: implement build
    return Scaffold(
      appBar: AppBar(
        title: Text('交织动画'),
      ),
      body: GestureDetector(
        onTap: () {
          _startAnim();
        },
        child: Center(
          child: Container(
            width: 300,
            height: 300,
            color: Colors.green,
            child: Stagger(controller: _controller),
          ),
        ),
      ),
    );
  }

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    _controller =
        AnimationController(duration: Duration(seconds: 3), vsync: this);
    _controller.addStatusListener((status) {
      switch (status) {
        case AnimationStatus.completed:
          _controller.reverse();
          break;
        case AnimationStatus.dismissed:
          _controller.forward();
          break;
        case AnimationStatus.forward:
        case AnimationStatus.reverse:
          break;
      }
    });
    _controller.forward();
  }
}

监听动画执行状态,循环执行动画。

3、效果

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值