动画的本质
动画,即,动起来的画。
Flutter,达到60FPS,即16ms一帧,每一帧即一个画面。
如一个Image,初始时width=300,height=300
第1帧:width=300,height=300
第2帧:width=295,height=295
第3帧:width=290,height=290
。。。。。。
。。。。。。
第58帧:width=10,height=10
第59帧:width=5,height=5
第60帧:width=0,height=0
在一秒内,从第1帧刷到60帧,你看到的就是Image的缩小效果。这就是动画。
Image的宽高属性在1秒内连续发生了变化,是缩放;
Image的位置属性,在1秒内连续发生变化,是移动效果;
Text的字体大小属性,在一段时间内发生连续变化,是字体大小的缩放效果;
。。。。。。
也就是说,widget的某个或某几个属性,在一段时间内发生连续变化,就是该widget对应属性的动画效果。
关键点:
- 作用在widget的属性上
- 一段时间
- 连续变化的属性值
上面的都是思路,那flutter中具体如何实现的呢?
flutter动画的几个类:
- AnimationController,设定在一个时间段内,会持续的产生0-1之间的值,可以控制动画的执行,如正向执行、反向执行、重复等,也可以添加状态监听,如每一帧的回调、整个动画状态的回调等。
- Tween,专门设定取值范围,AnimationController默认是0-1,通过Tween可以将这个取值范围改变,如0-100,红色-绿色等
- Curve,AnimationController会持续的产生一个区间的值,Curve控制的是在这个区间内,值变化的规律,比如上面一开始的例子,是线性变化,每一次固定减小5;我们也可以改变这个算法,如依次减少60、55、50.。。。,这是一个变化放缓的效果。Flutter内置了一些效果,后面我们一一看。
你是否注意到,上面的内容,说的都是在一段时间段内如何天花乱坠的产生天花乱坠的值,跟widget一点关系都没有。
怎么建立关联?
在build中:
build(){
return Widget( xxx属性:_AnimationController.value);
}
复制代码
每次AnimationController产生的值发生变化后,都会最终调用到build方法,让界面发生变化,最终形成连续的效果,也就是动画。
上面的示例代码:
import 'package:flutter/material.dart';
class Test01AnimationController extends StatefulWidget {
@override
_Test01AnimationControllerState createState() =>
_Test01AnimationControllerState();
}
class _Test01AnimationControllerState extends State<Test01AnimationController>
with SingleTickerProviderStateMixin {
int _count = 0;
double _width = 0; // 图片宽
double _height = 0; // 图片高
AnimationController _controller;
@override
void initState() {
super.initState();
_count = 0; // addListener回调的次数
// 创建一个动画,设定持续时间duration为2000ms
_controller = new AnimationController(
vsync: this, duration: const Duration(milliseconds: 2000));
// 每次值发生变化就回调该方法
_controller.addListener(() {
// 每次值发生变化后,就让图片宽高也发生变化
setState(() {
_count++;
_width = 300 * _controller.value;
_height = 300 * _controller.value;
});
});
// 动画开始执行
_controller.forward();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("AnimationController"),
centerTitle: true,
),
body: Center(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset(
"images/a.JPG",
width: _width,
height: _height,
),
Text("${_count} ${_controller.value}")
],
),
),
);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
}
复制代码
简短总结:
- 动画AnimationnController,持续产生一系列的值
- 每产生一次值,就setState一次,让该值作用到widget的属性上
- 每次setState后,build方法重新执行
- 最终看到一个变化的widget,即动画效果
重要的类
AnimationController
构造方法
AnimationController
AnimationController({
double? value,
this.duration,
this.reverseDuration,
this.debugLabel,
this.lowerBound = 0.0,
this.upperBound = 1.0,
this.animationBehavior = AnimationBehavior.normal,
required TickerProvider vsync,
}) : assert(lowerBound != null),
assert(upperBound != null),
assert(upperBound >= lowerBound),
assert(vsync != null),
_direction = _AnimationDirection.forward {
_ticker = vsync.createTicker(_tick);
_internalSetValue(value ?? lowerBound);
}
复制代码
此构造方法特点:
- 有上下值边界
- 在动画执行过程中持续产生上下边界范围内的值,通过controller.value考验获取到该值。
AnimationController.unbounded
AnimationController.unbounded({
double value = 0.0,
this.duration,
this.reverseDuration,
this.debugLabel,
required TickerProvider vsync,
this.animationBehavior = AnimationBehavior.preserve,
}) : assert(value != null),
assert(vsync != null),
lowerBound = double.negativeInfinity,
upperBound = double.infinity,
_direction = _AnimationDirection.forward {
_ticker = vsync.createTicker(_tick);
_internalSetValue(value);
}
复制代码
此构造方法特点:
- lowerBound与upperBound是无限的
- 通过controller.value 获取到的值是Infinity.
- 因此该构造方法的意义在于定义了一个持续的时间段,但是该时间段内的动画需要自己定义。通常用于物理模拟。
属性
value
有两个作用:
- 在动画执行过程中,通过controller.value获取当前时间点动画产生的值。
- 在创建AnimationController时设置初始值,比如value=0.5,AnimationController默认取值范围是0-1,那么在动画执行时,是从0.5开始取值,直到1。如果你设定了duration为2秒,那么实际执行时间约为1秒。如果你设定value=1,那么动画不执行。也就是说,AnimationController会按照duration去产生持续变化的值,设置value后,会取value-1的这一段。
```
_controller = new AnimationController(
vsync: this,
duration: const Duration(milliseconds: 2000),
value: 0.9
);
```
复制代码
还有一个关键点,value取值在lowerBound与upperBound之间时才有效果,如果value小于lowerBound,取值从lowerBound开始;如果value大于upperBound,那么动画相当于直接结束,不再执行。value默认值是lowerBound。
duration
动画持续时间
#####reverseDuration
当动画执行revrse时的持续时间,如果不设置,那么时间按duration计算。
lowerBound
默认0.0,设置时,lowerBound要小upperBounnd,否则报错
upperBound
默认1.0
设定了lowerBound与upperBound的例子:
import 'package:flutter/material.dart';
class Test02AnimationController extends StatefulWidget {
@override
_Test02AnimationControllerState createState() =>
_Test02AnimationControllerState();
}
class _Test02AnimationControllerState extends State<Test02AnimationController>
with SingleTickerProviderStateMixin {
int _count = 0;
double _width = 0; // 图片宽
double _height = 0; // 图片高
AnimationController _controller;
@override
void initState() {
super.initState();
_count = 0; // addListener回调的次数
// 创建一个动画,设定持续时间duration为2000ms
_controller = new AnimationController(
vsync: this,
duration: const Duration(milliseconds: 2000),
value: 0,
lowerBound: 0,
upperBound: 20,
animationBehavior: AnimationBehavior.normal
);
// 每次值发生变化就回调该方法
_controller.addListener(() {
// 每次值发生变化后,就让图片宽高也发生变化
setState(() {
_count++;
_width = 30 * _controller.value;
_height = 30 * _controller.value;
});
});
// 动画开始执行
_controller.forward();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("AnimationController"),
centerTitle: true,
),
body: Center(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset(
"images/a.JPG",
width: _width,
height: _height,
),
Text("${_count} ${_controller.value}")
],
),
),
);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
}
复制代码
animationBehavior
注意:此处限个人理解,做个记录,后续研究具体情形后再来更正。目前可略过此属性不看
flutter支持很多平台,但是有些动画在某些平台就是不支持或要求简化,而AccessibilityFeatures.disableAnimations字段就是返回的平台支持情况,当为true时,设备会要求flutter尽快减少或禁用动画。有两种方式控制:
- AnimationBehavior.normal,当使用new AnimationController()时默认此值,通过减少动画持续时间来让简化动画,让其尽早结束。
- AnimationBehavior.preserve,当使用new AnimationController.unbounded()时默认此值,AnimationController会保留其行为,我理解的是惯性。对于重复性动画,当不考虑AccessibilityFeatures.disableAnimations时,默认就是这种行为。
vsync
注册屏幕的每一帧回调监听,防止屏幕外动画,如锁屏时不回调,避免消耗不必要的资源。创建AnimationController时比须传入此值,而AnimationController通常是在State中创建,让State with 一个SingleTickerProviderStateMixin,传vsync时,传入this。
Curve
先看个效果:
Curve描述动画过程中速度变化的过程,AnimationController默认效果是匀速的,Curve可以改变这种效果,如实现加速、减速、反弹等。实际控制的是AnimationController在duration内产生值的规则。
如何使用:
AnimationController _controller;
CurvedAnimation _curvedAnimation;
@override
void initState() {
super.initState();
_controller = new AnimationController(
vsync: this, duration: const Duration(milliseconds: 3000));
_curvedAnimation =
new CurvedAnimation(parent: _controller, curve: Curves.ease);
// 动画开始执行
_controller.repeat();
}
复制代码
- 依然创建AnimationController
- 创建CurvedAnimation时,将AnimationController 和 curve传入,通过Curses.xxx,可以获取flutter内置的效果,当前时间点有41种。
- 依然是使用AnimationController进行动画的控制,如repeat、forward、reverse等。
- 以后取值改为从CurvedAnimation获取,如_curvedAnimation.value。
上面效果的代码:
import 'package:flutter/material.dart';
class Test05CurveImage extends StatefulWidget {
@override
_Test05CurveImageState createState() =>
_Test05CurveImageState();
}
class _Test05CurveImageState extends State<Test05CurveImage>
with SingleTickerProviderStateMixin {
double _width = 0; // 图片宽
double _height = 0; // 图片高
AnimationController _controller;
CurvedAnimation _curvedAnimation;
@override
void initState() {
super.initState();
_controller = new AnimationController(
vsync: this, duration: const Duration(milliseconds: 5000));
_curvedAnimation = new CurvedAnimation(parent: _controller, curve: Curves.bounceInOut);
_controller.addListener(() {
setState(() {
_width = 300 * _curvedAnimation.value;
_height = 300 * _curvedAnimation.value;
});
});
// 动画开始执行
_controller.forward();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("AnimationController"),
centerTitle: true,
),
body: Center(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset(
"images/a.JPG",
width: _width,
height: _height,
),
Text("_curvedAnimation.value=${_curvedAnimation.value}")
],
),
),
);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
}
复制代码
Curve来源:
- flutter内置提供
- 自定义
Flutter提供的Curve效果
当前是41种,见效果:
代码如下:
import 'package:flutter/material.dart';
import 'dart:math';
class Test04Curve extends StatefulWidget {
@override
_Test04CurveState createState() => _Test04CurveState();
}
class _Test04CurveState extends State<Test04Curve>
with SingleTickerProviderStateMixin {
List<ItemModel> curveList;
double size = 120;
@override
void initState() {
super.initState();
curveList = [
ItemModel('linear', Curves.linear),
ItemModel('bounceIn', Curves.bounceIn),
ItemModel('bounceOut', Curves.bounceOut),
ItemModel('bounceInOut', Curves.bounceInOut),
ItemModel('ease', Curves.ease),
ItemModel('easeIn', Curves.easeIn),
ItemModel('easeInBack', Curves.easeInBack),
ItemModel('easeInCirc', Curves.easeInCirc),
ItemModel('easeInCubic', Curves.easeInCubic),
ItemModel('easeInExpo', Curves.easeInExpo),
ItemModel('easeInQuad', Curves.easeInQuad),
ItemModel('easeInQuart', Curves.easeInQuart),
ItemModel('easeInQuint', Curves.easeInQuint),
ItemModel('easeInSine', Curves.easeInSine),
ItemModel('easeInToLinear', Curves.easeInToLinear),
ItemModel('easeOut', Curves.easeOut),
ItemModel('easeOutBack', Curves.easeOutBack),
ItemModel('easeOutCirc', Curves.easeOutCirc),
ItemModel('easeOutCubic', Curves.easeOutCubic),
ItemModel('easeOutExpo', Curves.easeOutExpo),
ItemModel('easeOutQuad', Curves.easeOutQuad),
ItemModel('easeOutQuart', Curves.easeOutQuart),
ItemModel('easeOutQuint', Curves.easeOutQuint),
ItemModel('easeOutSine', Curves.easeOutSine),
ItemModel('easeInOut', Curves.easeInOut),
ItemModel('easeInOutBack', Curves.easeInOutBack),
ItemModel('easeInOutCirc', Curves.easeInOutCirc),
ItemModel('easeInOutCubic', Curves.easeInOutCubic),
ItemModel('easeInOutExpo', Curves.easeInOutExpo),
ItemModel('easeInOutQuad', Curves.easeInOutQuad),
ItemModel('easeInOutQuart', Curves.easeInOutQuart),
ItemModel('easeInOutQuint', Curves.easeInOutQuint),
ItemModel('easeInOutSine', Curves.easeInOutSine),
ItemModel('decelerate', Curves.decelerate),
ItemModel('elasticIn', Curves.elasticIn),
ItemModel('elasticOut', Curves.elasticOut),
ItemModel('elasticInOut', Curves.elasticInOut),
ItemModel('slowMiddle', Curves.slowMiddle),
ItemModel('fastLinearToSlowEaseIn', Curves.fastLinearToSlowEaseIn),
ItemModel('fastOutSlowIn', Curves.fastOutSlowIn),
ItemModel('linearToEaseOut', Curves.linearToEaseOut),
];
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Curve"),
centerTitle: true,
),
body: Container(
padding: EdgeInsets.all(10),
child: GridView.builder(
gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
mainAxisExtent: size + 20,
mainAxisSpacing: 10,
crossAxisSpacing: 10,
maxCrossAxisExtent: size + 10),
itemBuilder: (context, index) {
return Container(
padding: EdgeInsets.symmetric(horizontal: 5),
child: Column(
children: [
MyCurveWidget(curveList[index].curve, size),
Text(curveList[index].name, style: TextStyle(fontSize: 10)),
],
),
);
},
itemCount: curveList.length,
),
),
);
}
}
class MyCurveWidget extends StatefulWidget {
Curve _curve;
double _size;
MyCurveWidget(this._curve, this._size);
@override
_MyCurveWidgetState createState() => _MyCurveWidgetState();
}
class _MyCurveWidgetState extends State<MyCurveWidget>
with SingleTickerProviderStateMixin {
AnimationController _controller;
CurvedAnimation _curvedAnimation;
@override
void initState() {
super.initState();
// 创建一个动画,设定持续时间duration为2000ms
_controller = new AnimationController(
vsync: this, duration: const Duration(milliseconds: 6000));
_curvedAnimation =
new CurvedAnimation(parent: _controller, curve: widget._curve);
// 动画开始执行
_controller.repeat();
}
@override
Widget build(BuildContext context) {
return Container(
height: widget._size,
width: widget._size,
child: Center(
child: CustomPaint(
size: Size(widget._size - 10, widget._size - 10),
painter: MyCurvePainter(_curvedAnimation),
),
),
);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
}
class MyCurvePainter extends CustomPainter {
Animation<double> _animation;
MyCurvePainter(this._animation) : super(repaint: _animation);
@override
void paint(Canvas canvas, Size size) {
translateToCenter(canvas, size);
drawBg(canvas, size);
drawBoll(canvas, size);
}
void drawBg(Canvas canvas, Size size) {
Paint paint = Paint()
..color = Colors.black12
..strokeWidth = 1
..style = PaintingStyle.stroke;
Offset p1 = Offset(-size.width / 2, 0);
Offset p2 = Offset(size.width / 2, 0);
canvas.drawLine(p1, p2, paint);
canvas.drawCircle(Offset.zero, size.width / 2, paint);
}
void drawBoll(Canvas canvas, Size size) {
double radius = size.width / 2;
Paint paint = Paint()
..color = Colors.orangeAccent
..style = PaintingStyle.fill;
canvas.drawCircle(
Offset(-radius + size.width * _animation.value, 0), 5, paint);
double sweepAngle = pi + pi * 2 * _animation.value;
canvas.drawCircle(
Offset(radius * cos(sweepAngle), radius * sin(sweepAngle)), 5, paint);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) {
return true;
}
void translateToCenter(Canvas canvas, Size size) {
canvas.translate(size.width / 2, size.height / 2);
}
}
class ItemModel {
String name;
Curve curve;
ItemModel(this.name, this.curve);
}
复制代码
自定义Curve
继承Curve,然后重写transformInternal方法,自己定义变化的规律。
如:
class MyCurve extends Curve {
@override
double transformInternal(double t) {
return sin(pi / 2 * t);
}
}
复制代码
- t的范围在0-1之间
- sin(pi / 2 * t) 应用t做了一个正弦函数曲线
效果如下;
代码如下:
import 'package:flutter/material.dart';
import 'dart:math';
class Test06CustomCurve extends StatefulWidget {
@override
_Test06CustomCurveState createState() => _Test06CustomCurveState();
}
class _Test06CustomCurveState extends State<Test06CustomCurve>
with SingleTickerProviderStateMixin {
double _width = 0; // 图片宽
double _height = 0; // 图片高
AnimationController _controller;
CurvedAnimation _curvedAnimation;
@override
void initState() {
super.initState();
_controller = new AnimationController(
vsync: this, duration: const Duration(milliseconds: 5000));
_curvedAnimation =
new CurvedAnimation(parent: _controller, curve: MyCurve());
_controller.addListener(() {
setState(() {
_width = 300 * _curvedAnimation.value;
_height = 300 * _curvedAnimation.value;
});
});
// 动画开始执行
_controller.forward();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("AnimationController"),
centerTitle: true,
),
body: Center(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset(
"images/a.JPG",
width: _width,
height: _height,
),
Text("_curvedAnimation.value=${_curvedAnimation.value}")
],
),
),
);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
}
class MyCurve extends Curve {
@override
double transformInternal(double t) {
return sin(pi / 2 * t);
}
}
复制代码
Tween
AnimationController可以设定取值范围lowerBound-upperBound,类型是double的。Tween扩展了取值范围和类型,可以是颜色、int、double等。
有两个重要的方法:
- evaluate
- animate
使用evaluate
代码如下;
import 'package:flutter/material.dart';
class Test07Tween extends StatefulWidget {
@override
_Test07TweenState createState() => _Test07TweenState();
}
class _Test07TweenState extends State<Test07Tween>
with SingleTickerProviderStateMixin {
double _width = 0; // 图片宽
double _height = 0; // 图片高
AnimationController _controller;
Tween sizeTween = new Tween(begin: 0.0, end: 300.0);
@override
void initState() {
super.initState();
_controller = new AnimationController(
vsync: this, duration: const Duration(milliseconds: 5000),upperBound: 1);
_controller.addListener(() {
setState(() {
_width = sizeTween.evaluate(_controller);
_height = sizeTween.evaluate(_controller);
});
});
// 动画开始执行
_controller.forward();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("AnimationController"),
centerTitle: true,
),
body: Center(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset(
"images/a.JPG",
width: _width,
height: _height,
),
Row(
children: [
Text("sizeTween.evaluate(_controller)="),
Text("${sizeTween.evaluate(_controller)}")
],
)
],
),
),
);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
}
复制代码
关键点:
- new Tween(begin: 0.0, end: 300.0),创建Tween对象,设定取值范围
- 正常创建controller
- sizeTween.evaluate(_controller),evaluate方法获取当前时间点的插值
其实到这里有个疑问,lowerBound、upperBound与Tween同时存在会怎么样?上面的例子upperBound=1,现在我们改成2看下效果:
相比upperBound=1,图片放大了一倍。因此推断是相乘的关系。
使用animate
使用Tween提供的animate方法,可以获取一个新的Animation ,通过此也可以获取value值,如下代码:
import 'package:flutter/material.dart';
class Test08TweenAnimate extends StatefulWidget {
@override
_Test08TweenAnimateState createState() => _Test08TweenAnimateState();
}
class _Test08TweenAnimateState extends State<Test08TweenAnimate>
with SingleTickerProviderStateMixin {
double _width = 0; // 图片宽
double _height = 0; // 图片高
AnimationController _controller;
Animation<dynamic> _animation;
Tween sizeTween = new Tween(begin: 0.0, end: 300.0);
@override
void initState() {
super.initState();
_controller = new AnimationController(
vsync: this, duration: const Duration(milliseconds: 5000));
_animation = sizeTween.animate(_controller);
_controller.addListener(() {
setState(() {
_width = _animation.value;
_height = _animation.value;
});
});
// 动画开始执行
_controller.forward();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("AnimationController"),
centerTitle: true,
),
body: Center(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Image.asset(
"images/a.JPG",
width: _width,
height: _height,
),
Row(
children: [
Text("_animation.value="),
Text("${_animation.value}")
],
)
],
),
),
);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
}
复制代码
关键点:
- 创建Tween:Tween sizeTween = new Tween(begin: 0.0, end: 300.0)
- 与controller关联: _animation = sizeTween.animate(_controller);
- 使用:_animation.value获取插值
- 注意依然是使用_controller来控制动画的开始、重复等
Interval 组合动画
Interval是Curve的子类,定义了一个区间范围[0,1]内取值,可以是[0.1,0.3],也可以是[0.4,0.8]。我们都知道,AnimationController整体描述的是一个动画过程,看做是[0,1],Interval则在[0,1]内选择了一块区域,如[0.3,0.5],当Tween在animate时指定该Interval后,表示该Tween在0.3时开始,0.5时结束。
举个例子,一个正方形,在动画的0-0.6完成大小变化,0.3-1.0完成颜色变化。
效果:
代码:
import 'package:flutter/material.dart';
class Test29Interval extends StatefulWidget {
@override
_State createState() => _State();
}
class _State extends State<Test29Interval>
with SingleTickerProviderStateMixin {
AnimationController _controller;
Tween _sizeTween = new Tween<double>(begin: 0.0, end: 200.0);
Tween _colorTween = new ColorTween(begin: Colors.orangeAccent, end: Colors.green);
Animation<double> _animationSize;
Animation<Color> _animationColor;
@override
void initState() {
super.initState();
_controller = new AnimationController(
vsync: this,
duration: const Duration(milliseconds: 6000),
);
_animationSize = _sizeTween.animate(new CurvedAnimation(
parent: _controller, curve: new Interval(0.0, 0.6)));
_animationColor = _colorTween.animate(new CurvedAnimation(
parent: _controller, curve: new Interval(0.3, 1.0)));
_controller.addListener(() {
setState(() {});
});
// 动画开始执行
_controller.forward();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Test29Interval"),
centerTitle: true,
),
body: Center(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
width: _animationSize.value,
height: _animationSize.value,
margin: EdgeInsets.all(10),
color: _animationColor.value,
),
Container(
width: 400,
child: Text("_animationSize.value = ${_animationSize.value}",style: TextStyle(fontSize: 14),),
)
],
),
),
);
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
}
复制代码
AnimatedWidget
大家应该注意到了,上面所有的动画变化都是通过setState来维护的,对于一个通用功能,如果每次都使用setState,显然比较繁琐。因此,Flutter提供了AnimatedWidget,在使用时就不用显示的调用setState了。
看个例子,效果:
代码:
import 'package:flutter/material.dart';
class Test30AnimatiedWidget extends StatefulWidget {
@override
_State createState() => _State();
}
class _State extends State<Test30AnimatiedWidget>
with SingleTickerProviderStateMixin {
AnimationController controller;
Animation<double> animation;
@override
void initState() {
// TODO: implement initState
super.initState();
controller = new AnimationController(
duration: const Duration(milliseconds: 6000), vsync: this);
animation = new Tween(begin: 0.0, end: 300.0).animate(controller);
controller.addStatusListener((status) {
if (status == AnimationStatus.completed) {
controller.reverse();
} else if (status == AnimationStatus.dismissed) {
controller.forward();
}
});
controller.forward();
}
@override
Widget build(BuildContext context) {
return ImageAnimatiedWidget(
animation: animation,
);
}
@override
void dispose() {
// TODO: implement dispose
super.dispose();
controller.dispose();
}
}
class ImageAnimatiedWidget extends AnimatedWidget {
@override
Widget build(BuildContext context) {
Animation animation = listenable;
return Center(
child: Container(
child: Image.asset(
"images/a2.png",
width: animation.value,
height: animation.value,
),
// width: animation.value,
// height: animation.value,
),
);
}
ImageAnimatiedWidget({Key key, Animation<double> animation})
: super(key: key, listenable: animation);
}
复制代码
AnimatedBuilder
同AnimatedWidget类似,隐了setState,提供了另一种widget组织方式。
同上面的例子,效果:
代码:
import 'package:flutter/material.dart';
class Test31AnimateBuilder extends StatefulWidget {
@override
_State createState() => _State();
}
class _State extends State<Test31AnimateBuilder>
with SingleTickerProviderStateMixin {
AnimationController controller;
Animation<double> animation;
@override
void initState() {
// TODO: implement initState
super.initState();
controller = new AnimationController(
duration: const Duration(milliseconds: 6000), vsync: this);
animation = new Tween(begin: 0.0, end: 300.0).animate(controller);
controller.addStatusListener((status) {
if (status == AnimationStatus.completed) {
controller.reverse();
} else if (status == AnimationStatus.dismissed) {
controller.forward();
}
});
controller.forward();
}
@override
Widget build(BuildContext context) {
return ImageAnimateBuilder(animation, MyImage());
}
@override
void dispose() {
// TODO: implement dispose
super.dispose();
controller.dispose();
}
}
class ImageAnimateBuilder extends StatelessWidget {
final Animation animation;
final Widget child;
ImageAnimateBuilder(this.animation, this.child);
@override
Widget build(BuildContext context) {
return Center(
child: AnimatedBuilder(
animation: animation,
child: child,
builder: (BuildContext context, Widget child) {
return Container(
width: animation.value,
height: animation.value,
child: child,
);
},
),
);
}
}
class MyImage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Center(
child: Container(
child: Image.asset(
"images/a2.png",
),
// width: animation.value,
// height: animation.value,
),
);
}
}
复制代码
AnimatedSwitcher
这是flutter提供的一个特殊的widget,效果是对于AnimatedSwitcher的child,当发生变化时,旧值执行去的动画,新值执行来的动画。去和来,指的是同一个动画的forward与reverse。
看一个例子,数字发生变化,当前数字逐渐缩小隐藏,新的数字是放大的过程。
效果:
代码:
import 'package:flutter/material.dart';
class Test32AnimatedSwitcher extends StatefulWidget {
const Test32AnimatedSwitcher({Key key}) : super(key: key);
@override
_State createState() => _State();
}
class _State extends State<Test32AnimatedSwitcher> {
int _count = 0;
@override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
AnimatedSwitcher(
duration: const Duration(milliseconds: 1000),
transitionBuilder: (Widget child, Animation<double> animation) {
//执行缩放动画
return ScaleTransition(child: child, scale: animation);
},
child: Text(
'$_count',
//显示指定key,不同的key会被认为是不同的Text,这样才能执行动画
key: ValueKey<int>(_count),
style: TextStyle(fontSize: 30),
),
),
RaisedButton(
child: const Text('+1',),
onPressed: () {
setState(() {
_count += 1;
});
},
),
],
),
);
}
}
复制代码