动画在目前应用开发中整体使用较少,这里只整理基础的核心动画原理,以备学习查阅。
显式动画
AnimationController
AnimationController,它是动画控制器,控制动画的启动、停止,还可以获取动画的运行状态,AnimationController 通常在 initState 方法中初始化。
- vsync:当创建 AnimationController 时,需要传递一个
vsync参数,存在vsync时会防止屏幕外动画消耗不必要的资源,单个 AnimationController 的时候使用 SingleTickerProviderStateMixin,多个 AnimationController 使用 TickerProviderStateMixin。 - duration:表示动画执行的时间。
class AnimationBaseDemo extends StatefulWidget {
@override
_AnimationBaseDemoState createState() => _AnimationBaseDemoState();
}
class _AnimationBaseDemoState extends State<AnimationBaseDemo>
with SingleTickerProviderStateMixin{
double _size = 100;
AnimationController _controller;
@override
void initState();
super.initState();
//AnimationController 的值默认是 0 到 1
_controller = AnimationController(vsync: this,duration: Duration(milliseconds: 500))
//监听动画执行,会多次调用
..addListener(() {
setState(() {
_size = 100+100*_controller.value;
});
})
//监听动画状态
..addStatusListener((status) {
print('status:$status');
});
@override
Widget build(BuildContext context) {
return Center(
child: GestureDetector(
onTap: () {
_controller.forward();
},
child: Container(
height: _size,
width: _size,
color: Colors.blue,
alignment: Alignment.center,
child: Text('点我变大',style: TextStyle(color: Colors.white,fontSize: 18),),
),
),
);
}
}
动画的状态分为四种:
- dismissed:动画停止在开始处。
- forward:动画正在从开始处运行到结束处(正向运行)。
- reverse:动画正在从结束处运行到开始处(反向运行)。
- completed:动画停止在结束处。
再来看下动画的控制方法:
- forward:正向执行动画。
- reverse:反向执行动画。
- repeat:反复执行动画。
- reset:重置动画。
AnimatedBuilder
AnimatedBuilder自动监听来自Animation对象的通知,不需要手动调用addListener()。
void initState() {
super.initState();
controller =
AnimationController(duration: const Duration(seconds: 1), vsync: this);
animation = Tween<double>(begin: 0, end: 1.0).animate(controller);
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: AnimatedBuilder(
animation: controller,
builder: (BuildContext context, Widget? child) {
return Container(
height: 200,
padding: const EdgeInsets.all(10),
margin: const EdgeInsets.all(10),
decoration: BoxDecoration(
shape: BoxShape.circle,
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: const [Colors.yellow, Colors.red],
stops: [0, controller.value])),
);
},
),
floatingActionButton: FloatingActionButton(
onPressed: () {
// 点击事件,设置动画开始
if (controller.status == AnimationStatus.completed) {
controller.reverse();
} else {
controller.forward();
}
},
child: const Icon(Icons.play_arrow),
),
);
}
Tween
AnimationController 设置的最小/大值类型是 double,但要设置颜色变化或其它变化就需要做其它映射。
class _TweenDemoState extends State<TweenDemo>
with SingleTickerProviderStateMixin {
AnimationController _controller;
Animation<Color> _animation;
@override
void initState() {
super.initState();
_controller =
AnimationController(vsync: this, duration: Duration(milliseconds: 500))
..addListener(() {
setState(() {});
});
//动画的控制依然由 AnimationController 控制,
//但需要 **Tween.animate(_controller)** 将控制器传递给Tween。
_animation =
ColorTween(begin: Colors.blue, end: Colors.red).animate(_controller);
}
@override
Widget build(BuildContext context) {
return Center(
child: GestureDetector(
onTap: () {
_controller.forward();
},
child: Container(
height: 100,
width: 100,
color: _animation.value,
alignment: Alignment.center,
child: Text(
'点我变色',
style: TextStyle(color: Colors.white, fontSize: 18),
),
),
),
);
}
@override
void dispose() {
super.dispose();
_controller.dispose();
}
}
渐变颜色的动画效果
controller =
AnimationController(duration: const Duration(seconds: 1), vsync: this);
animation = Tween<double>(begin: 0, end: 1.0).animate(controller);
AnimatedBuilder(
animation: controller,
builder: (BuildContext context, Widget? child) {
return Container(
height: 200,
padding: const EdgeInsets.all(10),
margin: const EdgeInsets.all(10),
decoration: BoxDecoration(
shape: BoxShape.circle,
gradient: LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: const [Colors.yellow, Colors.red],
stops: [0, controller.value])),
);
},
)
Curve
动画中还有一个Curve动画行进曲线的概念,负责控制动画变化的速率,通俗地讲就是使动画的效果能够以匀速、加速、减速、抛物线等各种速率变化。
_animation = Tween(begin: 100.0, end: 200.0)
.chain(CurveTween(curve: Curves.bounceIn))
.animate(_controller);
或者
_animation = _controller
.drive(CurveTween(curve: Curves.bounceIn))
.drive(Tween(begin: 100.0, end: 200.0));
系统已经提供了38种常用到动画曲线,以下是常用的几种曲线类型: linear
decelerate
bounceIn
bounceOut
elasticIn
Interval
Flutter中组合动画使用Interval,Interval继承自Curve。
动画从0.5(一半)开始到结束,如果动画时长为6秒,则从第3秒开始。
Interval中begin 和end参数值的范围是0.0到1.0。
Animation _sizeAnimation = Tween(begin: 100.0, end: 300.0).animate(CurvedAnimation(
parent: _animationController, curve: Interval(0.5, 1.0)));
先执行颜色变化,再执行大小变化
class AnimationDemo extends StatefulWidget {
@override
State<StatefulWidget> createState() => _AnimationDemo();
}
class _AnimationDemo extends State<AnimationDemo>
with SingleTickerProviderStateMixin {
AnimationController _animationController;
Animation _colorAnimation;
Animation _sizeAnimation;
@override
void initState() {
_animationController =
AnimationController(duration: Duration(seconds: 5), vsync: this)
..addListener((){setState(() {
});});
//如果需要两个动画同时执行,只需将2个 Interval 的值都改 Interval(0.0, 1.0) 。
_colorAnimation = ColorTween(begin: Colors.red, end: Colors.blue).animate(
CurvedAnimation(
parent: _animationController, curve: Interval(0.0, 0.5)));
_sizeAnimation = Tween(begin: 100.0, end: 300.0).animate(CurvedAnimation(
parent: _animationController, curve: Interval(0.5, 1.0)));
//开始动画
_animationController.forward();
super.initState();
}
@override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Container(
height: _sizeAnimation.value,
width: _sizeAnimation.value,
color: _colorAnimation.value),
],
),
);
}
@override
void dispose() {
_animationController.dispose();
super.dispose();
}
}
类似需要求也可以用 TweenSequence实现
_animation = TweenSequence([
//前40%的时间大小从100->200
TweenSequenceItem(
tween: Tween(begin: 100.0, end: 200.0)
.chain(CurveTween(curve: Curves.easeIn)),
weight: 40),
//200不变20%的时间
TweenSequenceItem(tween: ConstantTween<double>(200.0), weight: 20),
//最后40%的时间大小从200->300
TweenSequenceItem(tween: Tween(begin: 200.0, end: 300.0), weight: 40),
]).animate(_animationController);
隐式动画
项⽬中如果大量使用上面那种完整的动画写法,代码会变得非常冗余,所以 Flutter 官⽅默认提供了⼀些动画
封装,也可以叫做隐式动画,常⻅是 Animated* 开头的 Widget,
例如 AnimatedPositioned 、 AnimatedContainer 、AnimatedPadding 、 AnimatedOpacity 等控件,其内部已经完全封装好逻辑,只需要配置对应参数就可以触发动画。
body: Center(
child: AnimatedContainer(
width: _width,
height: _height,
decoration: BoxDecoration(
color: _color,
borderRadius: _borderRadius,
),
duration: Duration(seconds: 1),
curve: Curves.fastOutSlowIn,
),
)
如上代码所示,只需改变 _width 和 _height 就可以触发动画效果,当然改变其他某些属性也可以进行动画,比如下面的 alignment 和 color等。