本文翻譯自
原文地址:How to Choose Which Flutter Animation Widget is Right for You?
原作者:Andrew Fitz Gibbon
如果想要看這篇文章的視頻的話,點(diǎn)擊這里YouTube video
本文最初由Emily Fortuna撰寫,并已以她的名義發(fā)布。
好了,你已經(jīng)決定了在Flutter中使用動(dòng)畫--多么令人興奮。問題是,有很多不同的動(dòng)畫widget,想要找出用哪一個(gè)最合適也是令人不知多措的。幸運(yùn)的是,本文將會(huì)有所幫助。
你也可以在腦海里回顧自己有關(guān)于動(dòng)畫的考慮,我也將通過一系列問題幫助你決定如何創(chuàng)建動(dòng)畫。另一件需要記住的事是,F(xiàn)lutter核心庫(kù)中提供的動(dòng)畫相對(duì)來說更底層偏基礎(chǔ),這意味著如果你想要更復(fù)雜的動(dòng)畫,我建議你先在Pub.Dev上查找看看有沒有可用的動(dòng)畫庫(kù),這些庫(kù)提供更高級(jí)的封裝。
看一下下面的決策樹,我將在本文進(jìn)行解釋。

總的來說,你可能希望在你的Flutter APP中包含兩種主要的動(dòng)畫類型:基于繪制(drawing-based)的動(dòng)畫和基于代碼(code-based)的動(dòng)畫。
基于代碼的動(dòng)畫以Widget為關(guān)注點(diǎn),并植根于標(biāo)準(zhǔn)布局和樣式,比如rows, columns, colors, 或者 text styles,并不是說它們無聊或者簡(jiǎn)單,而是從本質(zhì)上講,它們傾向于改變現(xiàn)有存在Widget的外觀或過渡效果,而不是自己作為獨(dú)立小組件。
相反基于繪制的動(dòng)畫,看起來就像有人在繪制他們,他們經(jīng)常是獨(dú)立的Spirit,像游戲角色,或者涉及到通過代碼表達(dá)很難進(jìn)行轉(zhuǎn)換。
因此第一個(gè)問題是:"我的動(dòng)畫更像繪制,還是看起來可以通過Flutter的基礎(chǔ)組件進(jìn)行構(gòu)建?",如果你的動(dòng)畫更像是繪制,或者你正在和可以提供矢量或光柵圖表資源的設(shè)計(jì)團(tuán)隊(duì)一起工作,那么建議您使用三方工具(比如Rive 或 Lottie)以圖形的方式構(gòu)建動(dòng)畫,然后導(dǎo)出到Flutter,有幾個(gè)package可以幫助您將這些資源應(yīng)用到你的Flutter APP中。
另一方面,如果您的動(dòng)畫包只含Widget屬性轉(zhuǎn)變--比如改變顏色、形狀、位置--你可以只寫一些Flutter代碼就可以實(shí)現(xiàn)。
顯式還是隱式?
基于Flutter代碼的動(dòng)畫也有兩種類型:隱式和顯式動(dòng)畫,接下來是弄清你需要哪種類型。

當(dāng)值改變時(shí),隱式動(dòng)畫Widget會(huì)觸發(fā)動(dòng)畫
隱式動(dòng)畫只需要簡(jiǎn)單地為某些widget設(shè)置一個(gè)新值,F(xiàn)lutter就會(huì)將其從當(dāng)前值動(dòng)畫化為新值。這些widget很容易使用、功能強(qiáng)大。您在上圖中看到的所有動(dòng)畫都是通過隱式動(dòng)畫widget完成的,隱式動(dòng)畫是在制作動(dòng)畫時(shí)首先應(yīng)該考慮的。
顯式動(dòng)畫需要一個(gè)AnimationController,它們被稱之為”顯式“,是因?yàn)樗鼈儍H在明確要求開始時(shí)才開始動(dòng)畫,你能用顯式動(dòng)畫做到隱式動(dòng)畫能做的任何效果,并且能做更多的事情。代價(jià)就是你需要手動(dòng)的管理AnimationController的生命周期,因?yàn)樗皇且粋€(gè)Widget,因此意味著需要將其放到stateful widget中使用。也是由于這些原因,如果同樣的功能使用隱式動(dòng)畫Widget,開發(fā)起來代碼量會(huì)少更簡(jiǎn)單。
顯示還是隱式?有三個(gè)問題你需要問你自己,以確定你需要哪種類型的widget:"我的動(dòng)畫會(huì)永遠(yuǎn)重復(fù)嗎?",”永遠(yuǎn)“想表達(dá)的意思是,當(dāng)它在某個(gè)屏幕上的時(shí)候,或者只要判斷條件為真,比如音樂播放時(shí)。
第二個(gè)問題是:"動(dòng)畫中改變的值是否是不連續(xù)的?",舉一個(gè)我說的不連續(xù)的例子,如下圖中增長(zhǎng)的圓形動(dòng)畫,圓圈重復(fù)的從小變大、從小變大,它不會(huì)從小變大,然后再收縮,這種例子中,圓圈的大小是不連續(xù)的。

