Flutter动画: Animation动画基础(二)

我的博客

上一节我们简单介绍了一下animation的基本使用方法, 算是对flutter的动画有了个初步了解。

从之前的代码中可以看到,要让某个Widget动起来, 或者说要给Widget添加动画, 最少需要满足3个方面:

  • 设置控制器(controller)和动画数值变化(animation)区间, 并进行绑定,
  • 需要设置监听(listener), 然后在监听里更新数值(setState),
  • 将数值(animation.value)绑定到具体的Widget上, Widget自动根据数值变化更新UI

Flutter给了2个办法简化以上流程。

  • 1- 使用AnimatedWidget创建一个可重用动画的widget,
  • 2- 或者使用AnimatedBuilder从widget中分离出动画过渡。

AnimatedWidget

把需要动起来的Widget改写成AnimatedWidget子类, UI更新的工作就可以丢出去了。改写之前的例子, 让头像Header动起来,如下代码:

 

import 'package:flutter/material.dart';

class MyAnimatedWidgetDemo extends StatefulWidget {
  @override
  _MyAnimatedWidgetState createState() => _MyAnimatedWidgetState();
}

class _MyAnimatedWidgetState extends State<MyAnimatedWidgetDemo>
    with SingleTickerProviderStateMixin {
  AnimationController controller;
  Animation animation;

  @override
  void initState() {
    super.initState();
    controller = AnimationController(
      vsync: this,
      duration: Duration(milliseconds: 1000),
    );
    animation = Tween(begin: 0.0, end: 1.0).animate(controller);
    controller.repeat(reverse: true);
  }

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('继承AnimatedWidget助手类')),
      body: Center(child: AnimatedHeader(animation: animation)),
    );
  }
}

class AnimatedHeader extends AnimatedWidget {
  AnimatedHeader({Key key, Animation animation})
      : super(key: key, listenable: animation);
  
  @override
  Widget build(BuildContext context) {
    final Animation animation = listenable;
    return Container(
          width: 100 * (1 + animation.value),
          height: 100 * (1 + animation.value),
          child: Image.asset('assets/images/head.jpg'),
        );
  }
}

代码中没有给controller添加数值监听, 而是直接将数值变化(animation)直接传递给了动画组件AnimatedHeader。AnimatedHeader自行根据animation更新UI。其实工作原理和之前是一样的。

实际效果也和之前一样。

继承AnimatedWidget

ps:代码中没有对动画状态进行监听, 直接用了repeat方法做循环播放。controller.repeat(reverse: true);

AnimatedBuilder

上面代码存在的一个问题:AnimatedHeader里的child藏的比较深, 无法推广到普遍适用的范畴。 例如如果需要设计好几个相同动画效果的widget,需要一个一个单独再写一次。

更好的解决方案是将职责分离, 使用AnimatedBuilder可以使动画工具化。

分离child

把头像图片的widget先分离出来

 

class HeaderProfile extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Image.asset('assets/images/head.jpg');
  }
}

分离动画(实现类)

需要采用AnimatedBuilder来实现动画功能分离, 同时需要传入需要渲染的child和渲染方式(animation)。

AnimatedBuilder继承自抽象的AnimationWidget ,目的为了构建通用的AnimationWidget 实现类,不用每次使用AnimationWidget 都要创建一个实现类。

 

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

  final Widget child;
  final Animation<double> animation;

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

调用

需要将某个Widget赋予该动画效果时, 直接使用即可。

 

class MyAnimatedBuilderDemo extends StatefulWidget {
  @override
  _MyAnimatedBuilderDemoState createState() => _MyAnimatedBuilderDemoState();
}

class _MyAnimatedBuilderDemoState extends State<MyAnimatedBuilderDemo>
    with SingleTickerProviderStateMixin {
  AnimationController controller;
  Animation animation;

  @override
  void initState() {
    super.initState();
    controller = AnimationController(
      vsync: this,
      duration: Duration(milliseconds: 1000),
    );
    animation = Tween(begin: 100.0, end: 300.0).animate(controller);
    controller.repeat(reverse: true);
  }

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('采用AnimatedBuilder助手类')),
      body: Center(
        child: GrowTransition(
          animation: animation,
          child: HeaderProfile(),
        ),
      ),
    );
  }
}

