flutter AnimatedSwitcher 动画切换过渡组件
- 前言
- 一、AnimatedSwitcher 简介
- 二、AnimatedSwitcher 的简单使用
- 三、AnimatedSwitcher 自定义跑马灯动画
- 四、SlideTransitionX 的封装
- 总结
前言
本篇文章将记录 AnimatedSwitcher 过渡组件,这个组件动画是一个新的小部件来代替另一个。它提供了一个很好的过渡,使应用程序非常流畅。始终为其子小部件添加一个键以确保其正常工作。
一、AnimatedSwitcher 简介
AnimatedSwitcher 可以同时对其新、旧子元素添加显示、隐藏动画。也就是说在AnimatedSwitcher的子元素发生变化时,会对其旧元素和新元素做动画,我们先看看AnimatedSwitcher 的源码:
const AnimatedSwitcher({
Key? key,
this.child,
required this.duration, // 新child显示动画时长
this.reverseDuration,// 旧child隐藏的动画时长
this.switchInCurve = Curves.linear, // 新child显示的动画曲线
this.switchOutCurve = Curves.linear,// 旧child隐藏的动画曲线
this.transitionBuilder = AnimatedSwitcher.defaultTransitionBuilder, // 动画构建器
this.layoutBuilder = AnimatedSwitcher.defaultLayoutBuilder, //布局构建器
})
当AnimatedSwitcher的 child 发生变化时(类型或 Key 不同),旧 child 会执行隐藏动画,新 child 会执行执行显示动画。究竟执行何种动画效果则由transitionBuilder参数决定,该参数接受一个AnimatedSwitcherTransitionBuilder类型的 builder,定义如下:
typedef AnimatedSwitcherTransitionBuilder =
Widget Function(Widget child, Animation<double> animation);
关于AnimatedSwitcher 组件的介绍就到这里,具体的属性的使用,有兴趣的可以去私下尝试一下,下面我们先来一个简单的使用。
二、AnimatedSwitcher 的简单使用
我们先来实现一个计数器,然后在每一次自增的过程中,旧数字执行缩小动画隐藏,新数字执行放大动画显示,代码如下
AnimatedSwitcher(
duration: Duration(milliseconds: 1000), // 新child 显示时长
reverseDuration: Duration(milliseconds: 500), // 旧child 显示时长
// transitionBuilder 动画展示的样式,日常开发中,可以根据实际需要,来设置不同的样式
transitionBuilder: (Widget child, Animation<double> animation){
return ScaleTransition(scale: animation, child: child,);
},
child: Text(
"${count}",
// 显示指定key, 不同的key 会被认为是不同的text,不添加key,不会执行动画
key: ValueKey(count),
style: TextStyle(
color: Colors.blue,
fontSize: 32
),
),
),
MaterialButton(
onPressed: () {
setState(() {
count += 1;
});
},
child: Text("count+1"),
),
运行上面的代码,当你点击+1 的时候,数字会有一个旧数字缩小,新数字放大的一个展示
三、AnimatedSwitcher 自定义跑马灯动画
如果项目需求,需要我们的数据从右侧进来,左侧出去,那么我们现在就没法去实现了,如果需要实现,就需要我们去改造一下SlideTransition 组件,下面一起来看改造代码
class CustomSlideTransitionWidget extends AnimatedWidget {
final Widget child;
final bool transformHitTests;
const CustomSlideTransitionWidget({
Key? key,
required Animation<Offset> position,
required this.child,
this.transformHitTests = true,
}) : super(key: key, listenable: position);
Widget build(BuildContext context) {
final position = listenable as Animation<Offset>;
Offset offset = position.value;
if (position.status == AnimationStatus.reverse) {
offset = Offset(-offset.dx, offset.dy);
}
return FractionalTranslation(
translation: offset,
transformHitTests: transformHitTests,
child: child,
);
}
}
上面代码中,与SlideTransition唯一的不同就是对动画的反向执行进行了定制(从左边滑出隐藏),position.status == AnimationStatus.reverse
接下来我们来使用验证
AnimatedSwitcher(
duration: Duration(milliseconds: 1000), // 新child 显示时长
reverseDuration: Duration(milliseconds: 500), // 旧child 显示时长
transitionBuilder: (Widget child, Animation<double> animation) {
var tween =
Tween<Offset>(begin: Offset(1, 0), end: Offset(0, 0));
return CustomSlideTransitionWidget(
position: tween.animate(animation), child: child);
},
child: Text(
"${count}",
// 显示指定key, 不同的key 会被认为是不同的text,不添加key,不会执行动画
key: ValueKey(count),
style: TextStyle(color: Colors.blue, fontSize: 32),
),
),
运行结果输出:
当我们点击+1 的时候,新的数字会从右侧出来,旧的数字会从左侧消失。
四、SlideTransitionX 的封装
其实本篇文章,到这里就已经写完了,但是如果我们需要实现“左入右出”、“上入下出”或者 “下入上出”怎么办?这样是不是每一种模式,都得去定义一个Transition,这样无形中会很麻烦,那么接下来,我们就来继续改造一下CustomSlideTransitionWidget 组件,把上面说的都封装进去
class CustomSlideTransitionWidget extends AnimatedWidget {
final Widget child;
final bool transformHitTests;
final AxisDirection direction;
late Tween<Offset> _tween;
CustomSlideTransitionWidget({
Key? key,
required Animation<double> position,
required this.child,
this.transformHitTests = true,
this.direction = AxisDirection.down,
}) : super(key: key, listenable: position) {
switch (direction) {
case AxisDirection.up:
_tween = Tween(begin: const Offset(0, 1), end: const Offset(0, 0));
break;
case AxisDirection.right:
_tween = Tween(begin: const Offset(-1, 0), end: const Offset(0, 0));
break;
case AxisDirection.down:
_tween = Tween(begin: const Offset(0, -1), end: const Offset(0, 0));
break;
case AxisDirection.left:
_tween = Tween(begin: const Offset(1, 0), end: const Offset(0, 0));
break;
}
}
Widget build(BuildContext context) {
final position = listenable as Animation<double>;
Offset offset = _tween.evaluate(position);
if (position.status == AnimationStatus.reverse) {
switch (direction) {
case AxisDirection.up:
offset = Offset(offset.dx, -offset.dy);
break;
case AxisDirection.right:
offset = Offset(-offset.dx, offset.dy);
break;
case AxisDirection.down:
offset = Offset(offset.dx, -offset.dy);
break;
case AxisDirection.left:
offset = Offset(-offset.dx, offset.dy);
break;
}
}
return FractionalTranslation(
translation: offset,
transformHitTests: transformHitTests,
child: child,
);
}
}
使用方法
AnimatedSwitcher(
duration: Duration(milliseconds: 1000), // 新child 显示时长
reverseDuration: Duration(milliseconds: 500), // 旧child 显示时长
transitionBuilder: (Widget child, Animation<double> animation) {
return CustomSlideTransitionWidget(
position: animation,
// 上入下出
direction: AxisDirection.down,
child: child,
);
},
child: Text(
"${count}",
// 显示指定key, 不同的key 会被认为是不同的text,不添加key,不会执行动画
key: ValueKey(count),
style: TextStyle(color: Colors.blue, fontSize: 32),
),
),
在使用的时候,我们只需要选择一个direction 就可以了
运行效果如下:
总结
本篇文章不仅记录了AnimatedSwitcher的详细用法,同时也介绍了打破AnimatedSwitcher动画对称性的方法。然后经过我们自己的封装改造,AnimatedSwitcher 在日常开发过程中,将十分的受用。