flutter 动画前言
今天对Flutter动画进行一次突击,这篇文章是我对Flutter动画的使用和理解,有可能有不对的地方,欢迎大家给我指正
重要的类
- Animation< double >
在Flutter中,Animation对象本身和UI渲染没有任何关系。Animation是一个抽象类,它拥有其当前值和状态(完成或停止)。其中一个比较常用的Animation类是Animation。Flutter中的Animation对象是一个在一段时间内依次生成一个区间之间值的类。Animation对象的输出可以是线性的、曲线的、一个步进函数或者任何其他可以设计的映射。 根据Animation对象的控制方式,动画可以反向运行,甚至可以在中间切换方向。
Animation还可以生成除double之外的其他类型值,如:Animation< Color > 或 Animation< Size >。
Animation对象有状态。可以通过访问其value属性获取动画的当前值。
Animation对象本身和UI渲染没有任何关系。 - AnimationController
AnimationController是一个特殊的Animation对象,在屏幕刷新的每一帧,就会生成一个新的值。默认情况下,AnimationController在给定的时间段内会线性的生成从0.0到1.0的数字。
final AnimationController controller = new AnimationController(
duration: const Duration(milliseconds: 2000), vsync: this);
AnimationController派生自Animation,因此可以在需要Animation对象的任何地方使用。 但是,AnimationController具有控制动画的其他方法。例如,.forward()方法可以启动动画。数字的产生与屏幕刷新有关,因此每秒钟通常会产生60个数字,在生成每个数字后,每个Animation对象调用添加的Listener对象。
当创建一个AnimationController时,需要传递一个vsync
参数,存在vsync
时会防止屏幕外动画(译者语:动画的UI不在当前屏幕时)消耗不必要的资源。 通过将SingleTickerProviderStateMixin
添加到类定义中,可以将stateful对象作为vsync
的值。
- CurvedAnimation
CurvedAnimation 将动画过程定义为一个非线性曲线.
final CurvedAnimation curve =
new CurvedAnimation(parent: controller, curve: Curves.easeIn);
- Tween
默认情况下,AnimationController对象的范围从0.0到1.0。如果您需要不同的范围或不同的数据类型,则可以使用Tween来配置动画以生成不同的范围或数据类型的值。例如,以下示例,Tween生成从-200.0到0.0的值:
final Tween doubleTween = new Tween<double>(begin: -200.0, end: 0.0);
Tween对象不存储任何状态。相反,它提供了evaluate(Animation<double> animation)
方法将映射函数应用于动画当前值。 Animation对象的当前值可以通过value()方法取到。evaluate函数还执行一些其它处理,例如分别确保在动画值为0.0和1.0时返回开始和结束状态。
Tween.animate
要使用Tween对象,请调用其animate()方法,传入一个控制器对象。例如,以下代码在500毫秒内生成从0到255的整数值。
final AnimationController controller = new AnimationController(
duration: const Duration(milliseconds: 500), vsync: this);
Animation<int> alpha = new IntTween(begin: 0, end: 255).animate(controller);
注意animate()
返回的是一个Animation,而不是一个Animatable。
以下示例构建了一个控制器、一条曲线和一个Tween:
final AnimationController controller = AnimationController(
duration: const Duration(milliseconds: 2000), vsync: this);
final Animation animation = CurvedAnimation(parent: controller,curve: Curves.easeInOutQuart)
Animation<int> alpha = new IntTween(begin: 0, end: 255).animate(animation);
- 动画通知
一个Animation对象可以拥有Listeners和StatusListeners监听器,可以用addListener()
和addStatusListener()
来添加。 只要动画的值发生变化,就会调用监听器。一个Listener最常见的行为是调用setState()来触发UI重建。动画开始、结束、向前移动或向后移动(如AnimationStatus所定义)时会调用StatusListener。 下一节中有一个addListener()
方法的例子。监视动画的进度展示了如何调用addStatusListener()
。
如何构建一个动画
- 通常创建一个动画需要注意的是这个里面的
addListener()
我们必须手动执行setState()
来刷新UI界面
import 'package:flutter/material.dart';
class CurvedAnimationPage extends StatefulWidget {
final String title;
CurvedAnimationPage({Key key, this.title}) : super(key: key);
@override
_CurvedAnimationPageState createState() => _CurvedAnimationPageState();
}
class _CurvedAnimationPageState extends State<CurvedAnimationPage>
with SingleTickerProviderStateMixin {
Animation<double> animation;
AnimationController controller;
@override
void initState() {
super.initState();
controller = AnimationController(
duration: const Duration(milliseconds: 2000), vsync: this);
animation = Tween(begin: 0.0, end: 300.0).animate(controller)
..addListener(() {
setState(() {
// animation.value;
});
})
..addStatusListener((status) {
if (status == AnimationStatus.completed) {
controller.reverse();
} else if (status == AnimationStatus.dismissed) {
controller.forward();
}
});
controller.forward();
}
@override
void dispose() {
controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('曲线动画'),
),
body: Container(
width: animation.value,
height: animation.value,
child: FlutterLogo(),
));
}
}
- 通过
AnimatedWidget
来创建动画这里不需要手动修改setState()
import 'package:flutter/material.dart';
class CurvedAnimationPage extends StatefulWidget {
final String title;
CurvedAnimationPage({Key key, this.title}) : super(key: key);
@override
_CurvedAnimationPageState createState() => _CurvedAnimationPageState();
}
class _CurvedAnimationPageState extends State<CurvedAnimationPage>
with SingleTickerProviderStateMixin {
Animation<double> animation;
AnimationController controller;
@override
void initState() {
super.initState();
controller = AnimationController(
duration: const Duration(milliseconds: 2000), vsync: this);
animation = CurvedAnimation(parent: controller,curve: Curves.easeInOutQuart)
..addStatusListener((status) {
if (status == AnimationStatus.completed) {
controller.reverse();
} else if (status == AnimationStatus.dismissed) {
controller.forward();
}
});
controller.forward();
}
@override
void dispose() {
controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return AnimatedLogo(animation: animation,);
}
}
// AnimatedWidget 的使用
class AnimatedLogo extends AnimatedWidget {
static final _opacityTween = Tween<double>(begin: 0.1, end: 1);
static final _sizeTween = Tween<double>(begin: 0.0, end: 300);
AnimatedLogo({Key key, Animation<double> animation}):super(key:key,listenable:animation);
@override
Widget build(BuildContext context) {
final Animation<double> animation = listenable;
return Scaffold(
appBar: AppBar(
title: Text(
"曲线动画",
style: TextStyle(fontSize: 25, fontWeight: FontWeight.bold),
),
// centerTitle: false,
),
body: Center(
child: Opacity(
opacity: _opacityTween.evaluate(animation),
child: Container(
width: _sizeTween.evaluate(animation),
height: _sizeTween.evaluate(animation),
child: FlutterLogo(),
),
),
));
}
}
- 使用
AnimatedBuilder
创建动画
import 'package:flutter/material.dart';
class CurvedAnimationPage extends StatefulWidget {
final String title;
CurvedAnimationPage({Key key, this.title}) : super(key: key);
@override
_CurvedAnimationPageState createState() => _CurvedAnimationPageState();
}
class _CurvedAnimationPageState extends State<CurvedAnimationPage>
with SingleTickerProviderStateMixin {
Animation<double> animation;
AnimationController controller;
@override
void initState() {
super.initState();
controller = AnimationController(
duration: const Duration(milliseconds: 2000), vsync: this);
animation = CurvedAnimation(parent: controller,curve: Curves.easeInOutQuart)
..addStatusListener((status) {
if (status == AnimationStatus.completed) {
controller.reverse();
} else if (status == AnimationStatus.dismissed) {
controller.forward();
}
});
controller.forward();
}
@override
void dispose() {
controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('曲线动画d'),
),
body: GrowTransition(child: LogoWidget(),animation: animation)
);
}
}
// AnimatedBuilder 的使用
class GrowTransition extends StatelessWidget {
final Widget child;
final Animation<double> animation;
static final _opacityTween = Tween<double>(begin: 0.1, end: 1);
static final _sizeTween = Tween<double>(begin: 0.0, end: 300);
const GrowTransition({Key key,this.child,this.animation}) : super(key: key);
@override
Widget build(BuildContext context) {
return Center(
child: AnimatedBuilder(
animation: animation,
child: child,
builder: (BuildContext context, Widget child) {
return Opacity(
opacity: _opacityTween.evaluate(animation),
child: Container(
width: _sizeTween.evaluate(animation),
height: _sizeTween.evaluate(animation),
child: child,
),
);
},
),
);
}
}
class LogoWidget extends StatelessWidget {
const LogoWidget({Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
child:Container(
child: FlutterLogo(),
)
);
}
}
- 旋转+缩放
return RotationTransition(
turns:Tween(begin:0.0,end:1.0)
.animate(CurvedAnimation(
parent: animation1,
curve: Curves.fastOutSlowIn
)),
child:ScaleTransition(
scale:Tween(begin: 0.0,end:1.0)
.animate(CurvedAnimation(
parent: animation1,
curve:Curves.fastOutSlowIn
)),
child: child,
)
);
- 渐隐渐现
return FadeTransition(
opacity: Tween(begin: 0.0, end: 1.0).animate(CurvedAnimation(
parent: animation1,
curve: Curves.fastOutSlowIn)
),
child: child,
);
- 左右滑动动画
return SlideTransition(
position: Tween<Offset>(
begin: Offset(1.0, 0.0),
end: Offset(0.0, 0.0)
).animate(CurvedAnimation(
parent: animation1,
curve: Curves.fastOutSlowIn
)),
child: child,
);
本人已开通微信公众号,欢迎关注