常见的显式动画有RotationTransition、FadeTransition、ScaleTransition、SlideTransition、
AnimatedIcon。在显示动画中开发者需要创建一个AnimationController,通过AnimationController
控制动画的开始、暂停、重置、跳转、倒播等。
RotationTransition(旋转动画)、 AnimationController
示例1:AnimationController普通用法
class MyHomePage extends StatefulWidget { const MyHomePage({super.key}); @override State<MyHomePage> createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin { //加 with SingleTickerProviderStateMixin late AnimationController _controller; @override void initState() { // TODO: implement initState _controller = AnimationController( vsync: this, //程序的刷新频率与手机的频率统一 //Vsync 机制可以理解为是显卡与显示器的通信桥梁,显卡在渲染每一帧之前会等待垂 // 直同步信号,只有显示器完成了一次刷新时,发出垂直同步信号,显卡才会渲染下一帧,确保刷新率和帧率保 // 持同步,以达到供需平衡的效果,防止卡顿现象。 duration: const Duration(seconds: 5), //程序的执行时间 ); } @override void dispose() { _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('Title'), ), body: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ RotationTransition( turns: _controller, //调用 child: const FlutterLogo( //Logo size: 150, ), ), const SizedBox( height: 40, ), Padding( padding: const EdgeInsets.fromLTRB(10, 0, 0, 0), child: Wrap( spacing: 10, alignment: WrapAlignment.center, children: [ ElevatedButton( onPressed: () { _controller.forward(); //正序播放一次 }, child: const Text("Forward")), ElevatedButton( onPressed: () { _controller.reverse(); //倒序播放一次 }, child: const Text("Reverse")), ElevatedButton( onPressed: () { _controller.stop(); //停止播放 }, child: const Text("Stop")), ElevatedButton( onPressed: () { _controller.reset(); //重置 }, child: const Text("rest")), ElevatedButton( onPressed: () { _controller.repeat(); //重复播放 }, child: const Text("repeat")) ], ), ) ], ), ); } }
示例2:lowerBound upperBound
AnimationController 用于控制动画,它包含动画的启动 forward() 、停止 stop() 、反向播放
reverse() 等方法。 AnimationController 会在动画的每一帧,就会生成一个新的值。默认情况
下, AnimationController 在给定的时间段内线性的生成从 0.0 到1.0(默认区间)的数字 ,我们也
可以通过 lowerbound 和 upperBound 来修改 AnimationController 生成数字的区间。
class MyHomePage extends StatefulWidget { const MyHomePage({super.key}); @override State<MyHomePage> createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin { late AnimationController _controller; @override void initState() { // TODO: implement initState _controller = AnimationController( vsync: this, //Vsync 机制可以理解为是显卡与显示器的通信桥梁,显卡在渲染每一帧之前会等待垂 // 直同步信号,只有显示器完成了一次刷新时,发出垂直同步信号,显卡才会渲染下一帧,确保刷新率和帧率保 // 持同步,以达到供需平衡的效果,防止卡顿现象。 duration: const Duration(seconds: 1), lowerBound: 3, //第三圈转到第五圈 upperBound: 5); _controller.addListener(() { print(_controller.value); }); } @override void dispose() { _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('Title'), ), body: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ RotationTransition( turns: _controller, child: const FlutterLogo( size: 100, ), ), const SizedBox( height: 40, ), Padding( padding: const EdgeInsets.fromLTRB(10, 0, 0, 0), child: Wrap( spacing: 10, alignment: WrapAlignment.center, children: [ ElevatedButton( onPressed: () { _controller.forward(); //正序播放一次 }, child: const Text("Forward")), ElevatedButton( onPressed: () { _controller.reverse(); //倒序播放一次 }, child: const Text("Reverse")), ElevatedButton( onPressed: () { _controller.stop(); //停止播放 }, child: const Text("Stop")), ElevatedButton( onPressed: () { _controller.reset(); //重置 }, child: const Text("rest")), ElevatedButton( onPressed: () { _controller.repeat(); //重复播放 }, child: const Text("repeat")) ], ), ) ], ), ); } }
FadeTransition(透明到显示)
class MyHomePage extends StatefulWidget { const MyHomePage({super.key}); @override State<MyHomePage> createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin { late AnimationController _controller; @override void initState() { // TODO: implement initState _controller = AnimationController( vsync: this, //Vsync 机制可以理解为是显卡与显示器的通信桥梁,显卡在渲染每一帧之前会等待垂 // 直同步信号,只有显示器完成了一次刷新时,发出垂直同步信号,显卡才会渲染下一帧,确保刷新率和帧率保 // 持同步,以达到供需平衡的效果,防止卡顿现象。 duration: const Duration(seconds: 1), lowerBound: 0.5, //第三圈转到第五圈 upperBound: 1 ); } @override void dispose() { // TODO: implement dispose _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('Title'), ), body: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ FadeTransition( opacity: _controller, child: const FlutterLogo(size: 80), ), const SizedBox( height: 40, ), Padding( padding: const EdgeInsets.fromLTRB(10, 0, 0, 0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ ElevatedButton( onPressed: () { _controller.forward(); //正序播放一次 }, child: const Text("Forward")), ElevatedButton( onPressed: () { _controller.reverse(); //倒序播放一次 }, child: const Text("Reverse")), ], ), ) ], ), ); } }
ScaleTransition(动画放大与缩小)、Tween
示例1:AnimationController控制动画:
class MyHomePage extends StatefulWidget { const MyHomePage({super.key}); @override State<MyHomePage> createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin { late AnimationController _controller; @override void initState() { // TODO: implement initState _controller = AnimationController( vsync: this, //Vsync 机制可以理解为是显卡与显示器的通信桥梁,显卡在渲染每一帧之前会等待垂 // 直同步信号,只有显示器完成了一次刷新时,发出垂直同步信号,显卡才会渲染下一帧,确保刷新率和帧率保 // 持同步,以达到供需平衡的效果,防止卡顿现象。 duration: const Duration(seconds: 1), ); } @override void dispose() { _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('Title'), ), body: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ ScaleTransition( scale: _controller, child: const FlutterLogo(size: 80), ), const SizedBox( height: 40, ), Padding( padding: const EdgeInsets.fromLTRB(10, 0, 0, 0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ ElevatedButton( onPressed: () { _controller.forward(); //正序播放一次 }, child: const Text("Forward")), ElevatedButton( onPressed: () { _controller.reverse(); //倒序播放一次 }, child: const Text("Reverse")), ], ), ) ], ), ); } }
示例2: AnimationController结合Tween控制动画:
默认情况下, AnimationController 对象值的范围是[0.0,1.0]。如果我们需要构建UI的动画值在不同
的范围或不同的数据类型,则可以使用 Tween 来添加映射以生成不同的范围或数据类型的值
class MyHomePage extends StatefulWidget { const MyHomePage({super.key}); @override State<MyHomePage> createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin { late AnimationController _controller; @override void initState() { // TODO: implement initState _controller = AnimationController( vsync: this, //Vsync 机制可以理解为是显卡与显示器的通信桥梁,显卡在渲染每一帧之前会等待垂 // 直同步信号,只有显示器完成了一次刷新时,发出垂直同步信号,显卡才会渲染下一帧,确保刷新率和帧率保 // 持同步,以达到供需平衡的效果,防止卡顿现象。 duration: const Duration(seconds: 1), ); } @override void dispose() { _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('Title'), ), body: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ ScaleTransition( scale: _controller.drive(Tween(begin: 1, end: 2)), //动画开始与结束的大小 child: const FlutterLogo(size: 80), ), const SizedBox( height: 40, ), Padding( padding: const EdgeInsets.fromLTRB(10, 0, 0, 0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ ElevatedButton( onPressed: () { _controller.forward(); //正序播放一次 }, child: const Text("Forward")), ElevatedButton( onPressed: () { _controller.reverse(); //倒序播放一次 }, child: const Text("Reverse")), ], ), ) ], ), ); } }
SlideTransition(位移)
这是一负责平移的显示动画组件,使用时需要通过position属性传入一个Animated表示位移程度,通常
借助Tween实现。
示例1: _controller.drive驱动动画
class MyHomePage extends StatefulWidget { const MyHomePage({super.key}); @override State<MyHomePage> createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin { late AnimationController _controller; @override void initState() { // TODO: implement initState _controller = AnimationController( vsync: this, //Vsync 机制可以理解为是显卡与显示器的通信桥梁,显卡在渲染每一帧之前会等待垂 // 直同步信号,只有显示器完成了一次刷新时,发出垂直同步信号,显卡才会渲染下一帧,确保刷新率和帧率保 // 持同步,以达到供需平衡的效果,防止卡顿现象。 duration: const Duration(seconds: 1), ); } @override void dispose() { _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('Title'), ), body: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ SlideTransition( position: _controller.drive(Tween( begin: const Offset(0, 0), end: const Offset(1.2, 0) //表示实际的位置向右移动自身宽度的1.2倍 )), child: const FlutterLogo(size: 80), ), const SizedBox( height: 40, ), Padding( padding: const EdgeInsets.fromLTRB(10, 0, 0, 0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ ElevatedButton( onPressed: () { _controller.forward(); //正序播放一次 }, child: const Text("Forward")), ElevatedButton( onPressed: () { _controller.reverse(); //倒序播放一次 }, child: const Text("Reverse")), ], ), ) ], ), ); } }
示例2:Tween.animate 驱动动画
class MyHomePage extends StatefulWidget { const MyHomePage({super.key}); @override State<MyHomePage> createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin { late AnimationController _controller; @override void initState() { // TODO: implement initState _controller = AnimationController( vsync: this, //Vsync 机制可以理解为是显卡与显示器的通信桥梁,显卡在渲染每一帧之前会等待垂 // 直同步信号,只有显示器完成了一次刷新时,发出垂直同步信号,显卡才会渲染下一帧,确保刷新率和帧率保 // 持同步,以达到供需平衡的效果,防止卡顿现象。 duration: const Duration(seconds: 1), ); } @override void dispose() { _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('Title'), ), body: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ SlideTransition( position: Tween( begin: const Offset(0, 0), end: const Offset(1.2, 0) //表示实际的位置向右移动自身宽度的1.2倍 ) .animate(_controller), child: const FlutterLogo(size: 80), ), const SizedBox( height: 40, ), Padding( padding: const EdgeInsets.fromLTRB(10, 0, 0, 0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ ElevatedButton( onPressed: () { _controller.forward(); //正序播放一次 }, child: const Text("Forward")), ElevatedButton( onPressed: () { _controller.reverse(); //倒序播放一次 }, child: const Text("Reverse")), ], ), ) ], ), ); } }
示例3: 链式操作修改动画效果
class MyHomePage extends StatefulWidget { const MyHomePage({super.key}); @override State<MyHomePage> createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin { late AnimationController _controller; @override void initState() { // TODO: implement initState _controller = AnimationController( vsync: this, //Vsync 机制可以理解为是显卡与显示器的通信桥梁,显卡在渲染每一帧之前会等待垂 // 直同步信号,只有显示器完成了一次刷新时,发出垂直同步信号,显卡才会渲染下一帧,确保刷新率和帧率保 // 持同步,以达到供需平衡的效果,防止卡顿现象。 duration: const Duration(seconds: 1), ); } @override void dispose() { _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('Title'), ), body: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ SlideTransition( position: Tween( begin: const Offset(0, -1), end: const Offset(0, 0.8) //表示实际的位置向右移动自身宽度的1.2倍 ) .chain(CurveTween(curve: Curves.bounceIn)) //运动的曲线即运动的效果 .animate(_controller), child: const FlutterLogo(size: 80), ), const SizedBox( height: 40, ), Padding( padding: const EdgeInsets.fromLTRB(10, 0, 0, 0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ ElevatedButton( onPressed: () { _controller.forward(); //正序播放一次 }, child: const Text("Forward")), ElevatedButton( onPressed: () { _controller.reverse(); //倒序播放一次 }, child: const Text("Reverse")), ], ), ) ], ), ); } }
示例4: 链式操作修改动动画执行时间(交错式动画)
class MyHomePage extends StatefulWidget { const MyHomePage({super.key}); @override State<MyHomePage> createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin { late AnimationController _controller; @override void initState() { // TODO: implement initState _controller = AnimationController( vsync: this, //Vsync 机制可以理解为是显卡与显示器的通信桥梁,显卡在渲染每一帧之前会等待垂 // 直同步信号,只有显示器完成了一次刷新时,发出垂直同步信号,显卡才会渲染下一帧,确保刷新率和帧率保 // 持同步,以达到供需平衡的效果,防止卡顿现象。 duration: const Duration(seconds: 3), ); } @override void dispose() { _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('Title'), ), body: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ SlideTransition( position: Tween( begin: const Offset(0, -1), end: const Offset(0, 0.6) //表示实际的位置向右移动自身宽度的1.2倍 ) .chain(CurveTween(curve: Curves.bounceIn)) .chain(CurveTween(curve: const Interval(0.6, 0.8)))//最后的百分之20的时间完成动画 .animate(_controller), child: const FlutterLogo(size: 80), ), const SizedBox( height: 40, ), Padding( padding: const EdgeInsets.fromLTRB(10, 0, 0, 0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ ElevatedButton( onPressed: () { _controller.forward(); //正序播放一次 }, child: const Text("Forward")), ElevatedButton( onPressed: () { _controller.reverse(); //倒序播放一次 }, child: const Text("Reverse")), ], ), ) ], ), ); } }
AnimatedIcon
AnimatedIcon顾名思义,是一个用于提供动画图标的组件,它的名字虽然是以Animated开头,但是他
是一个显式动画组件,需要通过progress属性传入动画控制器,另外需要由Icon属性传入动画图标数据
class MyHomePage extends StatefulWidget { const MyHomePage({super.key}); @override State<MyHomePage> createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> with SingleTickerProviderStateMixin { late AnimationController _controller; @override void initState() { // TODO: implement initState super.initState(); _controller = AnimationController(vsync: this, duration: const Duration(seconds: 1)); //过度的时间为一秒 } @override void dispose() { // TODO: implement dispose super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( floatingActionButton: FloatingActionButton( child: const Icon(Icons.add), onPressed: () { _controller.forward(); }, ), appBar: AppBar( title: const Text('Title'), ), body: Center( child: AnimatedIcon( icon: AnimatedIcons.menu_close, progress: _controller, size: 40), //这是menu到close的过度 ), ); } }