今天開始學習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();
}