1. 一個簡單例子
我們通過一個下面一個簡單的放大動畫來了解動畫中相關(guān)的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());
}
從上述代碼中棘利,我們知道要實現(xiàn)一個簡單的縮放動畫,我們需要了解下面的api,
- Animation
該對象是Flutter動畫庫中的一個核心類五续,擁有當(dāng)前動畫的當(dāng)前狀態(tài)(例如它是開始洒敏、停止還是向前或向后移動)以及通過value獲取動畫的當(dāng)前值,但該對象本身和UI渲染沒有任何關(guān)系返帕,Animation可以生成除double之外的其他類型值桐玻,如:Animation<Color>或Animation<Size>。 - AnimationController
主要是用來管理Animation的荆萤。該類派生自Animation<double>镊靴。默認情況下,AnimationController在給定的時間段內(nèi)會線性的生成0.0到1.0之間的數(shù)字链韭。當(dāng)創(chuàng)建一個AnimationController時偏竟,需要傳遞一個vsync參數(shù),存在vsync時會使得當(dāng)動畫的UI不在當(dāng)前屏幕時敞峭,消耗不必要的資源踊谋。 - Tween
默認情況下,AnimationController對象的范圍從0.0到1.0旋讹,如果我們需要不同的數(shù)據(jù)類型殖蚕,則可以使用Tween來配置動畫以生成不同的范圍活數(shù)據(jù)類型的值。 - addListener
是動畫的監(jiān)聽器沉迹,只要動畫的值發(fā)生變化就會調(diào)用監(jiān)聽器睦疫,因此我們可以常用監(jiān)聽器來更新我們的UI界面。
2. AnimatedWidget
上述例子中鞭呕,我們最終是在addListener中利用setState方法來給widget添加動畫蛤育,而AnimatedWidget的相關(guān)系列將會幫我們省略掉這一步,F(xiàn)lutter API提供的關(guān)于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. 監(jiān)聽動畫的狀態(tài)
addStatusListener方法可以監(jiān)聽到動畫在整個運動期間的狀態(tài)。
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
下面我們利用該監(jiān)聽來實現(xiàn)一個循環(huán)動畫胡控,
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進行重構(gòu)
上面的例子中我們發(fā)現(xiàn)一個問題:動畫的logo我們不可以任意的替換扳剿。這時我們就可以利用AnimatedWidget來實現(xiàn)。
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());
}
重構(gòu)之后昼激,我們發(fā)現(xiàn)庇绽,我們可以將logo替換成任何我們想要的一個widget锡搜。
5. 并行動畫
在上面動畫的基礎(chǔ)上我們再給logo添加一個透明度變化的動畫。下面的例子中我們將學(xué)會如何在同一個動畫控制器上使用多個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());
}