Flutter动画详解第二篇之显式动画(Explicit Animations)
目录
前言
一、定义
1.AnimationController
1.常用属性
1. value
2. status
3. duration
2.常用方法
1.forward
2.reverse
3.repeat
4.stop
5. reset
6. animateTo(double target, {Duration? duration, Curve curve = Curves.linear})
7.animateBack(double target, {Duration? duration, Curve curve = Curves.linear})
8. addListener(VoidCallback listener)
10.addStatusListener(AnimationStatusListener listener)
11. removeStatusListener(AnimationStatusListener listener)
移除状态监听器。
2.Animation
3.Tween
4.Listeners
5.Builders
二、常见的显示动画组件
1.RotationTransition
2.FadeTransition
3.ScaleTransition
4.SlideTransition
三、自定义显式动画
四、参考文章
前言
上一篇文章介绍了Flutter中的隐式动画的用法。
我们会发现隐式动画使用起来非常的方便,我们只需要设置动画的旧值变化之后的值,Flutter会帮助我们完整动画的中间过程。Flutter中隐式动画的实现是全自动的。
今天我们介绍下Flutter中的显式动画。
一、定义
在Flutter中,显式动画(Explicit Animations)指的是那些需要你手动控制动画过程的动画。显式动画提供了更多的控制权,但也需要更多的代码和管理。显式动画通常涉及到以下几个核心组件:
- AnimationController
- Animation
- Tween
- Listeners
- Builders
核心组件的详解如下:
1.AnimationController
AnimationController是 Flutter 动画框架的核心部分之一,用于控制动画的播放、停止、前进、倒退等。它提供了丰富的 API 来管理和控制动画的行为。下面是 AnimationController的一些常用 API 及其解释:
1.常用属性
1. value
当前动画的值。可以是 `double` 类型,表示动画当前的进度。
double currentValue = controller.value;
2. status
当前动画的状态,是AnimationStatus枚举类型,可能的值有dismissed、forward、reverse 和 completed。
AnimationStatus currentStatus = controller.status;
3. duration
动画的时长。
controller.duration = Duration(seconds: 2);
4. upperBound和 lowerBound
动画的范围。
controller.upperBound = 1.0;
controller.lowerBound = 0.0;
2.常用方法
1.forward
动画正向执行,从lowerBound到upperBound。
controller.forward();
2.reverse
动画反向执行,从upperBound到lowerBound
controller.reverse();
3.repeat
重复执行动画,可以指定周期和是否反向。
controller.repeat(reverse: true);
4.stop
停止动画。
controller.stop();
5. reset
重置动画到 `lowerBound`,并停止动画。
controller.reset();
6. animateTo(double target, {Duration? duration, Curve curve = Curves.linear})
动画执行到指定值。
controller.animateTo(0.5, duration: Duration(seconds: 1), curve: Curves.easeInOut);
7.animateBack(double target, {Duration? duration, Curve curve = Curves.linear})
动画反向执行到指定值。
controller.animateBack(0.0, duration: Duration(seconds: 1), curve: Curves.easeInOut);
8. addListener(VoidCallback listener)
添加监听器,每次动画值改变时调用。
controller.addListener(() {
setState(() {
// 更新UI
});
});
9. removeListener(VoidCallback listener)
移除监听器。
controller.removeListener(listener);
10.addStatusListener(AnimationStatusListener listener)
添加状态监听器,动画状态改变时调用。
controller.addStatusListener((status) {
if (status == AnimationStatus.completed) {
// 动画完成
}
});
11. removeStatusListener(AnimationStatusListener listener)
移除状态监听器。
controller.removeStatusListener(listener);
2.Animation
在 Flutter 中,显式动画需要开发者手动管理动画的每个步骤,其中 Animation 类是核心组件之一。Animation 类本身是一个抽象类,它定义了动画的当前值和状态,并且可以被监听以响应动画的变化。通过 Animation 类,开发者可以访问动画的值并将其应用到 UI 元素上。
在实际做动画的过程中,我们会用到各种各样的Animation。例如我们做缩放动画的时候,Animation的类型为double类型,渐变动画的时候,Animation可以表示颜色的动画,平移动画的时候,Animation可以表示平移的大小。
3.Tween
定义动画的开始和结束值。常见的有 Tween、ColorTween 等。
4.Listeners
通过 addListener 和 addStatusListener 可以监听动画的每一帧和动画状态的变化。
5.Builders
通过 AnimatedBuilder 或 CustomPainter 等将动画值应用到UI上。
二、常见的显示动画组件
显示动画都以Transition结尾。常见的显示动画有RotateAnimation动画、FadeTransition、ScaleTransition、SlideTransition、AnimatedIcon。在显示动画中,我们通过AnimatedController控制动画的开始、暂停、重置、跳转、倒播等。
1.RotationTransition
RotationTransition主要用来做旋转动画。
我们以下面的效果为例,看看如何使用RotationTransition动画。
图1.RotationTransition动画
我们要做一个不停旋转的FlutterLogo。
首先我们设置FlutterLogo的大小为60。
我们要做一个不停旋转的显式动画,因此我们需要再FlutterLogo的外面使用RotationTransition包裹起来。
代码如下:
@override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('RotateAnimation动画'), ), body: Center( child: RotationTransition( turns: controller, child: const FlutterLogo( size: 60, ), ), ), ); }
我们看一下RotatinTransition的定义:
const RotationTransition({ super.key, required Animation turns, super.alignment = Alignment.center, super.filterQuality, super.child, }) : super(animation: turns, onTransform: _handleTurnsMatrix);
这里必须要传递AnimationController对象。因此我们在定义AnimationController。
// 定义AnimationController late AnimationController controller;
为了让程序和手机的刷新频率保持一致,我们的Stateful后面要实现SingleTickerProviderStateMixin。
代码如下:
class _RotateAnimationDemosState extends State with SingleTickerProviderStateMixin{ // 定义AnimationController late AnimationController controller; }
然后我们Widget的初始化方法中,初始化我们的AnimationController。
这里我们设置下动画的时长,vsync参数传this。表示当前app和手机刷新的频率保持一致。
//初始化 AnimationController @override void initState() { super.initState(); //vsync: 让程序和手机的刷新频率统一 controller = AnimationController(duration: const Duration(seconds: 3), vsync: this); }
OK.到这里之后,完整的代码如下:
import 'package:flutter/material.dart'; class RotateAnimationDemos extends StatefulWidget { const RotateAnimationDemos({super.key}); @override State createState() => _RotateAnimationDemosState(); } class _RotateAnimationDemosState extends State with SingleTickerProviderStateMixin { // 定义AnimationController late AnimationController controller; //初始化 AnimationController @override void initState() { super.initState(); //vsync: 让程序和手机的刷新频率统一 controller = AnimationController(duration: const Duration(seconds: 3), vsync: this); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('RotateAnimation动画'), ), body: Center( child: RotationTransition( turns: controller, child: const FlutterLogo( size: 60, ), ), ), ); } }
运行代码之后,页面加载出来了,但是FlutterLogo没有转起来。
因为显式动画是手动控制动画的播放,因此我们还需要手动调用下AnimationController的repeat方法。
//初始化 AnimationController @override void initState() { super.initState(); //vsync: 让程序和手机的刷新频率统一 controller = AnimationController(duration: const Duration(seconds: 3), vsync: this)..repeat(); }
再次运行,就发现FlutterLogo旋转起来了。
完整代码如下:
import 'package:flutter/material.dart'; class RotateAnimationDemos extends StatefulWidget { const RotateAnimationDemos({super.key}); @override State createState() => _RotateAnimationDemosState(); } class _RotateAnimationDemosState extends State with SingleTickerProviderStateMixin { // 定义AnimationController late AnimationController controller; //初始化 AnimationController @override void initState() { super.initState(); //vsync: 让程序和手机的刷新频率统一 controller = AnimationController(duration: const Duration(seconds: 3), vsync: this)..repeat(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('RotateAnimation动画'), ), body: Center( child: RotationTransition( turns: controller, child: const FlutterLogo( size: 60, ), ), ), ); } }
上述调用了AnimationController的repeat方法,AnimationController还提供了forward、reverse、stop、reset等常用方法,它们的含义如下:
- forward:动画仅执行一次
- reverse:动画倒序执行一次
- stop:动画停止
- reset:动画重置
- repeat:重复的执行动画
如果感兴趣,可以逐个调用这些方法看看效果。
2.FadeTransition
FadeTransition用于制作透明度动画。
FadeTransition和RotationTransition的用法基本差不多。
下面的例子中,展示了使用FadeTransition制作动画的过程。
图2.FadeTransition动画
完整代码如下:
import 'package:flutter/material.dart'; class FadeAnimationDemos extends StatefulWidget { const FadeAnimationDemos({super.key}); @override State createState() => _FadeAnimationDemosState(); } class _FadeAnimationDemosState extends State with SingleTickerProviderStateMixin{ // 定义AnimationController late AnimationController controller; //初始化 AnimationController @override void initState() { super.initState(); //vsync: 让程序和手机的刷新频率统一 controller = AnimationController( duration: const Duration(seconds: 1), vsync: this, lowerBound: 0.1, upperBound: 1.0, )..repeat(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('FadeTransition动画'), ), body: Center( child:FadeTransition( opacity: controller, child: const FlutterLogo(size: 200,), ), ), ); } }
3.ScaleTransition
ScaleTransition用于制作缩放动画,依然可以使用AnimationController更加精准的控制动画的细节。
图3.缩放动画
完整代码如下:
import 'package:flutter/material.dart'; class ScaleTransitionDemos extends StatefulWidget { const ScaleTransitionDemos({super.key}); @override State createState() => _ScaleTransitionDemosState(); } class _ScaleTransitionDemosState extends State with SingleTickerProviderStateMixin{ // 定义AnimationController late AnimationController controller; //初始化 AnimationController @override void initState() { super.initState(); //vsync: 让程序和手机的刷新频率统一 controller = AnimationController( duration: const Duration(seconds: 1), vsync: this, lowerBound: 0.1, upperBound: 1.0, )..repeat(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('ScaleTransition动画'), ), body: Center( child:ScaleTransition( scale: controller, child: Container( width: 100, height: 100, color: Colors.deepPurpleAccent, ), ), ), ); } }
4.SlideTransition
SlideTransition用于做平移动画。
下图是使用SlideTransition制作的平移动画。
图4.SlideTransition动画
完整代码如下:
import 'package:flutter/material.dart'; class SlideTransitionDemos extends StatefulWidget { const SlideTransitionDemos({super.key}); @override State createState() => _SlideTransitionDemosState(); } class _SlideTransitionDemosState extends State with SingleTickerProviderStateMixin{ // 定义AnimationController late AnimationController controller; //初始化 AnimationController @override void initState() { super.initState(); //vsync: 让程序和手机的刷新频率统一 controller = AnimationController( duration: const Duration(seconds: 1), vsync: this, lowerBound: 0.1, upperBound: 1.0, )..repeat(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('SlideTransition动画'), ), body: Center( child:SlideTransition( position: controller.drive(Tween(begin: const Offset(0, 0), end: const Offset(0.5, 0))), child: const FlutterLogo(size: 200,), ), ), ); } }
我们还可以使用AnimationController的drive方法修改动画的初始值。
例如在下面的缩放动画代码中,开始的时候,长度和宽度都是从0.5倍开始,2倍结束。
部分代码如下:
Center( child:ScaleTransition( scale: controller.drive(Tween(begin: 0.5, end: 2.0)), child: Container( width: 100, height: 100, color: Colors.deepPurpleAccent, ), ), )
还可以调用AnimationController的chain方法叠加动画的曲线。
三、自定义显式动画
这里我们看一下如何自定义显式动画。
例如我们这里有一个透明度为0.9的Container组件,代码如下:
import 'package:flutter/material.dart'; class CustomExplicitPage extends StatefulWidget { const CustomExplicitPage({super.key}); @override State createState() => _CustomExplicitPageState(); } class _CustomExplicitPageState extends State with SingleTickerProviderStateMixin { late AnimationController _controller; @override void initState() { super.initState(); _controller = AnimationController(vsync: this); } @override void dispose() { _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('自定义显示动画'), ), body: Center( child: Opacity( opacity: 0.9, child: Container( alignment: Alignment.center, color: Colors.deepPurpleAccent, width: 250, height: 250, child: const Text('Hi',style: TextStyle(fontSize: 24),), ), ), ), ); } }
我们看下如何使用AnimatedBuilder实现自定义显式动画。
首先我们把需要制作动画的Widget的外层包裹一个AnimatedBuilder。builder函数内部返回要操作的Widget,把AnimatedController传给AnimatedBuilder的animation属性。
部分代码如下:
Center( child: AnimatedBuilder( animation: _controller, builder: (BuildContext context, Widget? child) { return Opacity( opacity: 0.9, child: Container( alignment: Alignment.center, color: Colors.deepPurpleAccent, width: 250, height: 250, child: const Text('Hi',style: TextStyle(fontSize: 24),), ), ); }, ), )
然后调用AnimationController的repeat方法,一个自定义的显式动画就实现了。
完整代码如下:
import 'package:flutter/material.dart'; class CustomExplicitPage extends StatefulWidget { const CustomExplicitPage({super.key}); @override State createState() => _CustomExplicitPageState(); } class _CustomExplicitPageState extends State with SingleTickerProviderStateMixin { late AnimationController _controller; @override void initState() { super.initState(); _controller = AnimationController(vsync: this,duration: const Duration(seconds: 2))..repeat(); } @override void dispose() { _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('自定义显示动画'), ), body: Center( child: AnimatedBuilder( animation: _controller, builder: (BuildContext context, Widget? child) { return Opacity( opacity: _controller.value, child: Container( alignment: Alignment.center, color: Colors.deepPurpleAccent, width: 250, height: 250, child: const Text('Hi',style: TextStyle(fontSize: 24),), ), ); }, ), ), ); } }
完整的效果如下:
图5.自定义显式动画
四、参考文章
1.教程 | Flutter 中文文档 - Flutter 中文开发者网站 - Flutter