Flutter 71: 图解基本隐式动画 Widget

简介: 0 基础学习 Flutter,第七十一步:学习 Flutter 基本隐式动画组件!

      小菜前段时间自定义 ACEStepper 步进器时,在 ACEStep 中尝试过 AnimatedCrossFade 用于在两个 Widget 切换过度,简单实用,今天小菜重点学习一下并尝试相关隐式动画 Widget

AnimatedCrossFade 淡入淡出动画

源码分析

const AnimatedCrossFade({
    Key key,
    @required this.firstChild,                  // 首个展示 Widget
    @required this.secondChild,                 // 第二展示 Widget
    this.firstCurve = Curves.linear,            // 首个 Widget 展示动画
    this.secondCurve = Curves.linear,           // 第二 Widget 展示动画
    this.sizeCurve = Curves.linear,             // 切换时尺寸动画
    this.alignment = Alignment.topCenter,       // 对齐方式
    @required this.crossFadeState,              // 切换状态(是否切换)
    @required this.duration,                    // 切换动画时长
    this.reverseDuration,                       // 切换反向动画时长
    this.layoutBuilder = defaultLayoutBuilder,  // Widget 布局构造器
})

      分析源码可知,AnimatedCrossFade 可以在指定时间内从一个 Widget 到另一个 Widget 的平滑过渡或反向过渡;其中切换状态和时长是必要属性;

案例尝试

  1. 小菜尝试一个基本的动画过程,两个方块之间进行切换;
return GestureDetector(
    onTap: () { setState(() => isChanged = !isChanged); },
    child: Container(
        child: AnimatedCrossFade(
            firstChild: Container(width: 100, height: 100, color: Colors.purpleAccent.withOpacity(0.4)),
            secondChild: Container(width: 200, height: 200, color: Colors.blueGrey.withOpacity(0.4)),
            duration: Duration(milliseconds: 1500),
            crossFadeState: isChanged ? CrossFadeState.showSecond : CrossFadeState.showFirst)));

  1. reverseDuration 为切换反向动画时长;
reverseDuration: Duration(milliseconds: 500),

  1. firstCurve / secondCurve 为两个 Widget 切换时动画效果;动画效果有多种,小菜不在此赘述;
firstCurve: Curves.fastOutSlowIn,
secondCurve: Curves.easeInExpo,

  1. alignment 为尺寸动画切换时对齐位置,当两个 Widget 大小不同时效果明显,小菜尝试了两种位置进行对比;
alignment: Alignment.bottomRight,

alignment: Alignment.center,

  1. sizeCurve 为尺寸切换动画,当两个 Widget 大小不同时效果明显;
sizeCurve: Curves.easeInExpo,

sizeCurve: Curves.fastOutSlowIn,

  1. layoutBuilder 为布局构造器,这个是小菜认为最值得研究的地方,构造器并不陌生,但在这里的作用却比较特殊,通过 Stack 将两个 Widget 层级叠放,底部 Widget 默认尺寸位置以上层 Widget 为基准,默认 Position 边距均为 0.0;我们可以自定义调整动画起始位置;
// 默认
static Widget defaultLayoutBuilder(Widget topChild, Key topChildKey, Widget bottomChild, Key bottomChildKey) {
  return Stack(
      overflow: Overflow.visible,
      children: <Widget>[
        Positioned(key: bottomChildKey, left: 0.0, top: 0.0, right: 0.0, child: bottomChild),
        Positioned(key: topChildKey, child: topChild)
      ]);
}
// 调整 Position 位置
layoutBuilder: (topChild, topChildKey, bottomChild, bottomChildKey) {
  return Stack(children: <Widget>[
    Positioned(key: bottomChildKey, left: 50.0, top: 50.0, right: 50.0, bottom: 50.0, child: bottomChild),
    Positioned(key: topChildKey, child: topChild)
  ]);
}

      AnimatedCrossFade 源码

AnimatedSwitcher 切换动画

源码分析

const AnimatedSwitcher({
    Key key,
    this.child,
    @required this.duration,                // 切换动画时长
    this.reverseDuration,                   // 反向切换动画时长
    this.switchInCurve = Curves.linear,     // 切换显示时动画曲线
    this.switchOutCurve = Curves.linear,    // 切换隐藏时动画曲线
    this.transitionBuilder = AnimatedSwitcher.defaultTransitionBuilder,  // Widget 动画构造器
    this.layoutBuilder = AnimatedSwitcher.defaultLayoutBuilder,          // Widget 布局构造器
})

      分析源码可知,AnimatedSwitcher 更加灵活,可自由设置切换动画之间显示隐藏动画效果;当 child Widget 内容或 Key 有变更时,old child 会执行隐藏动画,new child 会执行展现动画;

案例尝试

  1. 小菜尝试切换两个基本的方块,但刚开始切换动画时长和反向切换动画时长没有效果,两个 Widget 只有参数更新,动画效果未执行;小菜尝试加入 Key 区分之后正常;
return GestureDetector(
    onTap: () => setState(() => isChanged = !isChanged),
    child: AnimatedSwitcher(
        duration: Duration(milliseconds: 500),
        reverseDuration: Duration(milliseconds: 1500),
        child: isChanged
            ? Container(key: UniqueKey(), color: Colors.purpleAccent.withOpacity(0.4),  width: 100, height: 100)
            : Container(key: UniqueKey(), color: Colors.green.withOpacity(0.4), width: 150, height: 120)));

  1. 小菜在切换过程中尝试不同的显示隐藏动画效果;
switchInCurve: Curves.easeInCubic,
switchOutCurve: Curves.fastLinearToSlowEaseIn,

switchInCurve: Curves.easeInExpo,
switchOutCurve: Curves.fastOutSlowIn,

  1. transitionBuilder 为动画构造器,可以自定义动画效果;小菜尝试了两种简单的缩放动画和平移动画,暂未尝试复杂动画;且动画属性与显示隐藏的 switchInCurve / switchOutCurve 动画曲线共同展示效果;
// 缩放动画效果
return GestureDetector(
    onTap: () => setState(() => isChanged = !isChanged),
    child: AnimatedSwitcher(
        duration: Duration(milliseconds: 500),
        reverseDuration: Duration(milliseconds: 1500),
        switchInCurve: Curves.easeInCubic,
        switchOutCurve: Curves.fastLinearToSlowEaseIn,
        child: isChanged
            ? Container(key: UniqueKey(), color: Colors.purpleAccent.withOpacity(0.4), width: 100, height: 100)
            : Container(key: UniqueKey(), color: Colors.green.withOpacity(0.4), width: 150, height: 120),
        transitionBuilder: (Widget child, Animation<double> animation) {
          return ScaleTransition(scale: animation, child: child);
        }));
        
// 平移动画效果
return GestureDetector(
    onTap: () => setState(() => isChanged = !isChanged),
    child: AnimatedSwitcher(
        duration: Duration(milliseconds: 500),
        reverseDuration: Duration(milliseconds: 1500),
        switchInCurve: Curves.easeInExpo,
        switchOutCurve: Curves.fastOutSlowIn,
        child: isChanged
            ? Container(key: UniqueKey(), color: Colors.purpleAccent.withOpacity(0.4), width: 100, height: 100)
            : Container(key: UniqueKey(), color: Colors.green.withOpacity(0.4), width: 150, height: 120),
        transitionBuilder: (Widget child, Animation<double> animation) {
          return SlideTransition(child: child, position: Tween<Offset>(begin: Offset(1, 0), end: Offset(0, 0)).animate(animation));
        }));

  1. childold/new Widget 一般是以 Stack 层级存储,在动画过程中两个 Widget 均要展示,可以通过 layoutBuilder 布局构造器进行自定义;小菜尝试调整对齐方式和只展示 current Widget 动画效果;
// 调整层级对齐方式
layoutBuilder: (Widget currentChild, List<Widget> previousChildren) {
  return Stack(children: <Widget>[
    ...previousChildren,
    if (currentChild != null) currentChild
  ], alignment: Alignment.bottomRight);
}
            
// 只展示当前 Widget 动画效果
layoutBuilder: (Widget currentChild, List<Widget> previousChildren) {
    return currentChild;
}

  1. AnimatedSwitcher 可以设置多个 Widget 平滑切换,相对于 AnimatedCrossFade 可扩展性更高;小菜尝试三个 Widget 平移切换;
return GestureDetector(
    onTap: () => setState(() {
          ++index;
          index = index % 3;
        }),
    child: AnimatedSwitcher(
        duration: Duration(milliseconds: 500),
        child: _animatedItemWid(index),
        transitionBuilder: (Widget child, Animation<double> animation) {
          return SlideTransition(child: child, position: Tween<Offset>(begin: Offset(1, 0), end: Offset(0, 0)).animate(animation));
        }));

Widget _animatedItemWid(index) {
  switch (index) {
    case 0:
      return Container(key: UniqueKey(), color: Colors.purpleAccent.withOpacity(0.4), width: 100, height: 100);
      break;
    case 1:
      return Container(key: UniqueKey(), color: Colors.green.withOpacity(0.4), width: 150, height: 120);
      break;
    case 2:
      return Container(key: UniqueKey(), color: Colors.orange.withOpacity(0.4), width: 120, height: 140);
      break;
  }
}

      AnimatedSwitcher 源码


      Flutter 还提供了很多灵活的隐式动画 Widget,小菜认为这两类最灵活,使用场景最多;小菜对隐式动画研究还不够深入,如有错误请多多指导!

