Flutter 动画组件那么多,记不住不会用怎么办?我都给你整理好了,收藏吧!

前言

Flutter 自带的基础动画组件称之为隐式动画组件,小小统计了一下,会有几十个那么多,包括通用的动画构建类、特定的动画效果类以及封装好的动画组件。这么多,好处是想用的基本都有,不好的地方是记不住,找起来也不太方便。本篇特地整理了 Flutter 的隐式动画组件,方便各位 Flutter 爱好者(搬砖者)随时查看和使用。

AnimatedWidget

AnimatedWidget是一个抽象类,可以通过 Animation 对象的值动态刷新每一帧从而实现动画效果。如果你想构建自己可复用的动画组件,那么可以使用 AnimatedWidget,我们在
Flutter 实现风车加载动画组件
Flutter 实现 3D 动画效果详解
这两篇文章中就使用了AnimatedWidget构建了一个风车加载指示动画组件。AnimatedWidget 的构造方法如下,其中 listenable 是一个抽象类 Listenable 对象,一般是 AnimationChangeNotifier

const AnimatedWidget({
  Key? key,
  required this.listenable,
}) 

Animation

Animation不算是一个组件,但是构建隐式动画组件都需要 Animation 对象来控制动画起止参数,动画曲线等。借助 Animation,你可以基于 StatefulWidgetStatelessWidget 构建自己的动画类。我们在
Flutter 实现爱心三连动画
使用了 Animation 构建了爱心尺寸变化的动画。Animation 通常是依赖 AnimationController 组合实现动画效果。

AnimatedBuilder

AnimatedBuilder将动画效果和组件分离,从而使得动效可以应用与不同组件。如果在应用中一个动效会被用于多个不同的组件,那么 AnimatedBuilder 是首选。我们在
Flutter 入门与实战(九十六):使用 AnimatedBuilder 分离组件和动画,实现动效复用
做了AnimatedBuilder的示例应用。AnimatedBuilder 的构造方法如下,animationbuilder 是外部传入的,因此可以构造与组件分离的可复用的动画效果。

const AnimatedBuilder({
    Key? key,
    required Listenable animation,
    required this.builder,
    this.child,
  }) 

AnimatedContainer

AnimatedContainerContainer 的动画替换组件,可以通过修改动画过程中的尺寸、对齐方式,tranform 参数等实现容器的动画效果。我们使用了 AnimatedContainer 实现了一个笑脸动画:
Flutter 复刻一个逼真的笑嘻嘻表情
AnimatedContainer的构造方法如下,可以看到基本上所有的布局相关的属性都可以受动效控制。

AnimatedContainer({
  Key? key,
  this.alignment,
  this.padding,
  Color? color,
  Decoration? decoration,
  this.foregroundDecoration,
  double? width,
  double? height,
  BoxConstraints? constraints,
  this.margin,
  this.transform,
  this.transformAlignment,
  this.child,
  this.clipBehavior = Clip.none,
  Curve curve = Curves.linear,
  required Duration duration,
  VoidCallback? onEnd,
})

AnimatedPositioned

AnimatedPositioned是 Stack 组件中的 Positioned 的动画替换组件。可以通过AnimatedPositioned 实现组件在 Stack 组件的位置,从而实现相对 Stack 组件的移动效果,譬如:
Flutter 模拟神舟十三号火箭发射动画
AnimatedPositioned的构造方法如下,需要注意的是横向参数(leftrightwidth)、纵向参数(topbottomheight)只能从3个里面选2个设置,否则会导致布局冲突。

const AnimatedPositioned({
  Key? key,
  required this.child,
  this.left,
  this.top,
  this.right,
  this.bottom,
  this.width,
  this.height,
  Curve curve = Curves.linear,
  required Duration duration,
  VoidCallback? onEnd,
})

AnimatedCrossFade

AnimatedCrossFade 用于两个组件切换,动效是渐现效果。同时若两个子组件的尺寸不同,可以使用 layoutBuilder 来平滑过渡尺寸的变化。两个子组件的动画曲线可以单独配置。最简单的应用就是更改两个要切换的子组件的显示,代码如下所示:

AnimatedCrossFade(
  duration: const Duration(seconds: 3),
  firstChild: const FlutterLogo(style: FlutterLogoStyle.horizontal, size: 100.0),
  secondChild: const FlutterLogo(style: FlutterLogoStyle.stacked, size: 100.0),
  crossFadeState: _first ? CrossFadeState.showFirst : CrossFadeState.showSecond,
)

AnimatedCrossFade 的构造方法如下,其中 layoutBuilder 默认的组件布局构建方法。

const AnimatedCrossFade({
  Key? key,
  required this.firstChild,
  required this.secondChild,
  this.firstCurve = Curves.linear,
  this.secondCurve = Curves.linear,
  this.sizeCurve = Curves.linear,
  this.alignment = Alignment.topCenter,
  required this.crossFadeState,
  required this.duration,
  this.reverseDuration,
  this.layoutBuilder = defaultLayoutBuilder,
})

AnimatedDefaultTextStyle

AnimatedDefaultTextStyle 用于文字样式动画效果,如果想自己控制字体变化过程(比如停留在中中间状态),可以使用DefaultTextStyleTransition 来完成。下面的动图是官网的效果,利用字体过渡的动画可以做品牌文字类的动画,可以加深用户的印象。

AnimatedDefaultTextStyle.gif

