1. 一个简单例子
我们通过一个下面一个简单的放大动画来了解动画中相关的Api。
import 'package:flutter/animation.dart';
import 'package:flutter/material.dart';
class HomeApp extends StatefulWidget {
_HomeAppState createState() => new _HomeAppState();
}
class _HomeAppState extends State<HomeApp> with SingleTickerProviderStateMixin {
Animation<double> animation;
AnimationController controller;
initState() {
super.initState();
controller = new AnimationController(
duration: const Duration(milliseconds: 2000), vsync: this);
animation = new Tween(begin: 20.0, end: 300.0).animate(controller)
..addListener(() {
setState(() {
// the state that has changed here is the animation object’s value
});
});
}
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('一个简单的放大动画'),
),
body: new Center(
child: Column(
children: <Widget>[
new Container(
margin: new EdgeInsets.symmetric(vertical: 10.0),
height: animation.value,
width: animation.value,
child: new FlutterLogo(),
),
FlatButton(
onPressed: () {
controller.forward();
},
child: Text('放大'),
)
],
),
),
),
);
}
dispose() {
controller.dispose();
super.dispose();
}
}
void main() {
runApp(new HomeApp());
}
从上述代码中,我们知道要实现一个简单的缩放动画,我们需要了解下面的api,
- Animation
该对象是Flutter动画库中的一个核心类,拥有当前动画的当前状态(例如它是开始、停止还是向前或向后移动)以及通过value获取动画的当前值,但该对象本身和UI渲染没有任何关系,Animation可以生成除double之外的其他类型值,如:Animation<Color>或Animation<Size>。 - AnimationController
主要是用来管理Animation的。该类派生自Animation<double>。默认情况下,AnimationController在给定的时间段内会线性的生成0.0到1.0之间的数字。当创建一个AnimationController时,需要传递一个vsync参数,存在vsync时会使得当动画的UI不在当前屏幕时,消耗不必要的资源。 - Tween
默认情况下,AnimationController对象的范围从0.0到1.0,如果我们需要不同的数据类型,则可以使用Tween来配置动画以生成不同的范围活数据类型的值。 - addListener
是动画的监听器,只要动画的值发生变化就会调用监听器,因此我们可以常用监听器来更新我们的UI界面。
2. AnimatedWidget
上述例子中,我们最终是在addListener中利用setState方法来给widget添加动画,而AnimatedWidget的相关系列将会帮我们省略掉这一步,Flutter API提供的关于AnimatedWidget的示例包括:AnimatedBuilder、AnimatedModalBarrier、DecoratedBoxTransition、FadeTransition、PositionedTransition、RelativePositionedTransition、RotationTransition、ScaleTransition、SizeTransition、SlideTransition。
import 'package:flutter/material.dart';
class AnimatedLogo extends AnimatedWidget {
AnimatedLogo({Key key, Animation<double> animation})
: super(key: key, listenable: animation);
@override
Widget build(BuildContext context) {
final Animation<double> animation = listenable;
return new Center(
child: new Container(
margin: new EdgeInsets.symmetric(vertical: 10.0),
height: animation.value,
width: animation.value,
child: new FlutterLogo(),
),
);
}
}
class HomeApp extends StatefulWidget {
_HomeAppState createState() => new _HomeAppState();
}
class _HomeAppState extends State<HomeApp> with SingleTickerProviderStateMixin {
Animation<double> animation;
AnimationController controller;
initState() {
super.initState();
controller = new AnimationController(
duration: const Duration(milliseconds: 2000), vsync: this);
animation = new Tween(begin: 20.0, end: 300.0).animate(controller)
..addListener(() {
setState(() {
// the state that has changed here is the animation object’s value
});
});
}
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('一个简单的放大动画'),
),
body: new Center(
child: Column(
children: <Widget>[
AnimatedLogo(animation: animation),
FlatButton(
onPressed: () {
controller.forward();
},
child: Text('放大'),
)
],
),
),
),
);
}
dispose() {
controller.dispose();
super.dispose();
}
}
void main() {
runApp(new HomeApp());
}
3. 监听动画的状态
addStatusListener方法可以监听到动画在整个运动期间的状态。
class _HomeAppState extends State<HomeApp> with SingleTickerProviderStateMixin {
Animation<double> animation;
AnimationController controller;
initState() {
super.initState();
controller = new AnimationController(
duration: const Duration(milliseconds: 2000), vsync: this);
animation = new Tween(begin: 20.0, end: 300.0).animate(controller)
..addStatusListener((state){
print('----->$state');
});
}
打印log:
flutter: ----->AnimationStatus.forward flutter: ----->AnimationStatus.completed
下面我们利用该监听来实现一个循环动画,
class _HomeAppState extends State<HomeApp> with SingleTickerProviderStateMixin {
Animation<double> animation;
AnimationController controller;
initState() {
super.initState();
controller = new AnimationController(
duration: const Duration(milliseconds: 2000), vsync: this);
animation = new Tween(begin: 20.0, end: 300.0).animate(controller)
..addStatusListener((state) {
print('----->$state');
if (state == AnimationStatus.completed) {
controller.reverse();
} else if (state == AnimationStatus.dismissed) controller.forward();
});
}
4. 利用AnimatedBuilder进行重构
上面的例子中我们发现一个问题:动画的logo我们不可以任意的替换。这时我们就可以利用AnimatedWidget来实现。
class GrowTransition extends StatelessWidget {
GrowTransition({this.child, this.animation});
final Widget child;
final Animation<double> animation;
Widget build(BuildContext context) {
return new Center(
child: new AnimatedBuilder(
animation: animation,
builder: (BuildContext context, Widget child) {
return new Container(
height: animation.value, width: animation.value, child: child);
},
child: child),
);
}
}
class HomeApp extends StatefulWidget {
_HomeAppState createState() => new _HomeAppState();
}
class _HomeAppState extends State<HomeApp> with SingleTickerProviderStateMixin {
Animation<double> animation;
AnimationController controller;
initState() {
super.initState();
controller = new AnimationController(
duration: const Duration(milliseconds: 2000), vsync: this);
final CurvedAnimation curved =
new CurvedAnimation(parent: controller, curve: Curves.easeIn);
animation = new Tween(begin: 20.0, end: 300.0).animate(curved);
}
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('一个简单的放大动画'),
),
body: new Center(
child: Column(
children: <Widget>[
GrowTransition(
child: FlutterLogo(),
animation: animation,
),
FlatButton(
onPressed: () {
controller.forward();
},
child: Text('放大'),
)
],
),
),
),
);
}
dispose() {
controller.dispose();
super.dispose();
}
}
void main() {
runApp(new HomeApp());
}
重构之后,我们发现,我们可以将logo替换成任何我们想要的一个widget。
5. 并行动画
在上面动画的基础上我们再给logo添加一个透明度变化的动画。下面的例子中我们将学会如何在同一个动画控制器上使用多个Tween,其中每个Tween管理动画中的不同效果。
class AnimatedLogo extends AnimatedWidget {
AnimatedLogo({Key key, Animation<double> animation})
: super(key: key, listenable: animation);
static final _opacityTween = new Tween(begin: 0.1, end: 1.0);
static final _sizeTween = new Tween(begin: 0.0, end: 300);
@override
Widget build(BuildContext context) {
final Animation<double> animation = listenable;
return new Center(
child: new Opacity(
opacity: _opacityTween.evaluate(animation),
child: new Container(
margin: new EdgeInsets.symmetric(vertical: 10.0),
height: _sizeTween.evaluate(animation),
width: _sizeTween.evaluate(animation),
child: new FlutterLogo(),
),
),
);
}
}
class HomeApp extends StatefulWidget {
_HomeAppState createState() => new _HomeAppState();
}
class _HomeAppState extends State<HomeApp> with SingleTickerProviderStateMixin {
Animation<double> animation;
AnimationController controller;
initState() {
super.initState();
controller = new AnimationController(
duration: const Duration(milliseconds: 2000), vsync: this);
animation = new CurvedAnimation(parent: controller, curve: Curves.easeIn);
animation.addStatusListener((status) {
if (status == AnimationStatus.completed) {
controller.reverse();
} else if (status == AnimationStatus.dismissed) {
controller.forward();
}
});
}
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('一个简单的放大动画'),
),
body: new Center(
child: Column(
children: <Widget>[
AnimatedLogo(
animation: animation,
),
FlatButton(
onPressed: () {
controller.forward();
},
child: Text('放大'),
)
],
),
),
),
);
}
dispose() {
controller.dispose();
super.dispose();
}
}
void main() {
runApp(new HomeApp());
}
网友评论