目录
- 基础动画概念和动画类
- 原始无动画部件
- 动画示例1(从小变大过渡)
- 动画示例2(使用 AnimatedWidget )
- 动画示例3(无限循环动画)
- 动画示例4(分离部件与动画)
- 动画示例5(非线性动画)
基础动画概念和动画类
Animation
Flutter动画库的核心类,任何动画都基于Animation
Animation
对象可以知道当前动画的状态(value
值),不过屏幕上显示的效果是一概不知的,即Animation
对象只负责动画过程中的数值变化AnimationController
管理动画,例如播放、反转,停用等CurvedAnimation
用于实现非线性动画,如快进慢出,慢进快出Tween
区间过渡插值,如数字从0到255,动画从黄到绿,分别可以用IntTween
、ColorTween
原始无动画部件
下面是一个没有动画,宽高固定为300的部件(Widget)
import 'package:flutter/material.dart';void main() => runApp(LogoApp());class LogoApp extends StatefulWidget { _LogoAppState createState() => _LogoAppState();}class _LogoAppState extends Statewith SingleTickerProviderStateMixin { @override Widget build(BuildContext context) { return Center( child: Container( margin: EdgeInsets.symmetric(vertical: 10), // 宽高固定 height: 300, width: 300, child: FlutterLogo(), ), ); }}复制代码
动画示例1(从小变大过渡)
实现图片从小到大(从宽高为0到宽高为300),步骤如下:
- 引入动画库
import 'package:flutter/animation.dart';
,并混入SingleTickerProviderStateMixin
- 创建动画控制类,此处可以定义动画的时长
- 利用
Tween
创建动画类,令值在0~300之间变化 - 利用
addListener
方法调用setState
更新value
import 'package:flutter/material.dart';import 'package:flutter/animation.dart';void main() => runApp(LogoApp());class LogoApp extends StatefulWidget { _LogoAppState createState() => _LogoAppState();}class _LogoAppState extends Statewith SingleTickerProviderStateMixin { // 定义动画变量 Animation animation; AnimationController controller; @override void initState() { super.initState(); // 实例化控制类,时长3秒, vsync可以在当前动画对用户不可见时节省机器资源(不进行动画计算) controller = AnimationController(duration: const Duration(seconds: 2), vsync: this); // 在0~300之间变动 animation = Tween (begin: 0, end: 300).animate(controller) ..addListener(() { // 通过setState令动画中的value产生变化 setState(() {}); }); // 开始播放动画 controller.forward(); } @override Widget build(BuildContext context) { return Center( child: Container( margin: EdgeInsets.symmetric(vertical: 10), // setState时value的变化产生在这里 height: animation.value, width: animation.value, child: FlutterLogo(), ), ); } @override void dispose() { // 注销动画实例,释放内存 controller.dispose(); super.dispose(); }}复制代码
动画示例2(使用 AnimatedWidget )
使用AnimatedWidget
组件简化示例1的代码,不再需要手动调用setState
,步骤如下:
- 使用
AnimatedWidget
创建动画部件 - 在构造参数中接收
Animation
参数,并传递给父构造函数的listenable
参数 - 在
build
中通过listenable
获得动画中的value
import 'package:flutter/material.dart';import 'package:flutter/animation.dart';void main() => runApp(LogoApp());class LogoApp extends StatefulWidget { _LogoAppState createState() => _LogoAppState();}class _LogoAppState extends Statewith SingleTickerProviderStateMixin { Animation animation; AnimationController controller; @override void initState() { super.initState(); controller = AnimationController(duration: const Duration(seconds: 2), vsync: this); // 不再需要手动setState animation = Tween (begin: 0, end: 300).animate(controller); controller.forward(); } @override Widget build(BuildContext context) => AnimatedLogo( animation: animation, ); @override void dispose() { controller.dispose(); super.dispose(); }}class AnimatedLogo extends AnimatedWidget { // 构造参数中的第二个参数为Animation,传递给父构造函数的listenable AnimatedLogo({Key key, Animation animation}) : super(key: key, listenable: animation); Widget build(BuildContext context) { // 通过listenable获取value final Animation animation = listenable; return Center( child: Container( margin: EdgeInsets.symmetric(vertical: 10), height: animation.value, width: animation.value, child: FlutterLogo(), ), ); }}复制代码
动画示例3(无限循环动画)
监控动画状态进度,并在过程中根据状态改变动画,实现动画的无限循环,步骤如下:
- 增加状态监听
addStatusListener
- 在监听到动画状态为
completed
(播放完毕)时,调用reverse()
方法反转动画,使动画回到初始状态 - 动画回到初始状态后停止时,状态为
dismissed
,此时再调用forward()
方法继续播放,实现无限循环
import 'package:flutter/material.dart';import 'package:flutter/animation.dart';void main() => runApp(LogoApp());class LogoApp extends StatefulWidget { _LogoAppState createState() => _LogoAppState();}class _LogoAppState extends Statewith SingleTickerProviderStateMixin { Animation animation; AnimationController controller; @override void initState() { super.initState(); controller = AnimationController(duration: const Duration(seconds: 2), vsync: this); /*** * 通过addStatusListener获得当前动画进度,实现无限循环的动画 * * AnimationStatus的四个状态 * 1. AnimationStatus.forward 动画播放 * 2. AnimationStatus.completed 动画播放完成 * 3. AnimationStatus.dismissed 动画回到初始状态后停止 * 4. AnimationStatus.reverse 动画反转 */ animation = Tween (begin: 0, end: 300).animate(controller) ..addStatusListener((status) { if (status == AnimationStatus.completed) { // 动画完成后反转 controller.reverse(); } else if (status == AnimationStatus.dismissed) { // 反转回初始状态时继续播放,实现无限循环 controller.forward(); } }); // 播放动画 controller.forward(); } @override Widget build(BuildContext context) => AnimatedLogo( animation: animation, ); @override void dispose() { controller.dispose(); super.dispose(); }}class AnimatedLogo extends AnimatedWidget { // 构造参数中的第二个参数为Animation,传递给父构造函数的listenable AnimatedLogo({Key key, Animation animation}) : super(key: key, listenable: animation); Widget build(BuildContext context) { // 通过listenable获取value final Animation animation = listenable; return Center( child: Container( margin: EdgeInsets.symmetric(vertical: 10), height: animation.value, width: animation.value, child: FlutterLogo(), ), ); }}复制代码
动画示例4(分离部件与动画)
在前面的动画中可以看到,部件显示的内容和动画混合在一起了,Flutter官网给出了一个可以使动画和部件内容分离的写法 按我理解,所有只是改变宽高(仅当前示例)的内容部件都可以使用这个GrowTransition动画过渡部件 步骤如下:
- 将部件内容创建为一个单独的内容部件
- 将过渡动画也创建为一个单独的部件,并使用
AnimatedBuilder
编写过渡的部分 - 将动画部件和内容部件组合在一起
import 'package:flutter/material.dart';import 'package:flutter/animation.dart';void main() => runApp(LogoApp());class LogoApp extends StatefulWidget { _LogoAppState createState() => _LogoAppState();}class _LogoAppState extends Statewith SingleTickerProviderStateMixin { Animation animation; AnimationController controller; @override void initState() { super.initState(); // 创建动画 controller = AnimationController(duration: const Duration(seconds: 2), vsync: this); animation = Tween (begin: 0, end: 300).animate(controller); controller.forward(); } // 将内容部件和动画组合起来 @override Widget build(BuildContext context) => GrowTransition( animation: animation, child: LogoWidget(), ); @override void dispose() { controller.dispose(); super.dispose(); }}// 内容部件class LogoWidget extends StatelessWidget { Widget build(BuildContext context) => Container( margin: EdgeInsets.symmetric(vertical: 10), child: FlutterLogo(), );}// 动画过渡部件class GrowTransition extends StatelessWidget { GrowTransition({this.child, this.animation}); // 需要有动画过渡效果的部件 final Widget child; // 为部件使用的动画 final Animation animation; Widget build(BuildContext context) => Center( /** * AnimatedBuilder将宽高定义为过渡状态 * 按我理解,所有只是改变宽高的内容部件都可以使用这个GrowTransition动画过渡部件 * 当然也可以定义一个只改变透明度的动画过渡部件复用 */ child: AnimatedBuilder( animation: animation, builder: (context, child) => Container( height: animation.value, width: animation.value, child: child, ), child: child), );}复制代码
动画示例5(非线性动画)
非线性动画很好理解,就慢进快出,快进慢出等。使用CurvedAnimation
类,步骤如下:
- 创建
AnimationController
,定义相关参数 - 创建
CurvedAnimation
,是得其parent
参数为前一步创建的动画控制器,参数curve
为动画效果 - 创建
Tween
过渡值,调用evaluate()
方法,参数即为上一步创建的CurvedAnimation
对象 - 接着就调用
forward()
播放动画即可
import 'package:flutter/material.dart';import 'package:flutter/animation.dart';void main() => runApp(LogoApp());class LogoApp extends StatefulWidget { _LogoAppState createState() => _LogoAppState();}class _LogoAppState extends Statewith SingleTickerProviderStateMixin { Animation animation; AnimationController controller; @override void initState() { super.initState(); controller = AnimationController(duration: const Duration(seconds: 2), vsync: this); // 曲线动画的核心的代码 animation = CurvedAnimation(parent: controller, curve: Curves.easeOut); controller.forward(); } @override Widget build(BuildContext context) => AnimatedLogo( animation: animation, ); @override void dispose() { controller.dispose(); super.dispose(); }}class AnimatedLogo extends AnimatedWidget { // 曲线动画使用的过渡效果 static final _sizeTween = Tween (begin: 0, end: 300); // 构造参数中的第二个参数为Animation,传递给父构造函数的listenable AnimatedLogo({Key key, Animation animation}) : super(key: key, listenable: animation); Widget build(BuildContext context) { // 通过listenable获取value final Animation animation = listenable; return Center( child: Container( margin: EdgeInsets.symmetric(vertical: 10), // 曲线动画部分开始 height: _sizeTween.evaluate(animation), width: _sizeTween.evaluate(animation), // 曲线动画部分结束 child: FlutterLogo(), ), ); }}复制代码
补充,另一个简单示例(修改动画示例1即可)
/** * 非曲线动画的的另一个示例 * 把animate的参数从controller改成animate即可 */ final Animation curve = CurvedAnimation(parent: controller, curve: Curves.easeOut); animation = Tween(begin: 0, end: 300).animate(curve); controller.forward();复制代码