AnimatedDefaultTextStyle 的构造方法如下。

const AnimatedDefaultTextStyle({
  Key? key,
  required this.child,
  required this.style,
  this.textAlign,
  this.softWrap = true,
  this.overflow = TextOverflow.clip,
  this.maxLines,
  this.textWidthBasis = TextWidthBasis.parent,
  this.textHeightBehavior,
  Curve curve = Curves.linear,
  required Duration duration,
  VoidCallback? onEnd,
})

AnimatedList

AnimatedList 借助 AnimatedListState 可以实现插入和移出元素时的动画过渡效果,从而给列表的元素增加和删除的操作带来更好的反馈,提升用户体验。我们在 还在用 ListView?使用 AnimatedList 让列表元素动起来中介绍了如何使用 AnimatedList。 整个的实现来说还是有点复杂,推荐在列表元素不多的时候使用。AnimatedList 的构造方法如下,其中关键的是 itemBuilder 接收了一个 animation 对象,因此可以用来完成插入动画。而对于删除元素,则需要借助 AnimatedListStateremoveItem 方法完成。

const AnimatedList({
  Key? key,
  required this.itemBuilder,
  this.initialItemCount = 0,
  this.scrollDirection = Axis.vertical,
  this.reverse = false,
  this.controller,
  this.primary,
  this.physics,
  this.shrinkWrap = false,
  this.padding,
  this.clipBehavior = Clip.hardEdge,
})

AnimatedModalBarrier

AnimatedModelBarrierModalBarrier 的替换,可以挡住它下层的组件,使得这些组件无法与用户交互,并且在组件上加一层颜色动画过渡遮罩。AnimatedModelBarrier 的构造方法如下。其中 dismissible 参数如果为 true,则点击遮罩时会退出当前页面返回到上一页。

const AnimatedModalBarrier({
  Key? key,
    required Animation<Color?> color,
    bool dismissible,
    String? semanticsLabel,
    bool? barrierSemanticsDismissible
})

可以使用AnimatedModalBarrier 做自定义弹层,当要弹出弹层时,使用 AnimatedModalBarrier 遮挡底层,然后再在它上层叠加新的组件就可以实现弹层的效果。下面是我们实现的一个示例,点击按钮后弹出一个遮罩层,然后遮罩上加了一个文字层。

AnimatedModalBarrier.gif

AnimatedOpacity

AnimatedOpacity是透明度控制动画,可以控制子组件的透明度。这个就很好理解了,在动画过程中更改组件透明度。我们在Flutter 实现淡入淡出(Fade)动画效果 已经有过介绍了。

AnimatedPhysicalModel

控制组件的阴影、颜色、边框圆弧等物理模型,但组件自身的形状不发生改变。比如下图是官方给了一个更改阴影的例子,虽然组件自己没有变,但是因为阴影的变化,感觉却像是推开了一道缝隙一样,也挺有趣的。

AnmatedPhysicalModel.gif

AnimatedPhysicalModel 的构造方法如下,其中颜色和阴影颜色是通过两个布尔值 animateColoranimateShadowColor 决定是否要通过动画显示的。

const AnimatedPhysicalModel({
  Key? key,
  required Widget child,
  required BoxShape shape,
  Clip clipBehavior,
  BorderRadius borderRadius,
  required double elevation,
  required Color color,
  bool animateColor,
  required Color shadowColor,
  bool animateShadowColor,
  Curve curve = Curves.linear,
  required Duration duration,
  VoidCallback? onEnd
})

下面是一段示例代码,通过更改elevation 属性实现Z 轴阴影的变化,同时做了颜色的过渡动画效果:

Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(
      title: Text('AnimatedPhysicalModel 动画'),
    ),
    body: Center(
      child: AnimatedPhysicalModel(
        child: Container(
          width: 300,
          height: 300,
        ),
        duration: Duration(seconds: 1),
        color: _elevation == 0.0 ? Colors.blue : Colors.green,
        animateColor: true,
        animateShadowColor: true,
        elevation: _elevation,
        shape: BoxShape.circle,
        shadowColor: Colors.blue[900]!,
        curve: Curves.easeInOutCubic,
      ),
    ),
    floatingActionButton: FloatingActionButton(
      child: Text(
        'Play',
        style: TextStyle(
          color: Colors.white,
        ),
        textAlign: TextAlign.center,
      ),
      onPressed: () {
        setState(() {
          _elevation = _elevation == 0 ? 10.0 : 0.0;
        });
      },
    ),
  );
}

AnimatedSize

子组件的尺寸变化动画组件,通过尺寸的改变可以做放大缩小的效果,下面是官方的一个示例,点击组件的时候更改组件的尺寸,感觉是缩放一样。

Widget build(BuildContext context) {
  return GestureDetector(
    onTap: () => _updateSize(),
    child: Container(
      color: Colors.amberAccent,
      child: AnimatedSize(
        curve: Curves.easeIn,
        duration: const Duration(seconds: 1),
        child: FlutterLogo(size: _size),
      ),
    ),
  );
}

总结

本篇列举了 Flutter 隐式动画的12个基础组件,普通的动画效果依赖这些组件基本就能搞定了。而如果需要转换类的动画效果需要使用 Transition 来支持,下篇岛上码农为你整理一下 Transition 类的动画组件。源码可以到这里下载:动画组件演示示例源码

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350

推荐阅读更多精彩内容