【Flutter 组件集录】FadeTransition| 8月更文挑战

1,713 阅读4分钟
前言:

这是我参与8月更文挑战的第 8 天,活动详情查看:8月更文挑战。为应掘金的八月更文挑战,我准备在本月挑选 31 个以前没有介绍过的组件,进行全面分析和属性介绍。这些文章将来会作为 Flutter 组件集录 的重要素材。希望可以坚持下去,你的支持将是我最大的动力~

本系列组件文章列表
1.NotificationListener2.Dismissible3.Switch
4.Scrollbar5.ClipPath6.CupertinoActivityIndicator
7.Opacity8.FadeTransition9. AnimatedOpacity
10. FadeInImage11. Offstage12. TickerMode
13. Visibility14. Padding15. AnimatedContainer
16.CircleAvatar17.PhysicalShape18.Divider
19.Flexible、Expanded 和 Spacer 20.Card

一、认识 FadeTransition 组件

上一篇介绍了 Opacity 组件,今天来看一个可以透明动画变换的组件: FadeTransition。其作用是:将一个组件 以指定透明度 opacity 动画进行透明变化`。


1.FadeTransition 基本信息

下面是 FadeTransition 组件类的定义构造方法,可以看出它继承自 SingleChildRenderObjectWidget。实例化时必须传入 opacity 入参,其类型为 Animation<double> ,用于控制透明度的动画器。还可以传入一个 chuild 组件。

---->[FadeTransition#FadeTransition 声明]----
final Animation<double> opacity;

2.FadeTransition 的使用

FadeTransition 组件的比 Opacity 复杂很多,因为我们要指定 opacity 动画器,像这样由用户知道动画器 实现动画效果的,称之为 显示动画组件 。如下代码,是在 2S 内将透明度在 0.2 ~ 1.0 间变换的动画效果:

由于需要使用 动画器,我们需要使用 StatefulWidget ,在 XXXState 中混入 SingleTickerProviderStateMixin ,可以让状态类作为 vsync 用来创建 AnimationController 。在 dispose 中释放动画控制器。

class FadeTransitionDemo extends StatefulWidget {
  @override
  _FadeTransitionDemoState createState() => _FadeTransitionDemoState();
}

class _FadeTransitionDemoState extends State<FadeTransitionDemo>
    with SingleTickerProviderStateMixin {
  late AnimationController _ctrl;
  late Animation<double> opacityAnim;

  @override
  void initState() {
    super.initState();
    _ctrl = AnimationController(vsync: this, duration: const Duration(seconds: 2));
    opacityAnim= Tween<double>(begin: 0.2,end: 1.0).animate(_ctrl);
    _ctrl..forward();
  }

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

通过 Tween_ctrl 创建一个 0.2 ~ 1.0 的动画器 opacityAnim , 作为入参传入 FadeTransition 构造函数中。这样在 _ctrl 动画控制器开启时,其子组件就可以执行透明度渐变动画。

@override
Widget build(BuildContext context) {
  return GestureDetector(
    onTap: () {
      _ctrl.forward(from: 0);
    },
    child: FadeTransition(
      opacity: opacityAnim,
      child: Image.asset(
        'assets/images/icon_head.png',
        width: 100,
        height: 100,
      ),
    ),
  );
}

二、 FadeTransition 的源码实现

1. FadeTransition 源码分析

它继承自 SingleChildRenderObjectWidget 就说明,该组件需要维护一个 RenderObject 对象的创建及更新。

createRenderObject 方法中,创建 RenderAnimatedOpacityopacity 动画器会被传入构造入参中。在 updateRenderObject 中,对 RenderAnimatedOpacity 对象进行更新。也就是说,渐变透明变换的绘制处理是在 RenderAnimatedOpacity 中进行的。


2.RenderAnimatedOpacity 源码分析

对于 FadeTransition 组件来说,最神奇的应该是:为什么透明度会跟随 Animation 对象不断触发重绘,从而进行动画。既然 动画器 opacity 是一个 Listenable 对象,那很容易想到是通过监听 动画器 进行重绘。 RenderAnimatedOpacity 通过混入 RenderAnimatedOpacityMixin 实现动画效果。


RenderAnimatedOpacityMixin#attach 方法中,会通过 addListener 来监听动画器的变化。在 detach 中移除监听。监听的方法是 _updateOpacity

_updateOpacity 方法中,如果新旧的 alpha 值不同,就会触发 markNeedsPaint 进行通知重绘。


RenderAnimatedOpacityMixin#paint 方法中的绘制逻辑和 Opacity 组件绘制是一样的。当 child 非空时,如果 _alpha = 0 就什么都不需要画,直接返回。如果 _alpha = 255 ,则直接绘制 child

如果有透明度时,会通过 context.pushOpacity ,创建一个透明层 layer 来完成透明功能。该方法在 Opacity 组件一文中介绍过了,这里不再赘述。


3. FadeTransition 的价值

如果不知道 FadeTransition 组件的存在,我们想要实现 透明度渐变 的效果,能想到的可能就是通过 AnimatedBuilder 监听 Opacity 组件,来动态改变透明度值,完成动画,代码如下:

AnimatedBuilder(
  animation: opacityAnim,
  builder: (ctx, child) => Opacity(
    opacity: opacityAnim.value,
    child: child,
  ),
  child: Image.asset(
    'assets/images/icon_head.png',
    width: 100,
    height: 100,
  ),
),

上面的方式可以达到一样的效果,那这和 FadeTransition 想必有什么劣势呢?首先,很明显使用 AnimatedBuilder 组件比较麻烦;其次使用 AnimatedBuilder 组件,每次动画器数值变化都会执行 builder 回调来创建组件,也就是说 Opacity 组件会被创建很多次。

而从 FadeTransition 源码中可以看出,对动画器的监听是在其对应的 RenderAnimatedOpacity 中进行的,也就是说,通过 FadeTransition 组件进行 透明度渐变动画 的整个动画过程,是没有伴随任何组件重建的。这就是其最大的优势。

FadeTransition 组件 的使用方式到这里就介绍完毕,那本文到这里就结束了,谢谢观看,明天见~