Flutter 中各种单属性动画组件的使用(二)|周末学习

1,573 阅读3分钟

这是我参与更文挑战的第6天,活动详情查看: 更文挑战
本文已参与 周末学习计划,点击查看详情

前言

前一篇我们聊了几个纯粹的 Flutter 单属性动画组件,这篇我们看一下稍微复杂点的动画组件「AnimatedCrossFade、AnimatedSwitcher、AnimatedSize」,这里的复杂是指使用上有更多注意的点和配置上的灵活性。

AnimatedCrossFade(动画淡入淡出)

有时我们需要两个 Widget 之间切换,直接切换非常的突兀,我们可以加一个淡入淡出的效果, Flutter 为我们准备好了,来看看效果
01.gif
下面代码就很简单,分别设置两个 child Widget 即可

GestureDetector(
  onTap: () {
    // 点击切换
    this.crossFirst = !crossFirst;
    setState(() {});
  },
  child: AnimatedCrossFade(
    // 第一个 child
    firstChild: DescContainer(
      height: 60,
      width: width,
      color: Colors.black,
      text: 'AnimatedCrossFade firstChild',
    ),
    // 第二个 child
    secondChild: DescContainer(
      height: 40,
      width: 300,
      color: Colors.pink,
      text: 'AnimatedCrossFade secondChild',
    ),
    // 切换显示 child
    crossFadeState: crossFirst
    ? CrossFadeState.showFirst
    : CrossFadeState.showSecond,
    duration: duration,
  ),
),

优化突兀感

虽然实现切换了,但是在 firstChild => secondChild 的时候还是非常的突兀,我们需要通过自定义设置 layoutBuilder 属性来优化。
02.gif

看源码

image.png
这里默认的 layoutBuilder 使用的是 defaultLayoutBuilder 我们改动两个地方即可。

AnimatedCrossFade(
  ...
  layoutBuilder:(topChild, topChildKey, bottomChild, bottomChildKey) {
    return Stack(
      clipBehavior: Clip.none,
      // 这里设置居中对齐
      alignment: Alignment.center,
      children: <Widget>[
        Positioned(
          key: topChildKey,
          child: topChild,
        ),
        Positioned(
          key: bottomChildKey,
          // 去掉左边和右边定位
          // left: 0.0,
          top: 0.0,
          // right: 0.0,
          child: bottomChild,
        ),
      ],
    );
  },
),

其他属性

  • firstChild 第一个 child widget
  • secondChild 第二个 child widget
  • firstCurve 第一个曲线动画
  • secondCurve 第二个曲线动画
  • sizeCurve 大小的曲线动画,为什么会有这个属性,我们下面会讲到
  • alignment 对齐方式
  • crossFadeState 淡入状态
  • duration 动画时间
  • reverseDuration 返回动画时间
  • layoutBuilder 布局构建起,就是我们上面说的

AnimatedSize(动画大小)

现在继续回去翻看一下 AnimatedCrossFade 源码你会发现就是用过 ClipRect剪裁(可以看我专栏中的《Flutter 中各种剪裁 Widget 的使用》)+ AnimatedSize 实现的。
image.png
我们先看看实现的效果
03.gif

GestureDetector(
  onTap: () {
    this.size = size == 200 ? 100 : 200;
    setState(() {});
  },
  child: AnimatedSize(
    duration: duration,
    // 这里要混入 TickerProviderStateMixin 才可以
    vsync: this,
    clipBehavior: Clip.antiAlias,
    alignment: Alignment.center,
    child: FlutterLogo(
      size: size,
    ),
  ),
),

这里你看到的其实是假象,我们改变一下它的 child 看看。
04.gif

AnimatedSize(
  child: DescContainer(
    // 这里的 ! 标识我们确定它不为空,是空安全的知识点
    color: Colors.pink[300]!,
    height: size,
    width: size,
    text: 'AnimatedSize',
  ),
)

其实看到效果和我们上面 AnimatedCrossFade 是一样的,在第二次转换的时候是非常突兀的,然后在返回看看 FlutterLogo 的实现
image.png
哈哈哈哈哈,看到这里你就明白了吧,和我专栏中聊的 《Flutter 中使用 AnimatedContainer 让你的 Widget 动起来》实现是一致的,要怎么处理呢?

  • 可以参照 AnimatedCrossFade 来使用 LayoutBuilder
  • 可以阅读 AnimatedContainer 源码来优化,暂时不展开聊了

AnimatedSwitcher(动画切换)

05.gif06.gif
未设置 key设置了 key
GestureDetector(
  onTap: () {
    this.playing = !playing;
    setState(() {});
  },
  child: AnimatedSwitcher(
    duration: duration,
    child: Icon(
      playing ? Icons.pause_circle : Icons.play_circle,
      // 这里的 key 非常重要
      key: ValueKey(playing),
      size: 80,
      color: Colors.blue,
    ),
  ),
),

扩展一下

我们这里切换的时候目前只是渐变的效果,我们还可以加上缩放等效果,这时就要用到 transitionBuilder

07.gif08.gif09.gif
ScaleTransitionFadeTransitionScaleTransitionFadeTransition
AnimatedSwitcher(
  duration: duration,
  transitionBuilder: (child, animation) {
    // 缩放效果
    return ScaleTransition(
      scale: animation,
      // 渐变效果
      child: FadeTransition(
        opacity: animation,
        child: child,
      ),
    );
  },
  child: Icon(
    playing ? Icons.pause_circle : Icons.play_circle,
    // 这里的 key 非常重要
    key: ValueKey(playing),
    size: 80,
    color: Colors.blue,
  ),
)

Flutter 内置了还有很多 Transition ,如 SizeTransition、PositionedTransition、AlignTransition、SlideTransition、等 可以去很方便的组合使用,期待你有很多的发现和创意。

源码仓库

基于 Flutter 🔥 最新版本

参考链接

关注专栏

  • 此文章已收录到下面👇 的专栏,可以直接关注
  • 更多文章继续阅读|系列文章持续更新

👏 欢迎点赞➕收藏➕关注,有任何问题随时在下面👇评论,我会第一时间回复哦