一、使用Animation完成简单动画
import 'package:flutter/material.dart';
import 'package:flutter/animation.dart';
class AnimationRoute extends StatefulWidget {
@override
_AnimationRouteState createState() {
// TODO: implement createState
return _AnimationRouteState();
}
}
class _AnimationRouteState extends State<AnimationRoute> with SingleTickerProviderStateMixin{
Animation animation;
AnimationController controller;
@override
void initState() {
// TODO: implement initState
super.initState();
// 创建动画控制器
controller = AnimationController(
vsync: this,
duration: Duration(seconds: 1)
);
// 创建动画曲线
CurvedAnimation curveAnimation = CurvedAnimation(parent: controller, curve: Curves.bounceIn);
animation = Tween(begin: Size(0,0), end: Size(300.0, 200.0)).animate(curveAnimation)
..addListener(() => setState((){}));
}
@override
Widget build(BuildContext context) {
// TODO: implement build
return Center(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(
margin: EdgeInsets.symmetric(vertical: 40.0),
height: 300,
child: Image.asset(
'assets/images/lake.jpg',
width: animation.value.width,
height: animation.value.height,
),
),
Stack(
children: <Widget>[
Positioned(
child: RaisedButton(
child: Text('start animate'),
onPressed: () {
if (animation.value.width == 300) {
controller.reverse();
} else {
controller.forward();
}
// controller.repeat(reverse: true); // 重复动画
},
),
)
],
)
],
),
);
}
@override
void dispose() {
controller.dispose();
super.dispose();
}
}
点击按钮后,代用controller.forward()
方法执行动画,使用controller.repeat(reverse: true)
可以让动画重复执行。上面示例通过CurvedAnimation
设置动画曲线,其中的curve
属性就是曲线属性,Flutter提供了大量动画曲线,如linear
、ease
、bounceIn
等。下面是一个展示各种动画曲线的一个示例,通过选择不同动画曲线,会有不同效果:
动画曲线代码:
import 'package:flutter/material.dart';
import 'package:flutter/animation.dart';
class AnimationRoute extends StatefulWidget {
@override
_AnimationRouteState createState() {
// TODO: implement createState
return _AnimationRouteState();
}
}
class _AnimationRouteState extends State<AnimationRoute>
with SingleTickerProviderStateMixin {
Animation animation;
AnimationController controller;
Curve _selectItem;
Map<String, Curve> map = {
'linear': Curves.linear,
'decelerate': Curves.decelerate,
'fastLinearToSlowEaseIn': Curves.fastLinearToSlowEaseIn,
'ease': Curves.ease,
'easeIn': Curves.easeIn,
'easeInToLinear': Curves.easeInToLinear,
'easeInSine': Curves.easeInSine,
'easeInQuad': Curves.easeInQuad,
'easeInCubic': Curves.easeInCubic,
'easeInQuart': Curves.easeInQuart,
'easeInQuint': Curves.easeInQuint,
'easeInExpo': Curves.easeInExpo,
'easeInCirc': Curves.easeInCirc,
'easeInBack': Curves.easeInBack,
'easeOut': Curves.easeOut,
'linearToEaseOut': Curves.linearToEaseOut,
'easeOutSine': Curves.easeOutSine,
'easeOutQuad': Curves.easeOutQuad,
'easeOutCubic': Curves.easeOutCubic,
'easeOutQuart': Curves.easeOutQuart,
'easeOutQuint': Curves.easeOutQuint,
'easeOutExpo': Curves.easeOutExpo,
'easeOutCirc': Curves.easeOutCirc,
'easeOutBack': Curves.easeOutBack,
'easeInOut': Curves.easeInOut,
'easeInOutSine': Curves.easeInOutSine,
'easeInOutQuad': Curves.easeInOutQuad,
'easeInOutCubic': Curves.easeInOutCubic,
'easeInOutQuart': Curves.easeInOutQuart,
'easeInOutQuint': Curves.easeInOutQuint,
'easeInOutExpo': Curves.easeInOutExpo,
'easeInOutCirc': Curves.easeInOutCirc,
'easeInOutBack': Curves.easeInOutBack,
'fastOutSlowIn': Curves.fastOutSlowIn,
'slowMiddle': Curves.slowMiddle,
'bounceIn': Curves.bounceIn,
'bounceOut': Curves.bounceOut,
'bounceInOut': Curves.bounceInOut,
'elasticIn': Curves.elasticIn,
'elasticOut': Curves.elasticOut,
'elasticInOut': Curves.elasticInOut
};
List<DropdownMenuItem> getDropMenuItems () {
return map.keys.toList().map((item){
return DropdownMenuItem(
value: map[item],
child: Text('$item'),
);
}).toList();
}
@override
void initState() {
// TODO: implement initState
super.initState();
_selectItem = map['linear'];
controller = AnimationController(vsync: this, duration: Duration(seconds: 1));
animation = Tween(begin: Size(0, 0), end: Size(300.0, 200.0))
.animate(CurvedAnimation(parent: controller, curve: Curves.bounceIn))
..addListener(() => setState(() {}));
}
@override
Widget build(BuildContext context) {
// TODO: implement build
return Center(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(
margin: EdgeInsets.symmetric(vertical: 40.0),
height: 200,
child: Image.asset(
'assets/images/lake.jpg',
width: animation.value.width,
height: animation.value.height,
),
),
Wrap(
runAlignment: WrapAlignment.center,
alignment: WrapAlignment.center,
children: <Widget>[
DropdownButton(
value: _selectItem,
items: getDropMenuItems(),
onChanged: (item){
setState(() {
_selectItem = item;
controller.reset();
animation = Tween(begin: Size(0, 0), end: Size(300.0, 200.0))
.animate(CurvedAnimation(parent: controller, curve: item))
..addListener(() => setState(() {}));
controller.forward();
});
},
)
],
),
],
),
);
}
@override
void dispose() {
controller.dispose();
super.dispose();
}
}
二、使用AnimatedWidget
使用AnimatedWidget
不再需要setState
。
import 'package:flutter/material.dart';
class AnimationWidgetDemo extends AnimatedWidget{
AnimationWidgetDemo({Key key, Animation<Size> animation}): super(key: key, listenable: animation);
@override
Widget build(BuildContext context) {
final Animation<Size> animation = listenable;
// TODO: implement build
return Image.asset(
'assets/images/lake.jpg',
width: animation.value.width,
height: animation.value.height,
);
}
}
class AnimatinoWidgetRoute extends StatefulWidget{
@override
_AnimatinoWidgetRouteState createState() {
// TODO: implement createState
return _AnimatinoWidgetRouteState();
}
}
class _AnimatinoWidgetRouteState extends State<AnimatinoWidgetRoute> with SingleTickerProviderStateMixin{
AnimationController controller;
Animation<Size> animation;
@override
void initState() {
// TODO: implement initState
super.initState();
controller = AnimationController(vsync: this, duration: Duration(seconds: 2));
animation = Tween(begin: Size(0, 0), end: Size(300, 200)).animate(controller);
}
@override
Widget build(BuildContext context) {
// TODO: implement build
return Center(
child: Column(
children: <Widget>[
Container(
margin: EdgeInsets.symmetric(vertical: 40.0),
height: 200,
child: AnimationWidgetDemo(animation: animation,)
),
RaisedButton(
child: Text('start animation'),
onPressed: () {
controller.forward();
},
)
],
),
);
}
}
三、使用AnimatedBuilder
使用AnimatedBuilder
与AnimatedWidget
同样不需要调用setState
,并且可以直接在组件中使用,不需要单独抽离出来。
import 'package:flutter/material.dart';
class AnimatedBuilderDemo extends StatefulWidget{
@override
_AnimatedBuilderDemoState createState() {
// TODO: implement createState
return _AnimatedBuilderDemoState();
}
}
class _AnimatedBuilderDemoState extends State<AnimatedBuilderDemo> with SingleTickerProviderStateMixin{
AnimationController controller;
Animation animation;
@override
void initState() {
// TODO: implement initState
super.initState();
controller = AnimationController(vsync: this, duration: Duration(seconds: 2));
animation = Tween(begin: Size(0, 0), end: Size(300, 200))
.animate(CurvedAnimation(parent: controller, curve: Curves.bounceIn));
}
@override
Widget build(BuildContext context) {
// TODO: implement build
return AnimatedBuilder(
animation: animation,
builder: (context, child){
return Center(
child: Column(
children: <Widget>[
Container(
margin: EdgeInsets.symmetric(vertical: 40.0),
height: 200,
child: Image.asset(
'assets/images/lake.jpg',
width: animation.value.width,
height: animation.value.height,
)
),
RaisedButton(
child: Text('start animation'),
onPressed: () {
controller.forward();
},
)
],
),
);
},
);
}
}
四、动画状态监听
使用Animationd的addStatusListener
方法可以监听动画状态。Flutter中有四种动画状态,在AnimationStatus
枚举类中定义:
枚举值 | 说明 |
---|---|
dismissed | 动画在起始点停止 |
forward | 动画正在正向执行 |
reverse | 动画正在反向执行 |
completed | 动画在终点完成 |
animation = Tween(begin: Size(0, 0), end: Size(300, 200))
.animate(CurvedAnimation(parent: controller, curve: Curves.bounceIn))
..addStatusListener((status){
print(status);
});