一個(gè)只會(huì)變大不會(huì)縮小的圓圈,這是一個(gè)不連續(xù)的動(dòng)畫
最后一個(gè)問題需要問你自己的是:"是否有多個(gè)Widget以協(xié)調(diào)的方式一起動(dòng)畫?",例如下圖中的多個(gè)盒子一起動(dòng)畫

如果您對(duì)這三個(gè)問題的任何一個(gè)回答”是“,那么你需要使用顯式Widget,否則您可以使用隱式Widget!一旦確定使用顯式還是隱式Widget,最后一個(gè)問題將會(huì)引導(dǎo)你找到民所需要的特定Widget。
使用哪一個(gè)Widget?
問問自己,"是否有滿足我需求的內(nèi)置Widget?",如果你想尋找內(nèi)置隱式動(dòng)畫Widget,請(qǐng)查找名為AnimatedFoo的Widget,”FOO“是要設(shè)置的動(dòng)畫屬性,例如AnimatedOpacity,此外還可以檢查AnimatedContainer,對(duì)于許多不同的隱式動(dòng)畫來說,它是一個(gè)非常強(qiáng)大和通用的小部件。
如果找不到你要要的內(nèi)置隱式動(dòng)畫,可以使用TweenAnimationBuilder創(chuàng)建一個(gè)自定義的隱式動(dòng)畫。相反如果你正在尋找內(nèi)置顯示動(dòng)畫,它們通常被稱為FooTransition,其中”FOO“是您想要設(shè)置的動(dòng)畫屬性,例如SlideTransition。
如果找不到相關(guān)的內(nèi)置顯示動(dòng)畫,則您需要問自己最后一個(gè)問題:"我希望我的動(dòng)畫稱為獨(dú)立的Widget,還是希望成為周圍Widget的一部分?",這個(gè)問題的答案主要取決于你的想法,如果你想要自定義獨(dú)立的顯示動(dòng)畫,你應(yīng)該繼承自AnimatedWidget,否則你可以使用AnimatedBuilder。
如果您關(guān)心性能問題,還有最后一個(gè)選擇需要考慮,那就是使用CustomPainter進(jìn)行動(dòng)畫。你可以像使用AnimatedWidget那樣使用它,不同的是CustomPainter是直接繪制在Canvas上,無需標(biāo)準(zhǔn)的Widget構(gòu)建范例。如果使用得當(dāng),你可以創(chuàng)建一些整潔、完全自定義的效果或者節(jié)省性能,如果錯(cuò)用,你的動(dòng)畫將會(huì)導(dǎo)致更多的性能問題。因此要小心,就像手動(dòng)內(nèi)存管理一樣,確保將共享指針散布到各處之前,你知道自己都在做什么。
結(jié)論
總之,可以問自己一系列高層次的問題,以指導(dǎo)你如何創(chuàng)建動(dòng)畫?;谶@些問題的順序創(chuàng)建了決策樹,來決定哪種Widget更適合你的需要。如果決策樹的終點(diǎn)折疊起來,它們將落成一條線,大約從左到右遞增的難度,感謝您閱讀本文,并繼續(xù)創(chuàng)建Flutter動(dòng)畫,無論是通過三方框架,package或者是顯式的隱式的動(dòng)畫。

系列文章: