今天开始学习flutter中动画的使用,什么叫绘制动画呢,其实我想表达的意思是,结合上一篇文章中学习的绘制,本文中让它动起来?
准备着手做一个类似于加载圈的动画,比如说下拉刷新,或者页面网络请求加载时的动画,这篇文章先做一个建议版的,来个动画入门?
先对flutter中的动画做个简单介绍:
Flutter的动画也不复杂,关键词AnimationController
。
Flutter中的动画是基于Animation
,这个对象本身是一个抽象类,在一段时间内依次产生一些值。我们使用封装好的AnimationController
来做动画,它在屏幕刷新的每一帧,产生一个新的值,默认情况是在给定的时间段内线性的生成0.0到1.0的数字。
需要注意的是在使用AnimationController
的时候需要结合TickerProvider
,因为只有在TickerProvider
下才能配置AnimationController
中的构造参数vsync
。TickerProvider
是一个抽象类,所以我们一般使用它的实现类TickerProviderStateMixin
和SingleTickerProviderStateMixin
。
那么,这两种方式有什么不同呢? 如果整个生命周期中,只有一个AnimationController
,那么就使用SingleTickerProviderStateMixin
,因为此种情况下,它的效率相对来说要高很多。反之,如果有多个AnimationController
,就是用TickerProviderStateMixin
。
还有duration
属性可以设置持续时间。还有一些方法可以控制动画forward
启动,reverse
反转,repeat
重复。
AnimationController
有addListener
和addStatusListener
方法可以添加监听,一个是值监听一个是状态监听。值监听常用在调用setState
来触发UI重建来实现动画,状态监听用在动画状态变化的时候执行一些方法,比如在动画结束时反转动画。
简单了解完上述的这些属性和方法之后,就可以做一个简单地动画来练练手了
这里的需求是一段圆弧在给定的圆形路径上不断转圈,在点击click之后,整个圆形路径填充一圈,然后结束
这里我们先进行绘制,顺便复习一下上一篇文章里面学到的内容:
class MyPainter extends CustomPainter {
Color lineColor;
Color completeColor;
double startPercent;//圆弧转动时 开始的位置
bool isComplete = false;
double width;
MyPainter(
{this.lineColor,
this.completeColor,
this.startPercent,
this.width,
this.isComplete : false});
@override
void paint(Canvas canvas, Size size) {
Paint line = Paint()
..color = lineColor
..strokeCap = StrokeCap.round
..style = PaintingStyle.stroke
..strokeWidth = width;
Paint complete = Paint()
..color = completeColor
..strokeCap = StrokeCap.round
..style = PaintingStyle.stroke
..strokeWidth = width;
Offset center = Offset(size.width / 2, size.height / 2); // 坐标中心
double radius = min(size.width / 2, size.height / 2); // 半径
canvas.drawCircle(
// 画圆方法
center,
radius,
line);
double arcAngle = isComplete == true ? startPercent : 2 * pi / 10;
//画转动的圆弧
canvas.drawArc(Rect.fromCircle(center: center, radius: radius),
isComplete == true ? 2 * pi : startPercent, arcAngle, false, complete);
}
@override
bool shouldRepaint(MyPainter oldDelegate) {
return startPercent != oldDelegate.startPercent;
}
}
所谓动画,大概就是动起来的画吧,现在画已经有了,下一步就是让他动起来
class _HomeContentState extends State<HomeContent>
with TickerProviderStateMixin {
double percentage = 0.0;
AnimationController percentageAnimationController;
bool isClick = false;
@override
void initState() {
super.initState();
percentageAnimationController = new AnimationController(
vsync: this, duration: new Duration(milliseconds: 2000))
..repeat()
..addListener(() {
setState(() {
percentage = percentageAnimationController.value * pi * 2;
});
});
}
@override
Widget build(BuildContext context) {
return new Center(
child: new Container(
height: 200.0,
width: 200.0,
child: new CustomPaint(
foregroundPainter: new MyPainter(
lineColor: Colors.lightBlueAccent,
completeColor: Colors.blueAccent,
startPercent: percentage,
isComplete: isClick,
width: 8.0),
child: new Padding(
padding: const EdgeInsets.all(8.0),
child: new RaisedButton(
color: Colors.green,
splashColor: Colors.transparent,
shape: new CircleBorder(),
child: new Text("Click"),
onPressed: () {
setState(() {
isClick = true;
percentageAnimationController.forward(from: 0.0);
});
}),
),
),
),
);
}
}
就是上面介绍动画里面的步骤,就不再重复了,当然也可以换一种写法用AnimatedBuilder,就不需要setState了
Widget build(BuildContext context) {
return new Center(
child: new Container(
height: 200.0,
width: 200.0,
child: AnimatedBuilder(animation: percentageAnimationController, builder: (context, child) {
return new CustomPaint(
foregroundPainter: new MyPainter(
lineColor: Colors.lightBlueAccent,
completeColor: Colors.blueAccent,
startPercent: percentage,
isComplete: isClick,
width: 8.0),
child: new Padding(
padding: const EdgeInsets.all(8.0),
child: new RaisedButton(
color: Colors.green,
splashColor: Colors.transparent,
shape: new CircleBorder(),
child: new Text("Click"),
onPressed: () {
isClick = true;
percentageAnimationController.forward(from: 0.0);
}),
),
);
})
),
);
}
有一点需要注意的是,controller的销毁
@override
void dispose() {
percentageAnimationController.dispose();
super.dispose();
}