写点什么

【Flutter 专题】106 图解 AnimatedWidget & AnimatedBuilder 动画应用

发布于: 2021 年 06 月 12 日
【Flutter 专题】106 图解 AnimatedWidget & AnimatedBuilder 动画应用

     小菜继续上一节中自定义的 ACEPageMenu 滑动菜单,详细介绍一下涉及到的 AnimatedBuilder 动画,在此之前需要先了解 AnimatedWidget

AnimatedWidget

     AnimatedWidget 是一个有状态的 StatefulWidget 小部件,通过指定 Listenable 更改值时重建小部件;AnimatedWidget 对于无状态的窗口小部件比较实用;含有众多子类动画,小菜会在之后的博客中慢慢学习;



源码分析

abstract class AnimatedWidget extends StatefulWidget {  const AnimatedWidget({    Key key,    @required this.listenable,  }) : super(key: key);
final Listenable listenable;
@protected Widget build(BuildContext context);
@override _AnimatedState createState() => _AnimatedState();
@override void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); properties.add(DiagnosticsProperty<Listenable>('animation', listenable)); }}
复制代码


     分析源码可得,AnimatedWidget 主要通过 Listenable 来监听小部件动画,通常是 AnimationChangeNotifier;通过重写 build() 方法来设置动画过程;并在 _AnimatedState 中设置状态的更新 setState()


     由此可见,AnimatedWidget 已封装好 setState() 状态更新模块,允许将调用中的动画代码中分离出 Widget,而无需单独维护一个 State 状态来保存动画;

案例尝试

     小菜尝试 AnimatedWidget 方式展示一个类似 ACEPageMenu 从顶部滑出的一个小动画效果;


class TopMenuWidget extends AnimatedWidget {  const TopMenuWidget({Key key, AnimationController controller}) : super(key: key, listenable: controller);
Animation<double> get _progress => listenable;
@override Widget build(BuildContext context) { return Transform.translate( offset: Offset(50, 100 * _progress.value), child: Opacity( opacity: _progress.value, child: Container( width: 300.0, height: 100.0, color: Colors.yellow, child: Center( child: GestureDetector( onTap: () => print('I am from AnimatedWidget !'), child: Icon(Icons.ac_unit, color: Colors.white)))))); }}
复制代码


AnimatedBuilder

      AnimatedBuilder 也是用于构建动画的通用 Widget,是渲染树中的一个独立的类,适用于要提取单独动画效果的较复杂的 Widget;可自动监听来自 Animation 对象的通知,无需手动调用 addListener()

源码分析

class AnimatedBuilder extends AnimatedWidget {  const AnimatedBuilder({    Key key,    @required Listenable animation,    @required this.builder,    this.child,  }) : super(key: key, listenable: animation);
final TransitionBuilder builder; final Widget child;
@override Widget build(BuildContext context) { return builder(context, child); }}
typedef TransitionBuilder = Widget Function(BuildContext context, Widget child);
复制代码


      分析源码可得,AnimatedBuilder 继承自 AnimatedWidget,只需构造窗口小部件并将其传递给构建器函数即可;其中 TransitionBuilder 在每次动画更改值时调用;其中 child 比较特殊,可以作为优化的方向;


      如果 builder 函数包含一个不依赖于动画的子树,则一次构建该子树比在每个动画变更时都重新构建子树更为高效;即在 child 中预先定义好 WidgetAnimatedBuilder 会将其传递到构造器函数中;

案例尝试

     小菜尝试 AnimatedBuilder 方式展示一个类似 ACEPageMenu 从底部滑出的一个小动画效果;


return AnimatedBuilder(    animation: _controller,    child: Container(        color: Colors.brown,        height: 100.0, width: 300.0,        child: Center(            child: GestureDetector(                onTap: () => print('I am form AnimatedBuilder !'),                child: Icon(Icons.ac_unit, color: Colors.white)))),    builder: (BuildContext context, Widget child) {      return Transform.translate(          offset: Offset(50, ScreenUtils.getScreenHeight() - _controller.value * 200),          child: Opacity(opacity: _controller.value, child: child));    });
复制代码



     条条大路通罗马,同一效果可以有多种不同的实现方式;AnimatedWidgetAnimatedBuilder 使用都很便利,而小菜认为 AnimatedBuilder 在处理复杂动画时更加灵活方便;


注意事项

      小菜在尝试缩放动画过程中,遇到之前不曾注意的地方,即动画起始位置由 originalignment 共同决定,以 aligment 对齐位置为坐标原点,origin 在此基础上平移起始位置;与 scale 无关;


Transform.scale({    Key key,    @required double scale,    this.origin,    this.alignment = Alignment.center,    this.transformHitTests = true,    Widget child,})
复制代码


      小菜尝试了几个基本场景:


  1. alignment 对齐方式默认居中,origin 起始位置默认为 Offset.zero 时,scale 增大为 1.5 倍;可以看到其初始动画位置仍为 Widget 缩放前的中心位;


return Transform.scale(    scale: _progress.value * 1.5,    origin: Offset.zero,    alignment: Alignment.center,    child: Container(width: 200.0, height: 200.0, color: Colors.yellow.withOpacity(0.8)));
复制代码



  1. alignment 对齐方式默认居中,调整 origin 起始位置为 Offset(50, 50);可以看到其初始动画位置是以 alignment 居中点为原点,水平竖直方向为坐标系轴,平移 origin 位置;


return Transform.scale(    scale: _progress.value,    origin: Offset(50, 50),    alignment: Alignment.center,    child: Container(width: 200.0, height: 200.0, color: Colors.yellow.withOpacity(0.8)));
复制代码



  1. alignment 默认居中右对齐,origin 起始位置为 Offset(50, 50);可以看到其初始动画位置是以 alignment 居中右对齐中点为原点,水平竖直方向为坐标系轴,平移 origin 位置;


return Transform.scale(    scale: _progress.value,    origin: Offset(50, 50),    alignment: Alignment.centerRight,    child: Container(width: 200.0, height: 200.0, color: Colors.yellow.withOpacity(0.8)));
复制代码





      AnimatedWidget 和 AnimatedBuilder 案例源码




      小菜对动画的源码还不够深入,如有错误,请多多指导!


来源: 阿策小和尚

发布于: 2021 年 06 月 12 日阅读数: 7
用户头像

还未添加个人签名 2021.05.13 加入

Android / Flutter 小菜鸟~

评论

发布
暂无评论
【Flutter 专题】106 图解 AnimatedWidget & AnimatedBuilder 动画应用