效果如下:

 

AnimatedBuilder

系统预制的AnimatedBuilder实现类

Flutter中其实已经预制了不少AnimatedBuilder实现类, 如下所示(估计还会增加)。
具体可以直接使用Flutter的api。

  • AlignTransition
  • AnimatedAlign
  • AnimatedContainer
  • AnimatedCrossFade
  • AnimatedDefaultTextStyle
  • AnimatedIcon
  • AnimatedList
  • AnimatedModalBarrier
  • AnimatedOpacity
  • AnimatedPadding
  • AnimatedPositioned
  • AnimatedPositionedDirectional
  • AnimatedSwitcher
  • DecoratedBoxTransition
  • DefaultTextStyleTransition
  • FadeTransition
  • PositionedTransition
  • RelativePositionedTransition
  • RotationTransition
  • ScaleTransition
  • SizeTransition
  • SlideTransition
  • TweenAnimationBuilder

以上实现类大致分为2类(以下结论不一定正确, 没有全部测试过),

  • 显式动画: 以Transition结尾的类,例如OpacityTransitions,PositionedTransition等。 显式动画指的是需要手动设置动画的时间,运动曲线,取值范围的动画。将值传递给动画部件如: RotationTransition,最后使用一个AnimationController 控制动画的开始和结束。
  • 隐式动画: 以Animated开头的类, 例如AnimatedContainer, AnimatedOpacity等, 通过设置动画的起始值和最终值来触发。当使用 setState 方法改变部件的动画属性值时,框架会自动计算出一个从旧值过渡到新值的动画。

下面我们来拉几个实现类做例子

我们用两种方法做一个点击头像慢慢消隐/显现的动画

FadeTransition

 

import 'package:flutter/material.dart';

class MyFadeTransitionDemo extends StatefulWidget {
  @override
  _MyFadeTransitionDemoState createState() => _MyFadeTransitionDemoState();
}

class _MyFadeTransitionDemoState extends State<MyFadeTransitionDemo>
    with SingleTickerProviderStateMixin {
  Duration duration = Duration(milliseconds: 1000);
  AnimationController controller;
  Animation<double> opacityAnimation;

  @override
  void initState() {
    super.initState();
    controller = AnimationController(vsync: this, duration: duration);
    opacityAnimation = Tween<double>(begin: 0.2, end: 1.0).animate(controller);
  }

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('点击消隐-FadeTransition组件')),
      body: Center(
        child: FadeTransition(
          opacity: opacityAnimation,
          child: InkWell(
              onTap: () {
                if (controller.status == AnimationStatus.completed) {
                  controller.reverse();
                }
                if (controller.status == AnimationStatus.dismissed) {
                  controller.forward();
                }
              },
              child: Image.asset('assets/images/head.jpg')),
        ),
      ),
    );
  }
}

实现效果如下:

 

FadeTransition

AnimatedOpacity

上代码

 

import 'package:flutter/material.dart';

class MyAnimatedOpacityDemo extends StatefulWidget {
  @override
  _MyAnimatedOpacityDemoState createState() => _MyAnimatedOpacityDemoState();
}

class _MyAnimatedOpacityDemoState extends State<MyAnimatedOpacityDemo> {
  bool isActive = false;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('点击消隐-AnimatedOpacity组件')),
      body: Center(
        child: AnimatedOpacity(
          opacity: isActive ? 0.2 : 1.0,
          duration: Duration(milliseconds: 1000),
          child: InkWell(
              onTap: () {
                setState(() {
                  isActive = !isActive;
                });
              },
              child: Image.asset('assets/images/head.jpg')),
        ),
      ),
    );
  }
}

效果和上面是一样的。

AnimatedOpacity

要想构建漂亮的用户界面, 动画必不可少, 一些icon的点缀性小动画能极大增强用户体验。

Flutter的动画入门难度比Android低多了, 后面的章节我们再来练习几个动画实例。



作者:朋朋彭哥
链接:https://www.jianshu.com/p/a49020f42fe0
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值