AnimatedSwitcher
AnimatedSwitcher
动画组件来实现页面内的场景切换。AnimatedSwitcher
可以同时对其新、旧子元素添加显示、隐藏动画。也就是说在AnimatedSwitcher
的子元素发生变化时,会对其旧元素和新元素做动画。
关于AnimatedSwitcher
有一个地方需要特别注意,那就是如果切换的两个组件相同的话,AnimatedSwitcher
会以为组件没有改变,而不会进行动效切换。如果两个组件类型相同,需要使用不同的 Key
来区分,通常是使用 ValueKey
。
- 构造函数
const AnimatedSwitcher({
super.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,//布局构建器
})
transitionBuilder
:切换转变动画构建,是一个函数,定义如下,可以用这个方法来构建自己的切换动效。
typedef AnimatedSwitcherTransitionBuilder = Widget Function(Widget child, Animation<double> animation);
layoutBuilder
:可以设置新组件在组件树中的布局,也是一个函数。
typedef AnimatedSwitcherLayoutBuilder = Widget Function(Widget? currentChild, List<Widget> previousChildren);
例子:
import 'package:flutter/material.dart';
class AnimatedSwitcherPage extends StatefulWidget {
const AnimatedSwitcherPage({Key? key}) : super(key: key);
@override
State<AnimatedSwitcherPage> createState() => _AnimatedSwitcherPageState();
}
class _AnimatedSwitcherPageState extends State<AnimatedSwitcherPage> {
Widget? _animateWidget;
bool isSwitcher = false;
@override
void initState() {
// TODO: implement initState
super.initState();
_animateWidget = const Image(
image: AssetImage('images/liu.webp'),
fit: BoxFit.cover,
height: double.infinity,
key: ValueKey(1),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.black,
appBar: AppBar(
title: const Text('AnimatedSwitcher'),
backgroundColor: Colors.black,
),
body: Center(
child: Container(
padding: const EdgeInsets.all(10),
child: AnimatedSwitcher(
duration: const Duration(seconds: 1),
transitionBuilder: (child,animation){
//我们可以通过不同的动画组件实现不同的动画,下面我们用的SizeTransition组件
return SizeTransition(
sizeFactor: animation,
child: child,
);
},
child: _animateWidget,
),
),
),
floatingActionButton: FloatingActionButton(
backgroundColor: Colors.white,
child: const Text('点击',style: TextStyle(color: Colors.black),),
onPressed: (){
setState(() {
isSwitcher = !isSwitcher;
_animateWidget = isSwitcher
? const Image(
image: AssetImage('images/liu2.webp'),
fit: BoxFit.cover,
height: double.infinity,
key: ValueKey(2),
)
: const Image(
image: AssetImage('images/liu.webp'),
fit: BoxFit.cover,
height: double.infinity,
key: ValueKey(1),
);
});
},
),
);
}
}
效果:
我们也可以自定义组件,比如自定义一个图片左出右入的动画组件:替换我们的SizeTransition
组件即可
class MySlideTransition extends AnimatedWidget {
const MySlideTransition({
Key? key,
required Animation<Offset> position,
this.transformHitTests = true,
required this.child
}) :super(key: key, listenable:position);
final bool transformHitTests;
final Widget child;
@override
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,
);
}
}
transitionBuilder: (Widget child, Animation<double> animation){
var tween = Tween<Offset>(begin: Offset(1,0),end: Offset(0,0));
return MySlideTransition(
position: tween.animate(animation),
child: child
);
},
那如果要实现“左入右出”、“上入下出”或者 “下入上出”怎么办?封装一个通用的SlideTransitionX
来实现这种“出入动画”。
class SlideTransitionX extends AnimatedWidget {
SlideTransitionX({
Key? key,
required Animation<double> position,
this.transformHitTests = true,
this.direction = AxisDirection.down,
required this.child,
}) : 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;
}
}
final bool transformHitTests;
final AxisDirection direction;
final Widget child;
late final Tween<Offset> _tween;
@override
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,
);
}
}
使用:
transitionBuilder: (Widget child, Animation<double> animation){
return SlideTransitionX(
position: animation,
direction: AxisDirection.down,
child: child,
);
},