30分钟学会Flutter动画开发
前言
动画是UI交互的重要组成部分,精心设计的动画会让用户界面感觉更直观、流畅,能改善用户体验。flutter作为跨平台的UI框架,对动画提供了良好的支持和简单轻松的实现,本文对flutter动画的类型和动画方式以及第三方的动画框架做一点总结,希望对大家有所帮助。
Flutter的动画支持可以轻松实现各种动画类型。许多widget,特别是Material Design widgets, 都带有在其设计规范中定义的标准动画效果,但也可以自定义这些效果。
一、flutter动画组成要素和载体
flutter动画一般有4部分要素组成,分别是Animation、AnimationController、Curve、Tween。它们的关系如下图:
Animation<T>:代表一个动画,是动画的核心类,可以从通过animation.value拿到当前动画的值,以及读取到当前当前动画的状态(例如它是开始、停止还是向前或向后移动)
AnimationController:顾名思义就是动画的控制器,可以控制动画的开始、停止、向前向后还有重复执行等操作。在页面销毁的时候,记得要调用controller的dispose方法,否则会有内存泄漏的风险。
Curve:动画运动过程中的速率的曲线,比如可以定义匀速运动,先快后慢、先慢后快等。
Tween:动画的取值范围,默认是0.0~1.0,如果需要定义动画不同的类型和范围,可以通过Tween的begin和end来指定。
Tween通过animate方法可以生成指定区间的全新动画。
所以,四个要素组合起来,一般的动画定义代码为:
final AnimationController controller = AnimationController(duration: const Duration(milliseconds: 500), vsync: this);
final Animation curve = CurvedAnimation(parent: controller, curve: Curves.easeOut);
Animation<int> animation = IntTween(begin: 0, end: 255).animate(curve);
动画定义完成之后,在UI树中需要一个动画的载体来完成动画。flutter承载动画的组件是AnimatedWidget和ImplicitlyAnimatedWidget,为了方便开发者开发动画,AnimatedWidget和ImplicitlyAnimatedWidget提供了很多子类,AnimatedWidget的子类概览如下图:
AnimatedWidget和ImplicitlyAnimatedWidget的区别就是:AnimatedWidget需要开发者自己定义一个animation传递给它,并且自己管理AnimationController,而ImplicitlyAnimatedWidget只需要给它设定一个目标值和duration,它就会开始动画,并且内部自己管理一个AnimationController,简化了操作。
二、常用的四种动画实现
一般场景下,常用到的动画主要有以下四种:渐隐渐现动画、位移动画、旋转动画、放大缩小动画。
在flutter中,这四种动画都有对应的封装类
渐隐渐现动画 -- FadeTransition
位移动画 -- SlideTransition
旋转动画 -- RotationTransition
放大缩小动画 -- ScaleTransition
它们的用法都是类似,首先是定义一个animation,然后将animaton作为关键参数传给对应的AnimatedWidget。
下面就透明度动画和位移动画做一个简单的示例,其他两种动画都是类似的用法
首先是创建一个透明动画对象和位移动画对象:
AnimationController _controller;
Animation<double> _animation;
Animation<Offset> _offsetAnimation;
@override
void initState() {
super.initState();
_controller =
AnimationController(vsync: this, duration: Duration(seconds: 2))
..repeat(reverse: true);
_animation = CurvedAnimation(parent: _controller, curve: Curves.easeIn);
_offsetAnimation = Tween<Offset>(begin: Offset.zero, end: Offset(1.5, 0.0))
.animate(_controller);
}
然后是将animation对象作为参数传给对应的动画组件:
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
FadeTransition(
opacity: _animation,
child: FlutterLogo(
size: 150,
),
),
SlideTransition(
position: _offsetAnimation,
child: FlutterLogo(
size: 150,
),
),
],
)
这样就实现了两个动画,可以说flutter实现动画还是非常简单的,简化了开发者的工作。
如果这四种动画想混合使用,应该怎么做呢?比如在位移的过程中,还要有透明度的变化,可能还有大小的变化。
答案是将这几个AnimatedWidget嵌套使用:
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(''),
),
body: Container(
alignment: Alignment.topCenter,
child: FadeTransition(
opacity: _animation,
child: SlideTransition(
position: _offsetAnimation,
child: ScaleTransition(
scale: _animation,
child: FlutterLogo(
size: 150,
),
),
),
),
),
);
}
效果如下:
需要注意的是,这几个嵌套使用的animation,必须使用同一个AnimationController。
三、列表组动画Group Animation的实现
如果想让一个ListView里面的组件都执行一个动画,这就是group animation的概念,就像这样:
要实现这样的组动画,flutter中提供了一个AnimatedList的组件,大体的使用结构如下
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(''),
),
body: AnimatedList(
key: _listKey,
initialItemCount: itemList.length,
itemBuilder: (context, index, animation) {
return SlideTransition(
position: animation
.drive(CurveTween(curve: Curves.easeIn))
.drive(Tween<Offset>(begin: Offset(-1, 0), end: Offset(0, 0))),
child: CardItem(item: index),
);
},
),
);
}
四、第三方动画框架介绍和使用
一些优秀的第三方动画框架也对flutter进行了支持,比如lottie、flare和svga
1) Flare
Flare是一家可以快速制作矢量动画的网站,提供专门的Flutter组件来承载网站导出的动画文件,使用Flare创建的动画不仅可以有效减少安装包的体积,还能创建更加复杂绚丽的动画体验。Flare动画最早出现在2019年12月举行的Flutter技术大会上,一经发布立马受到开发者的喜爱和追捧。
作为一个专业制作矢量动画的网站,Flare提供了非常丰富的免费矢量动画。由于Flare并没有提供桌面版的开发工具,所以创建Flare动画之前需要登录Flare官网来制作Flare动画文件,如果还没有Flare账号可以先注册一个。
Flare通常以工程形式来创建和管理动画项目,目前Flare支持创建动画项目有两类,分别是Flare和Nima,它们的区别如下。
Flare:为App和Web构建实时、快速的动画,同时也支持构建游戏应用动画。
Nima:为游戏引擎和应用构建2D动画。
由于Nima主要用于构建2D游戏动画,所以如果是普通的应用开发只需要新建一个Flare项目即可
目前Flare也对flutter提供了支持,具体的使用方法可以参考pub.dev:https://pub.dev/packages/flare_flutter
import 'package:flare_flutter/flare_actor.dart';
class MyHomePage extends StatefulWidget {
@override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
return new FlareActor("assets/Filip.flr", alignment:Alignment.center, fit:BoxFit.contain, animation:"idle");
}
}
2)Lottie
Lottie是Airbnb开源的一个动画渲染库,支持多平台,包括iOS、Android、React Native以及Flutter.
- 动效设计师使用After Effects制作动画,然后使用Bodymovin导出JSON文件,可以将JSON文件放到Bodymovin网站上运行看效果,也可以放在lottiefiles网站上运行看效果,而且lottiefiles有很多免费动画JSON资源可以下载看
- 各个端使用对应的Lottie SDK加载JSON文件,实现动画效
目前Lottie也对flutter提供了支持,具体的使用方法可以参考pub.dev:https://pub.dev/packages/lottie
import 'package:flutter/material.dart';
import 'package:lottie/lottie.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: ListView(
children: [
// Load a Lottie file from your assets
Lottie.asset('assets/LottieLogo1.json'),
// Load a Lottie file from a remote url
Lottie.network(
'https://raw.githubusercontent.com/xvrh/lottie-flutter/master/example/assets/Mobilo/A.json'),
// Load an animation and its images from a zip file
Lottie.asset('assets/lottiefiles/angel.zip'),
],
),
),
);
}
}
结束语
以上就是对flutter动画的做的一点总结和介绍,当然还有很多其他类型的动画没有介绍,比如页面转场动画和共享元素动画等。丰富的动画场景带来丰富的体验,后面会继续探索。