flutter 图解_Flutter 71: 图解基本隐式动画 Widget

小菜前段时间自定义 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 的平滑过渡或反向过渡;其中切换状态和时长是必要属性;

案例尝试

小菜尝试一个基本的动画过程,两个方块之间进行切换;

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)));

2. reverseDuration 为切换反向动画时长;

reverseDuration: Duration(milliseconds: 500),

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

firstCurve: Curves.fastOutSlowIn,

secondCurve: Curves.easeInExpo,

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

alignment: Alignment.bottomRight,

alignment: Alignment.center,

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

sizeCurve: Curves.easeInExpo,

sizeCurve: Curves.fastOutSlowIn,

6. 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: [

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: [

Positioned(key: bottomChildKey, left: 50.0, top: 50.0, right: 50.0, bottom: 50.0, child: bottomChild),

Positioned(key: topChildKey, child: topChild)

]);

}

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 会执行展现动画;

案例尝试

小菜尝试切换两个基本的方块,但刚开始切换动画时长和反向切换动画时长没有效果,两个 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)));

2. 小菜在切换过程中尝试不同的显示隐藏动画效果;

switchInCurve: Curves.easeInCubic,

switchOutCurve: Curves.fastLinearToSlowEaseIn,

switchInCurve: Curves.easeInExpo,

switchOutCurve: Curves.fastOutSlowIn,

3. 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 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 animation) {

return SlideTransition(child: child, position: Tween(begin: Offset(1, 0), end: Offset(0, 0)).animate(animation));

}));

4. child 中 old/new Widget 一般是以 Stack 层级存储,在动画过程中两个 Widget 均要展示,可以通过 layoutBuilder 布局构造器进行自定义;小菜尝试调整对齐方式和只展示 current Widget 动画效果;

// 调整层级对齐方式

layoutBuilder: (Widget currentChild, List previousChildren) {

return Stack(children: [

...previousChildren,

if (currentChild != null) currentChild

], alignment: Alignment.bottomRight);

}

// 只展示当前 Widget 动画效果

layoutBuilder: (Widget currentChild, List previousChildren) {

return currentChild;

}

5. 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 animation) {

return SlideTransition(child: child, position: Tween(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;

}

}

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

来源: 阿策小和尚

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值