来源: 阿策小和尚

目录
相关文章
|
2天前
|
容器
Flutter Widget 解析
Flutter Widget 解析
|
2天前
|
存储 容器
Flutter 有状态Widget 和 无状态Widget
Flutter 有状态Widget 和 无状态Widget
|
1月前
深入理解Flutter鸿蒙next版本 中的Widget继承:使用extends获取数据与父类约束
本文详细介绍了Flutter中如何通过继承其他Widget来创建自定义组件。首先解释了Widget继承的基本概念,包括StatelessWidget和StatefulWidget的区别。接着通过具体示例展示了如何继承StatelessWidget和StatefulWidget,并在子类中访问父类的build方法和状态。最后,结合多个自定义Widget展示了如何在实际应用中灵活使用继承和组合来构建复杂的UI。
76 8
|
1月前
|
开发工具 UED 容器
Flutter&鸿蒙next 实现长按录音按钮及动画特效
本文介绍了如何在 Flutter 中实现一个带有动画效果的长按录音按钮。通过使用 `GestureDetector` 监听长按手势,结合 `AnimatedContainer` 和 `AnimationController` 实现按钮的动画效果,以及 `flutter_sound` 插件完成录音功能。文章详细讲解了功能需求、实现思路和代码实现,帮助读者逐步掌握这一实用功能的开发方法。
122 5
|
1月前
|
容器
flutter&鸿蒙next 使用 InheritedWidget 实现跨 Widget 传递状态
在 Flutter 中,状态管理至关重要。本文详细介绍了如何使用 InheritedWidget 实现跨 Widget 的状态传递。InheritedWidget 允许数据在 Widget 树中向下传递,适用于多层嵌套的场景。通过一个简单的计数器示例,展示了如何创建和使用 InheritedWidget,包括其基础概念、工作原理及代码实现。虽然 InheritedWidget 较底层,但它是许多高级状态管理解决方案的基础。
103 2
|
1月前
|
前端开发 开发者
深入探索 Flutter 鸿蒙版的画笔使用与高级自定义动画
本文深入探讨了 Flutter 中的绘图功能,重点介绍了 CustomPainter 和 Canvas 的使用方法。通过示例代码,详细讲解了如何绘制自定义图形、设置 Paint 对象的属性以及实现高级自定义动画。内容涵盖基本绘图、动画基础、渐变动画和路径动画,帮助读者掌握 Flutter 绘图与动画的核心技巧。
80 1
|
2月前
动画控制器在 Flutter 中的工作原理
【10月更文挑战第18天】总的来说,动画控制器 `AnimationController` 在 Flutter 中起着关键的作用,它通过控制动画的数值、速度、节奏和状态,实现了丰富多彩的动画效果。理解它的工作原理对于我们在 Flutter 中创建各种精彩的动画是非常重要的。
|
2月前
|
容器
flutter:第一个flutter&Widget的使用 (二)
本文介绍了Flutter框架下的基本组件及其用法,包括简单的 Stateless Widget 如文本和按钮,以及更复杂的 StatefulWidget 示例。详细解释了如何使用 `context` 获取祖先小部件的信息,并展示了 `MaterialApp` 的属性及用途。此外,还探讨了 `StatefulWidget` 与 `StatelessWidget` 的区别,以及 `AppBar` 的常见属性配置方法。适合Flutter初学者参考学习。
|
1月前
|
Dart JavaScript 前端开发
Flutter 的 Widget 概述与常用 Widgets 与鸿蒙 Next 的对比
Flutter 是 Google 开发的开源 UI 框架,用于快速构建高性能的移动、Web 和桌面应用。Flutter 通过 Widget 构建 UI,每个 UI 元素都是 Widget,包括文本、按钮、图片等。Widget 不仅描述外观,还描述行为,是不可变的。常见的 Widget 包括结构型(Container、Column、Row)、呈现型(Text、Image)、交互型(ElevatedButton)和状态管理型(StatefulWidget)。Flutter 与鸿蒙 Next 在组件化架构、开发语言、布局系统、性能和跨平台支持方面各有优势
75 0
|
2月前
|
UED
flutter:动画&状态管理 (十三)
本文档介绍了Flutter中`animatedList`的使用方法和状态管理的示例。`animatedList`用于创建带有动画效果的列表,示例代码展示了如何添加、删除列表项,并执行相应的动画效果。状态管理部分通过一个简单的点击切换颜色的示例,演示了如何在Flutter中管理组件的状态。