前言:
这是我参与8月更文挑战的第 8 天,活动详情查看:8月更文挑战。为应掘金的八月更文挑战,我准备在本月挑选 31 个以前没有介绍过的组件,进行全面分析和属性介绍。这些文章将来会作为 Flutter 组件集录 的重要素材。希望可以坚持下去,你的支持将是我最大的动力~
一、认识 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 方法中,创建 RenderAnimatedOpacity ,opacity 动画器会被传入构造入参中。在 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 组件 的使用方式到这里就介绍完毕,那本文到这里就结束了,谢谢观看,明天见~