上一篇文章Flutter 動(dòng)畫詳解系列概述了下Flutter中的動(dòng)畫類型匹舞,及如何選擇恰當(dāng)?shù)膭?dòng)畫創(chuàng)建方式椿息,接下來我們來看下最簡單的動(dòng)畫涛菠,隱式動(dòng)畫铡恕。
系統(tǒng)的隱式動(dòng)畫Widget
在 Flutter 中的 Widgets 中有一部已經(jīng)實(shí)現(xiàn)隱式動(dòng)畫Widget。如下圖列出部分:
首先我們來看一段未使用動(dòng)畫的代碼:
bool _bigger = false;
@override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Center(
child: Container(
width: _bigger ? 100 : 500,
height: 100,
color: Colors.red,
),
),
RaisedButton(
onPressed: () => setState(() {
_bigger = !_bigger;
}),
child: Icon(Icons.star),
),
],
),
);
}
未加動(dòng)畫效果時(shí)脏答,矩形的形變會(huì)顯得十分生硬糕殉,如果使用AnimatedContainer
替換Container
,增加一個(gè)動(dòng)畫的過渡效果:
Center(
child: AnimatedContainer(
width: _bigger ? 100 : 500,
height: 100,
color: Colors.red,
duration: Duration(seconds: 1),
),
),
整個(gè)過渡過程顯得比較自然順暢殖告,我們通過新舊值之間的值進(jìn)行動(dòng)畫處理的過程稱為插值阿蝶。每當(dāng)舊值和新值發(fā)生變化時(shí),AnimatedContainer便會(huì)處理其屬性插值黄绩。
同樣我們也可以通過插值來修改AnimatedContainer的其它屬性羡洁,包括decoration
的漸變色:
AnimatedContainer(
decoration: BoxDecoration(
gradient: RadialGradient(
colors: [Colors.purple, Colors.transparent],
stops: [ _bigger ? 0.2 : 0.5, 1.0])
),
),
上述代碼很簡單的演示如何使用 隱式動(dòng)畫Widget 來實(shí)現(xiàn)動(dòng)畫效果,非常的方便簡單爽丹,但這也意味著可靈活性較差筑煮,在隱式動(dòng)畫Widget中,我們控制動(dòng)畫效果只能控制動(dòng)畫時(shí)長(Duration)和動(dòng)畫的曲線(Curve,具體的曲線效果可以參考 系統(tǒng)自帶的曲線效果)粤蝎。
AnimatedContainer(
width: _bigger ? 100 : 500,
child: Image.asset('assets/star.png'),
duration: Duration(seconds: 1),
curve: Curves.easeInOutQuint,
),
另外除了系統(tǒng)自帶的曲線效果外真仲,我們還可以通過繼承Curve
來實(shí)現(xiàn)自定義的曲線效果,如下實(shí)現(xiàn)了正弦曲線。
class SineCurve extends Curve {
final double count;
SineCurve({this.count = 1});
@override
double transformInternal(double t) {
return sin(count * 2 * pi * t) * 0.5 + 0.5;
}
}
小結(jié)
在Flutter中初澎,系統(tǒng)已經(jīng)提供了隱式動(dòng)畫的Widget秸应,這些Widget是普通Widget的動(dòng)畫版本,我們可以通過 duration
、curve
來控制動(dòng)畫效果软啼。
還有我們不一定需要通過StatefulWidget中使用setState來生成動(dòng)畫效果桑谍,我們也可以使用StreamBuilder和FutureBuilder來觸發(fā)動(dòng)畫。
FutureBuilder(
future: future,
builder: (BuildContext context, AsyncSnapshot<String> snapshot) {
double width;
switch(snapshot.connectionState) {
case ConnectionState.none:
case ConnectionState.waiting:
case ConnectionState.active:
width = 0;
break;
case ConnectionState.done:
width = 500;
break;
}
return AnimatedContainer(
width: width,
height: 100,
color: Colors.red,
duration: Duration(seconds: 1),
);
}
),
以上祸挪,如果Flutter框架提供給你的隱式動(dòng)畫Widget不能滿足你的需求锣披,那么進(jìn)一步的話可以試試使用TweenAnimationBuilder
來自定義創(chuàng)建隱式動(dòng)畫。
自定義隱式動(dòng)畫TweenAnimationBuilder
使用TweenAnimationBuilder贿条,該 Widget 使用的時(shí)候我們需要傳遞 duration 參數(shù)動(dòng)畫時(shí)間雹仿、tween 參數(shù)動(dòng)畫要設(shè)置的值的范圍(補(bǔ)間)、重要的還有 builder 參數(shù)闪唆,builder函數(shù)的參數(shù)包含context盅粪、補(bǔ)間參數(shù)tween的類型钓葫、還有child
,讓我們看一個(gè)簡單的例子悄蕾,紅色的矩形框旋轉(zhuǎn)360度:
TweenAnimationBuilder<double>(
tween: Tween<double>(begin: 0, end: 2 * pi),
duration: Duration(seconds: 2),
builder: (BuildContext context, double angle, Widget child) {
return Transform.rotate(
angle: angle,
child: Container(
color: Colors.red,
width: 100,
height: 100,
),
);
},
),
讓我們?cè)賮砜磦€(gè)例子,使用ColorFilered
Wideget做一個(gè)圖片渲染的效果础浮。
TweenAnimationBuilder(
tween: ColorTween(begin: Colors.white, end: Colors.red),
duration: Duration(seconds: 2),
builder: (_, Color color, __) {
return ColorFiltered(
child: Image.asset('assets/sun.png'),
colorFilter: ColorFilter.mode(color, BlendMode.modulate),
);
},
)
通過Tween補(bǔ)間參數(shù)設(shè)置了從白色到紅色的過渡帆调,由顏色和圖片的混合,另外如何補(bǔ)間參數(shù)可變的豆同,所以如果補(bǔ)間參數(shù)是不變的話可以將參數(shù)聲明為靜態(tài)常量來使用番刊。
static final colorTween = ColorTween(begin: Colors.white, end: Colors.red);
Center(
child: TweenAnimationBuilder<Color>(
tween: colorTween,
duration: Duration(seconds: 2),
builder: (_, Color color, __) {
return ColorFiltered(
child: Image.asset('assets/sun.png'),
colorFilter: ColorFilter.mode(color, BlendMode.modulate),
);
},
),
),
動(dòng)態(tài)修改 Tween 參數(shù)
上面的例子中我們并沒有調(diào)用setState,僅僅展示了動(dòng)畫從Tween的初始值到終值的簡單動(dòng)畫效果影锈,除此之外芹务,我們還可以通過動(dòng)態(tài)的修改Tween來實(shí)現(xiàn)動(dòng)畫效果:
class OngoingAnimationByModifyingEndTweenValue extends StatefulWidget {
@override
_OngoingAnimationState createState() => _OngoingAnimationState();
}
class _OngoingAnimationState extends State<OngoingAnimationByModifyingEndTweenValue> {
double _newValue = .4;
Color _newColor = Colors.white;
@override
Widget build(BuildContext context) {
return Stack(
children: <Widget>[
starsBackground,
Column(
children: <Widget>[
Center(
child: TweenAnimationBuilder(
tween: ColorTween(begin: Colors.white, end: _newColor),
duration: Duration(seconds: 2),
builder: (_, Color color, __) {
return ColorFiltered(
child: Image.asset('assets/sun.png'),
colorFilter: ColorFilter.mode(color, BlendMode.modulate),
);
},
),
),
Slider.adaptive(
value: _newValue,
onChanged: (double value) {
setState(() {
_newValue = value;
_newColor = Color.lerp(Colors.white, Colors.red, value);
});
},
),
],
),
],
);
}
}
首先我們聲明一個(gè)_newColor
作為 Tween 的終值,通過滑動(dòng) Slider
Widget,我們改變_newColor
鸭廷。調(diào)用setState每次的滑動(dòng)都會(huì)使得動(dòng)畫更新枣抱。
另外,需要明確的一點(diǎn)事辆床,TweenAnimationBuilder 的動(dòng)畫屬性值是從當(dāng)前值向最新的終值轉(zhuǎn)變的佳晶。如上面的例子,當(dāng)我們拖動(dòng) Slider 時(shí)讼载,顏色變化相對(duì)于之前的顏色,而不是每次從最初的白色開始動(dòng)畫漸變轿秧。
TweenAnimationBuilder 總會(huì)將當(dāng)前顏色到終值顏色平滑的動(dòng)畫過渡。所以如果改變的不是終值顏色而是開始顏色咨堤,動(dòng)畫效果是不會(huì)有區(qū)別的菇篡。
補(bǔ)充說明
除了上訴介紹的TweenAnimationBuilder參數(shù)外,我們還需要注意的參數(shù)還有:
- curve一喘,動(dòng)畫曲線逸贾,在上一篇文章Flutter 動(dòng)畫詳解系列有過介紹。
- 動(dòng)畫完成的回調(diào)
onEnd :
,我們可以在動(dòng)畫完成時(shí)完成指定的操作铝侵,如動(dòng)畫完成后顯示另一個(gè)Widget灼伤。 - child參數(shù),child參數(shù)的設(shè)置其實(shí)也是一個(gè)潛在的性能優(yōu)化項(xiàng)咪鲜,正確的設(shè)置child狐赡,對(duì)動(dòng)畫性能的提升也是一大幫助。例子中雖然顏色發(fā)生了變化疟丙,但圖片本身保持不變颖侄,但是當(dāng)前代碼是每次build都會(huì)重新創(chuàng)建Image Widget。針對(duì)此類的優(yōu)化方式享郊,我們可以提前創(chuàng)建圖片览祖,將圖片作為參數(shù)傳入,這樣 Flutter 就知道每幀渲染時(shí)變化的是顏色炊琉,而不是圖片本身展蒂。 當(dāng)然因?yàn)槔颖旧砗唵危源藘?yōu)化不會(huì)有明顯的效果苔咪,但當(dāng)實(shí)現(xiàn)復(fù)雜動(dòng)畫效果時(shí)锰悼,慎重考慮child的實(shí)現(xiàn),將會(huì)對(duì)你的動(dòng)畫性能帶來一定的幫助团赏。
總結(jié)
OK箕般,以上就是對(duì) Flutter 中的隱式動(dòng)畫的介紹了,包括系統(tǒng)自帶的AnimatedFoo
和TweenAnimationBuilder
都有了一定的涉及舔清。包括如何不通過使用StatefulWidget
來實(shí)現(xiàn)動(dòng)畫效果丝里、如何改變tween
的終值產(chǎn)生順滑的動(dòng)畫效果、如何提升TweenAnimationBuilder
的動(dòng)畫性能時(shí)体谒,我們可以設(shè)置tween參數(shù)時(shí)可以考慮設(shè)置為靜態(tài)常量杯聚,設(shè)置child參數(shù)時(shí)可以考慮提前創(chuàng)建好child,作為參數(shù)傳遞营密。