要使用Flutter中動(dòng)畫(huà), 首先要熟悉Flutter的動(dòng)畫(huà)基礎(chǔ)概念和相關(guān)類煎源。
- Animation:Flutter中動(dòng)畫(huà)的核心類。
- AnimationController:動(dòng)畫(huà)管理類纳鼎。
- Tween:補(bǔ)間對(duì)象果元,用于計(jì)算動(dòng)畫(huà)使用的數(shù)據(jù)范圍之間的插值契耿。
- Listeners和StatusListeners:用于監(jiān)聽(tīng)動(dòng)畫(huà)狀態(tài)改變。
- CurvedAnimation:用于定義非線性曲線動(dòng)畫(huà) 。
簡(jiǎn)單點(diǎn)說(shuō), 熟悉了上面幾個(gè)術(shù)語(yǔ)和用法, 就基本入門了Flutter的動(dòng)畫(huà)基礎(chǔ). 但是沒(méi)有一定使用經(jīng)驗(yàn), 上面的術(shù)語(yǔ)其實(shí)很抽象. Flutter的官方文檔其實(shí)講解的非常詳細(xì), 具體參見(jiàn): [Flutter中的動(dòng)畫(huà)]
先做個(gè)例子
我們還是做一個(gè)例子來(lái)說(shuō)明其簡(jiǎn)單用法.
import 'package:flutter/material.dart';
class NormalAnimationDemo extends StatefulWidget {
@override
_NormalAnimationDemoState createState() => _NormalAnimationDemoState();
}
class _NormalAnimationDemoState extends State<NormalAnimationDemo>
with SingleTickerProviderStateMixin {
AnimationController _controller;
Animation<double> _animation;
Duration _duration = Duration(milliseconds: 1000);
@override
void initState() {
super.initState();
_controller = AnimationController(vsync: this, duration: _duration)
..addListener(() {
// 用于實(shí)時(shí)更新_animation.value
setState(() {});
})
..addStatusListener((status) {
if (status == AnimationStatus.completed) {
// 監(jiān)聽(tīng)動(dòng)畫(huà)完成的狀態(tài) The animation is stopped at the end
_controller.reverse();
}
if (status == AnimationStatus.dismissed) {
// 監(jiān)聽(tīng)動(dòng)畫(huà)結(jié)束的狀態(tài) The animation is stopped at the beginning
_controller.forward();
}
});
_animation = CurvedAnimation(parent: _controller, curve: Curves.fastOutSlowIn);
_animation = Tween<double>(begin: 0.0, end: 1.0).animate(_animation);
_controller.forward();
}
@override
void dispose() {
_controller?.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('動(dòng)畫(huà)Demo')),
body: Center(
child: Container(
width: 100 * (1 + _animation.value),
height: 100 * (1 + _animation.value),
child: Image.asset('assets/images/head.jpg'),
),
),
);
}
}
動(dòng)畫(huà)效果如下:
總結(jié)一下:
這個(gè)簡(jiǎn)單的例子用到了Animation, AnimationController,CurvedAnimation和Tween的概念, 還對(duì)動(dòng)畫(huà)的過(guò)程進(jìn)行了監(jiān)控.
Animation
在Flutter中碎绎,Animation對(duì)象本身和UI渲染沒(méi)有任何關(guān)系。Animation是一個(gè)抽象類描扯,它擁有其當(dāng)前值和狀態(tài)(完成或停止)。其中一個(gè)比較常用的Animation類是Animation<double>
趟薄。
Flutter中的Animation對(duì)象是一個(gè)在一段時(shí)間內(nèi)依次生成一個(gè)區(qū)間之間值的類绽诚。Animation對(duì)象的輸出可以是線性的、曲線的杭煎、一個(gè)步進(jìn)函數(shù)或者任何其他可以設(shè)計(jì)的映射憔购。
根據(jù)Animation對(duì)象的控制方式,動(dòng)畫(huà)可以反向運(yùn)行岔帽,甚至可以在中間切換方向。
Animation還可以生成除double之外的其他類型值导绷,如:Animation<Color>
或 Animation<Size>
犀勒。
Animation對(duì)象有狀態(tài)⊥浊可以通過(guò)訪問(wèn)其value屬性獲取動(dòng)畫(huà)的當(dāng)前值贾费。
Animation對(duì)象本身和UI渲染沒(méi)有任何關(guān)系。
AnimationController
AnimationController派生自Animation<double>
檐盟,因此可以在需要Animation對(duì)象的任何地方使用褂萧。 但是,AnimationController具有控制動(dòng)畫(huà)的其他方法葵萎。例如导犹,.forward()方法可以啟動(dòng)動(dòng)畫(huà)唱凯。
數(shù)字的產(chǎn)生與屏幕刷新有關(guān),因此每秒鐘通常會(huì)產(chǎn)生60個(gè)數(shù)字谎痢,在生成每個(gè)數(shù)字后磕昼,每個(gè)Animation對(duì)象調(diào)用添加的Listener對(duì)象。
當(dāng)創(chuàng)建一個(gè)AnimationController時(shí)节猿,需要傳遞一個(gè)vsync參數(shù)票从,存在vsync時(shí)會(huì)防止屏幕外動(dòng)畫(huà)(譯者語(yǔ):動(dòng)畫(huà)的UI不在當(dāng)前屏幕時(shí))消耗不必要的資源。 通過(guò)將SingleTickerProviderStateMixin添加到類定義中滨嘱,可以將stateful對(duì)象作為vsync的值峰鄙。
CurvedAnimation
CurvedAnimation 將動(dòng)畫(huà)過(guò)程定義為一個(gè)非線性曲線. 有許多種系統(tǒng)預(yù)制的曲線, 可以通過(guò)Curves.
獲取。一般來(lái)說(shuō), 默認(rèn)的都是線性(Curves.linear)太雨。
Tween
默認(rèn)情況下吟榴,AnimationController對(duì)象的范圍從0.0到1.0。如果您需要不同的范圍或不同的數(shù)據(jù)類型躺彬,則可以使用Tween來(lái)配置動(dòng)畫(huà)以生成不同的范圍或數(shù)據(jù)類型的值煤墙。
例如,以下示例宪拥,Tween生成從-200.0到0.0的值:
final Tween doubleTween = Tween(begin: -200.0, end: 0.0);
Tween是一個(gè)無(wú)狀態(tài)(stateless)對(duì)象仿野,需要begin和end值。Tween的唯一職責(zé)就是定義從輸入范圍到輸出范圍的映射她君。輸入范圍通常為0.0到1.0脚作,但這不是必須的。
Tween繼承自Animatable<T>
缔刹,而不是繼承自Animation<T>
球涛。
Animatable與Animation相似,不是必須輸出double值校镐。例如亿扁,ColorTween指定兩種顏色之間的過(guò)渡。
final Tween colorTween = ColorTween(begin: Colors.transparent, end: Colors.black54);
Tween對(duì)象不存儲(chǔ)任何狀態(tài)鸟廓。相反从祝,它提供了evaluate(Animation animation)
方法將映射函數(shù)應(yīng)用于動(dòng)畫(huà)當(dāng)前值。 Animation對(duì)象的當(dāng)前值可以通過(guò)value()方法取到引谜。evaluate函數(shù)還執(zhí)行一些其它處理牍陌,例如分別確保在動(dòng)畫(huà)值為0.0和1.0時(shí)返回開(kāi)始和結(jié)束狀態(tài)。
Tween.animate 要使用Tween對(duì)象员咽,請(qǐng)調(diào)用其animate()方法毒涧,傳入一個(gè)控制器對(duì)象。
例如贝室,以下代碼在500毫秒內(nèi)生成從0到255的整數(shù)值契讲。
final AnimationController controller = AnimationController( duration: const Duration(milliseconds: 500), vsync: this);
Animation alpha = IntTween(begin: 0, end: 255).animate(controller);
注意animate()返回的是一個(gè)Animation仿吞,而不是一個(gè)Animatable。
監(jiān)聽(tīng)
一個(gè)Animation對(duì)象可以擁有Listeners和StatusListeners監(jiān)聽(tīng)器怀泊,可以用addListener()和addStatusListener()來(lái)添加茫藏。 只要?jiǎng)赢?huà)的值發(fā)生變化,就會(huì)調(diào)用監(jiān)聽(tīng)器霹琼。
一個(gè)Listener最常見(jiàn)的行為是調(diào)用setState()來(lái)觸發(fā)UI重建务傲。 上面的例子就用于實(shí)時(shí)更新animation.value的值。
..addListener(() {
// 用于實(shí)時(shí)更新_animation.value
setState(() {});
})
動(dòng)畫(huà)完成枣申、結(jié)束售葡、向前移動(dòng)或向后移動(dòng)(如AnimationStatus所定義)時(shí)會(huì)調(diào)用StatusListener。上面的例子就監(jiān)聽(tīng)了動(dòng)畫(huà)完成(AnimationStatus.completed
)和結(jié)束(AnimationStatus.dismissed
)的狀態(tài), 同時(shí)根據(jù)其狀態(tài)發(fā)出了Reverse和Forward命令忠藤。
..addStatusListener((status) {
if (status == AnimationStatus.completed) {
// 監(jiān)聽(tīng)動(dòng)畫(huà)完成的狀態(tài) The animation is stopped at the end
_controller.reverse();
}
if (status == AnimationStatus.dismissed) {
// 監(jiān)聽(tīng)動(dòng)畫(huà)結(jié)束的狀態(tài) The animation is stopped at the beginning
_controller.forward();
}
})
至此, 一個(gè)簡(jiǎn)單的動(dòng)畫(huà)過(guò)程就完成了挟伙。