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

1,574 阅读4分钟
前言:

这是我参与8月更文挑战的第 9 天,活动详情查看: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

一、认识 AnimatedOpacity 组件

上两篇介绍了 Opacity 组件 和 FadeTransition 组件,他们都和操作组件的透明度有关。除了这两位,还有一个组件也和透明度有关,它就是: AnimatedOpacity。和 FadeTransition 组件的功能是一样的,它也可以进行 透明度渐变动画。那他和 FadeTransition 有什么异同点,为什么框架中会给出两个功能一致的组件?带着这个问题,我们来开始认识 AnimatedOpacity 组件。


1.AnimatedOpacity 基本信息

我们通过继承树可以看出,该组件继承自 ImplicitlyAnimatedWidget ,是一个 StatefulWidgetImplicitlyAnimatedWidget 称之为 隐式动画,其最大的特点是: 使用者无需自己提供 动画器,也可以进行动画变换。


通过 AnimatedOpacity 的构造入参,可以发现,它确实不需要传入动画器,但可以指定动画相关的 时长动画曲线。从这里,很容易猜出 ImplicitlyAnimatedWidget 相关类会维护 动画控制器,封装动画的操作。这样的目的很清楚:避免用户直接和 动画器 打交道,方便使用。从这里可以衍生出一个问题 : 既然用户无法直接操纵动画器,那么动画是如何被开启的? 带着问题,继续往下看。


2.AnimatedOpacity 的使用

我们先来看一个 AnimatedOpacity 使用的小 demo 。如下,通过 Switch 来切换 AnimatedOpacityopacity 的值。可见,当 opacity 配置属性变化时,就会执行动画。

下面代码中,通过 Wrap 组件,竖直包裹 SwitchContainer ,其中容器里放着 AnimatedOpacity,让一个图标进行动画变化。在 Switch 点击时,重新构建 AnimatedOpacity 并更新 opacity 的配置值。然后就神奇地执行动画了,可以看出,动画的执行和组件重构是有关系的。

class AnimatedOpacityDemo extends StatefulWidget {
  @override
  _AnimatedOpacityDemoState createState() => _AnimatedOpacityDemoState();
}

class _AnimatedOpacityDemoState extends State<AnimatedOpacityDemo> {
  final double beginOpacity = 1.0;
  final double endOpacity = 0;

  late double _opacity;

  @override
  void initState() {
    super.initState();
    _opacity = beginOpacity;
  }

  bool get selected => _opacity == 0;

  @override
  Widget build(BuildContext context) {
    return Wrap(
      direction: Axis.vertical,
      crossAxisAlignment: WrapCrossAlignment.center,
      children: <Widget>[
        Switch(
          value: selected,
          onChanged: onChanged,
        ),
        Container(
          color: Colors.grey.withAlpha(22),
          width: 100,
          height: 100,
          child: buildAnimatedOpacity(),
        ),
      ],
    );
  }

  Widget buildAnimatedOpacity() =>
      AnimatedOpacity(
        duration: const Duration(seconds: 1),
        curve: Curves.fastOutSlowIn,
        opacity: _opacity,
        child: _buildChild(),
      );

  void onChanged(bool value) {
    setState(() {
      _opacity = value ? endOpacity : beginOpacity;
    });
  }

  Widget _buildChild() =>
      const Icon( Icons.add_to_drive, color: Colors.green, size: 60 );
}

3.AnimatedOpacity 的价值

AnimatedOpacity 组件最大的亮点在于只要其 opacity 属性发生变化,并重新构建,就可以进行动画。下面的案例更能体现它的价值:通过 Slider 的滑动,控制 AnimatedOpacityopacity 属性变化,及重新构建。这样每当滑动到新的值,都会 动画渐变到该透明度,如果使用 FadeTransition 组件,自己操作纵动画器来实现,就会比较复杂。 AnimatedOpacity 组件这类的隐式动画大大降低了用户使用动画的门槛

// Slider 组件构建
Container(
  height: 50,
  child: Slider(
    label: "$_opacity",
    value: _opacity,
    divisions: 5,
    onChanged: onChanged,
  ),
),

void onChanged(double value) {
  setState(() {
    _opacity = value;
  });
}

另外,AnimatedOpacity 可以通过 onEnd 回调监听动画执行完毕的时机。


三、 AnimatedOpacity 的源码实现

1. AnimatedOpacity 源码分析

前面说过 AnimatedOpacity 继承自 ImplicitlyAnimatedWidget

ImplicitlyAnimatedWidget 做为 StatefulWidget 需要实现 createState 创建 State 对象,但由于其是抽象类,可以选择不实现,交由子类完成。

ImplicitlyAnimatedWidget 中没有实现 createState ,而把返回的状态类型将限制为 ImplicitlyAnimatedWidgetState<ImplicitlyAnimatedWidget>


AnimatedOpacity 作为 ImplicitlyAnimatedWidget 子类,需要实现 createState 抽象方法。如下,状态类为 _AnimatedOpacityState

这样组件层面的源码就理清了,ImplicitlyAnimatedWidget 相当于一个中间层,可以通过 ImplicitlyAnimatedWidgetState 额外做一些事情。其子类组件的创建的状态类,也需要继承自 ImplicitlyAnimatedWidgetState 。下面就来看看,状态类做了什么。


2. 状态类的处理

实现来看作为实现类的 _AnimatedOpacityState 。非常简单,就是维护 _opacityAnimation,依赖于 FadeTransition 组件进行动画。是不是想直呼 好家伙


动画控制器的源泉在于抽象的状态类 ImplicitlyAnimatedWidgetState ,这里将进行 动画控制器 的监听、开启等维护工作。

initState 中会对动画器进行监听,如果动画完成,会执行 onEnd 回调。


3. 动画的开启

看到这里,你应该能猜出来动画触发的时机。当 StatefulWidget重新构建时,状态类是不会重新初始化的。而是触发 State#didUpdateWidget 来通知 Widget 配置的变更,在此可以处理一下组件更新的逻辑。可以看出,在 ImplicitlyAnimatedWidgetState#didUpdateWidget 中,会对配置进行对比,发生变化将会更新。最后 _controller 会执行 forward 进行动画。


AnimatedOpacity 组件,本质上就是对 动画控制器FadeTransition 组件的一层封装。简化用户对动画使用的门槛,降低动画使用的错误率。其他的隐式动画组件也是类似,AnimatedOpacity 的使用方式到这里就介绍完毕,那本文到这里就结束了,谢谢观看,明天见~