前言
动画是flutter中必学的知识点,动画的基础知识不多,学习flutter动画其实主要是学习下面这些类的使用,当然,如果要深度了解的话,还需要了解他们的源码。
- Animation
- AnimationController
- Tween
- AnimatedWidget
- AnimatedBuilder
- CurvedAnimation
- Hero
1. Animation
1. 基本概念
Animation是一个抽象类,CurvedAnimation(曲线动画)的父类,记录了当前动画的进度所代表的具体值和动画状态,内部有抽象方法addListener和addStatusListener,addListener可以监听当前动画的进度对应的具体值,addStatusListener可以监听当前动画的状态,有以下四种状态:
enum AnimationStatus {
/// The animation is stopped at the beginning
dismissed,
/// The animation is running from beginning to end
forward,
/// The animation is running backwards, from end to beginning
reverse,
/// The animation is stopped at the end
completed,
}
复制代码
每种状态的含义注释写的很明白了,就不再解释。
2. AnimationController
1. 基本概念
AnimationController 是一个动画控制器,它可以控制动画的开始和停止,控制动画正向还是反向播放,控制动画的执行时间,控制动画值的上下限。AnimationController的构造函数参数如下:
- duration:正方向动画的时间长度
- reverseDuration:反方向动画的时间长度
- lowerBound:动画值的下限,默认0
- upperBound:动画值的上限,默认1
- vsync:如果是在 State 中创建AnimationController,那么可以使用
SingleTickerProviderStateMixin,它的主要作用是获取每一帧刷新的通知,相当于给动画添加了一个动起来的引擎
AnimationController({
double? value,
this.duration,
this.reverseDuration,
this.debugLabel,
this.lowerBound = 0.0,
this.upperBound = 1.0,
this.animationBehavior = AnimationBehavior.normal,
required TickerProvider vsync,
})
复制代码
2. 使用
如下所示,在state类里面创建了一个动画控制器,正向播放,时间长度2秒,state类继承自
SingleTickerProviderStateMixin
AnimationController _animationController = AnimationController(duration: Duration(seconds: 2), vsync: this);
复制代码
3. Tween
1. 基本概念
Tween的代码如下:
class Tween<T extends dynamic> extends Animatable<T> {
Tween({
this.begin,
this.end,
});
T? begin;
T? end;
@protected
T lerp(double t) {
assert(begin != null);
assert(end != null);
return begin + (end - begin) * t as T;
}
@override
T transform(double t) {
if (t == 0.0)
return begin as T;
if (t == 1.0)
return end as T;
return lerp(t);
}
}
复制代码
Tween有一个dynamic的泛型限制,所以这个泛型可以是任意类型,Tween内部有 begin和end
两个成员属性,还有lerp和transform方法,这些代码很简单,其实就是用于将当前的动画进度转化成
begin和end两个成员变量的具体的中间值,也就是说,Tween的作用就是在给定的泛型限制下根据当前动画进度(0.0-1.0)获取动画进行的过程中的begin和end成员变量之间对应的具体值
2. 使用
1. 动画效果
如下图所示,一个黑色的方块由小变大
2. 代码
如下代码所示,先在21行创建一个AnimationController,正向执行动画,时间2s,再在22行创建一个Tween对象,起始值和结束值为0.0和200.0,然后在23行通过addListener方法设置监听,在监听的回调方法中通过_animation.value获取当前动画的进度的具体值,然后调用setState(() {})刷新页面。
mport 'dart:ffi';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/src/widgets/framework.dart';
class TweenStudy extends StatefulWidget {
@override
State<StatefulWidget> createState() => TweenState();
}
class TweenState extends State with SingleTickerProviderStateMixin {
Animation<double> _animation;
AnimationController _animationController;
double animationValue = 10;
Color color = Colors.black;
@override
void initState() {
super.initState();
_animationController = AnimationController(duration: Duration(seconds: 2), vsync: this);
_animation = Tween<double>(begin: 0.0, end: 200.0).animate(_animationController)
..addListener(() {
animationValue = _animation.value;
setState(() {});
})
..addStatusListener((status) {});
_animationController.forward();
}
@override
Widget build(BuildContext context) {
return new MaterialApp(
theme: ThemeData(primaryColor: Colors.white),
home: Scaffold(
appBar: AppBar(
backgroundColor: Colors.white,
toolbarHeight: 45,
leading: GestureDetector(
child: Icon(Icons.arrow_back),
onTap: () => {Navigator.pop(context)},
),
centerTitle: true,
elevation: 0,
title: Text(
'TweenStudy',
style: TextStyle(color: Colors.black87, fontSize: 18),
)),
body: Column(
children: [
GestureDetector(
onTap:(){
_animationController.reset();
_animationController.forward();
},
child: Container(
color: color,
width: animationValue,
height: animationValue,
alignment: Alignment.center,
),
)
],
),
));
}
@override
void dispose() {
//必须在super.dispose()调用,否则可能会有内存泄漏
_animationController.stop();
_animationController.dispose();
super.dispose();
}
}
复制代码
4. AnimatedWidget
1. 基本概念
AnimatedWidget 就是一个可以执行动画的Widget,继承自StatefulWidget,使用它可以简化动画的使用方式,在上面的例子中使用Tween的时候,我们给动画设置了addListener监听,然后在回调中手动调用setState(() {})刷新页面,如果使用了AnimatedWidget,那就不用再手动设置监听了
2. 使用
1. 动画效果
如下图所以,一个flutter logo从小放大
2. 代码
如下代码所示,先写一个类CustomAnimationWidget继承自 AnimatedWidget,然后重写build方法,返回一个承载动画的值,构造函数中接收一个Animation参数,然后重写build方法,返回一个承载动画的Container,Container的宽高使用Animation对象的value变量,value变量代表当前动画进度的具体值。然后在动画执行的过程中,Container的宽高就会发生变化,那么,使用CustomAnimationWidget呢?
首先还是需要在state中新建 AnimationController和Tween对象,将AnimationController和Tween对象绑定后启动动画,并且将Tween对象放入到
CustomAnimationWidget的构造函数中即可
/// 1. 先写一个类继承自 AnimatedWidget,然后重写build方法,返回一个承载动画的widget
class CustomAnimationWidget extends AnimatedWidget{
/// 放大因子,用于放大动画值
int factor = 1;
CustomAnimationWidget({Key key, Animation<double> animation, this.factor = 1}): super(key: key, listenable: animation);
@override
Widget build(BuildContext context) {
Animation<double> animation = listenable;
return Center(
child: Container(
width: animation.value * factor,
height: animation.value * factor,
child: FlutterLogo(),
),
);
}
}
class AnimationWidgetStudy extends StatefulWidget {
@override
State<StatefulWidget> createState() => AnimationWidgetState();
}
class AnimationWidgetState extends State with SingleTickerProviderStateMixin {
AnimationController animationController;
Animation tween;
@override
void initState() {
super.initState();
animationController = AnimationController(
duration: Duration(milliseconds: 2000), vsync: this);
tween = Tween<double>(begin: 0.0, end: 200.0).animate(animationController);
animationController.forward();
}
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(primaryColor: Colors.white),
home: Scaffold(
appBar: AppBar(
backgroundColor: Colors.white,
toolbarHeight: 45,
leading: GestureDetector(
child: Icon(Icons.arrow_back),
onTap: () => {Navigator.pop(context)},
),
centerTitle: true,
elevation: 0,
title: Text(
'AnimationWidgetStudy',
style: TextStyle(color: Colors.black87, fontSize: 18),
)),
body: CustomAnimationWidget(animation: tween),
));
}
@override
void dispose() {
//必须在super.dispose()调用,否则可能会有内存泄漏
animationController.stop();
animationController.dispose();
super.dispose();
}
}
复制代码
5. AnimatedBuilder
1. 基本概念
AnimatedBuilder是构建通用动画的一个组件,是拆分动画的一个工具类,可以将动画渲染和承载动画的widget相分离,在上面使用AnimationWidget构建动画的时候,其实有一个问题,就是每次刷新动画的时候都会重新构建显示图片logo的widget,而更好的做法是将动画渲染和widget相分离,而
AnimatedBuilder就可以实现这样的功能
2. 使用
1. 动画效果
2. 代码
如下代码所示,写一个AnimatedBuildWidget继承自StatelessWidget,然后在build中返回
AnimatedBuilder实例对象,AnimatedBuilder接收一个Animation类型的参数,这个参数就是要执行的动画,还接收一个child参数,这个就是承载动画的widget,还接收一个builder参数,返回一个widget,然后在State中新建AnimationController和Tween对象,这两个对象绑定后和承载动画的widget一起放入AnimatedBuildWidget的构造参数中即可
class AnimatedBuildWidget extends StatelessWidget {
Widget child;
Animation animation;
AnimatedBuildWidget(this.child, this.animation);
@override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: animation,
builder: (context, child) {
return Container(
width: animation.value,
height: animation.value,
child: child,
);
},
child: child,
);
}
}
class AnimatedBuildStudy extends StatefulWidget {
@override
State<StatefulWidget> createState() => AnimatedBuildState();
}
class AnimatedBuildState extends State with SingleTickerProviderStateMixin {
AnimationController animationController;
Animation tween;
@override
void initState() {
super.initState();
animationController = AnimationController(
duration: Duration(milliseconds: 2000), vsync: this);
tween = Tween<double>(begin: 0.0, end: 200.0).animate(animationController);
animationController.forward();
}
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(primaryColor: Colors.white),
home: Scaffold(
appBar: AppBar(
backgroundColor: Colors.white,
toolbarHeight: 45,
leading: GestureDetector(
child: Icon(Icons.arrow_back),
onTap: () => {Navigator.pop(context)},
),
centerTitle: true,
elevation: 0,
title: Text(
'AnimatedBuildStudy',
style: TextStyle(color: Colors.black87, fontSize: 18),
)),
body: Container(
child: Center(
child: AnimatedBuildWidget(FlutterLogo(), tween),
),
),
));
}
}
复制代码
6. CurvedAnimation
1. 基本概念
使用Tween启动的动画是线性动画,但是动画有时候是一个曲线过程,例如可以先加速后减速,这时候的动画就是一个曲线过程的动画,使用CurvedAnimation就可以开启曲线动画,CurvedAnimation的构造函数如下所示:parent代表AnimationController,curve代表动画正向播放的曲线,reverseCurve代表动画反向播放的曲线
CurvedAnimation({
required this.parent,
required this.curve,
this.reverseCurve,
}) : assert(parent != null),
assert(curve != null) {
_updateCurveDirection(parent.status);
parent.addStatusListener(_updateCurveDirection);
}
复制代码
Curve类型的对象的有一些常量Curves可以直接使用,常用的有如下一些:
- linear:匀速动画
- decelerate:匀减速动画
- ease:先加速后减速
- easeIn:先快后慢动画
- easeOut:先慢后快动画
- easeInOut:先慢,然后加速,最后减速
当然,也可以自定义Curve,如下所示,写一个类继承自Curve,重写transformInter方法,完成内部逻辑即可
class _BounceInOutCurve extends Curve {
const _BounceInOutCurve._();
@override
double transformInternal(double t) {
...
}
}
复制代码
2.使用
1. 动画效果
如下图所示,一个图片放大的过程
2. 代码
如下代码所示,创建一个AnimationController和CurvedAnimation对象,将CurvedAnimation对象的curve设置为Curves.bounceInOut,即可出现上面放大过程中的反弹的效果
class CurvedAnimationStudy extends StatefulWidget {
@override
State<StatefulWidget> createState() => CurvedAnimationState();
}
class CurvedAnimationState extends State with SingleTickerProviderStateMixin {
AnimationController animationController;
Animation tween;
@override
void initState() {
super.initState();
animationController = AnimationController(
duration: Duration(milliseconds: 2000), vsync: this);
tween =CurvedAnimation(parent: animationController, curve: Curves.bounceInOut);
animationController.forward();
}
@override
Widget build(BuildContext context) {
return new MaterialApp(
theme: ThemeData(primaryColor: Colors.white),
home: Scaffold(
appBar: AppBar(
backgroundColor: Colors.white,
toolbarHeight: 45,
leading: GestureDetector(
child: Icon(Icons.arrow_back),
onTap: () => {Navigator.pop(context)},
),
centerTitle: true,
elevation: 0,
title: Text(
'CurvedAnimationStudy',
style: TextStyle(color: Colors.black87, fontSize: 18),
)),
body: Container(
child: CustomAnimationWidget(
animation: tween,
factor: 200,
),
),
));
}
}
复制代码
7. Hero
1. 基本概念
不同页面的转场动画,在页面切换的时候出现的动画效果,例如在一个列表上面点击图片,跳到详情页,然后出现的图片飞行放大的效果,Hero的构造函数如下:
- createRectTween:CreateRectTween对象,代表了widget从起始位置“飞到”目标位置的过程中如何变化
- tag:用于关联发生hero动画的两个widget的标识
- child:定义承载动画的widget
const Hero({
Key key,
@required this.tag,
this.createRectTween,
this.flightShuttleBuilder,
this.placeholderBuilder,
this.transitionOnUserGestures = false,
@required this.child,
}) : assert(tag != null),
assert(transitionOnUserGestures != null),
assert(child != null),
super(key: key);
复制代码
2. 使用
1. 动画效果
2. 代码
class HeroWidget extends StatelessWidget{
String photo;
VoidCallback voidCallback;
double width;
HeroWidget(this.photo, this.voidCallback, this.width);
@override
Widget build(BuildContext context) {
return SizedBox(
width: width,
height: width,
child: Hero(
tag: photo,
child: Material(
color: Colors.transparent,
child: InkWell(
onTap: voidCallback,
child: Image.network(photo, fit: BoxFit.contain,),
),
),
),
);
}
}
class HeroStudy extends StatelessWidget {
@override
Widget build(BuildContext context) {
timeDilation = 2.6;
return MaterialApp(
theme: ThemeData(primaryColor: Colors.white),
home: Scaffold(
appBar: AppBar(
backgroundColor: Colors.white,
toolbarHeight: 45,
leading: GestureDetector(
child: Icon(Icons.arrow_back),
onTap: () => {Navigator.pop(context)},
),
centerTitle: true,
elevation: 0,
title: Text(
'AnimationWidgetStudy',
style: TextStyle(color: Colors.black87, fontSize: 18),
)),
body: Container(
child: HeroWidget(
"http://pic21.photophoto.cn/20111011/0006019003288114_b.jpg",
() {
Navigator.of(context).push(MaterialPageRoute(builder: (context) {
return MaterialApp(
theme: ThemeData(primaryColor: Colors.white),
home: Scaffold(
appBar: AppBar(
backgroundColor: Colors.white,
toolbarHeight: 45,
leading: GestureDetector(
child: Icon(Icons.arrow_back),
onTap: () => {Navigator.pop(context)},
),
centerTitle: true,
elevation: 0,
title: Text(
'AnimationWidgetStudy',
style:
TextStyle(color: Colors.black87, fontSize: 18),
)),
body: Center(
child: HeroWidget("http://pic21.photophoto.cn/20111011/0006019003288114_b.jpg",
() {
Navigator.of(context).pop();
}, 500),
),
));
}));
}, 100),
),
));
}
}
复制代码
8. QQ交流群
770892444