作者:森碼
鏈接:http://www.itdecent.cn/p/57debb89a24f
來源:簡書
文章來源于Flutter Widgets 101 Ep. 4,感興趣的同學(xué)可以直接看視頻,更便于理解。
首先,我們要明白Key的作用,Keys preserve state when widgets move around in your widget tree. They can be used to preserve the user's scroll location, or keeping state when modifying a collection.(當(dāng)組件在組件樹中移動時(shí)使用Key可以保持組件之前的狀態(tài),比如在用戶滑動時(shí)或者集合改變時(shí)),這是一個(gè)抽象的定義,大家盡量理解原文,在后面的例子中可以很好的理解。
什么時(shí)候使用Key?
我們參照視頻,寫下了如下Demo,當(dāng)點(diǎn)擊FloatingActionButton時(shí),交換兩個(gè)Widget的位置。(你可以下載項(xiàng)目,運(yùn)行一下,會更便于理解)

image.png
當(dāng)我們使用Stateless Widget時(shí),widget可以正常的移動。swap_color_1.dart
當(dāng)我們把Widget改為Stateful時(shí),交換出現(xiàn)了問題。swap_color_2.dart
當(dāng)我們?yōu)镾tateful加入Key之后,程序又能正常的執(zhí)行swap_color_3.dart
從Demo中我們可以看出,當(dāng)使用Stateless Widget時(shí),我們并不需要使用key,當(dāng)使用Stateful Widget時(shí),集合內(nèi)有數(shù)據(jù)移動和改變并且需要展示到界面時(shí)才需要key。
Key應(yīng)該用到哪?
首先我們來搞懂上面,為什么swap_color_2會出問題,要明白這個(gè)問題,我們需要明白Flutter的渲染策略。
我們在構(gòu)建Flutter的UI時(shí)是以Widget的形式『拼接』出來的,組件樹作為UI每一個(gè)組件都對應(yīng)一個(gè)元素(原文中是Slot),從而形成了『元素樹』(Element Tree),元素樹的內(nèi)容非常簡單,只包含了組件的類型和子元素的引用(Type),你可以把元素樹當(dāng)做Flutter App中的骨架(skeleton),它只展現(xiàn)了App的結(jié)構(gòu),并不包含其他具體的信息。
當(dāng)我們交換組件樹中的元素時(shí),組件確實(shí)進(jìn)行了交換,但是元素樹卻不一定。Flutter會先遍歷(walk)整個(gè)元素樹,從Row上的主元素,到主元素的子元素,查看整體的結(jié)構(gòu)是否發(fā)生了變化,當(dāng)然,它檢查的只能是元素的Type和Key,在給出的例子中,當(dāng)我們不設(shè)置Key時(shí),元素樹對比Type,發(fā)現(xiàn)Type并沒有發(fā)生變化,而Flutter卻是用元素樹和元素對應(yīng)的狀態(tài)(可用或者不可用),來決定這個(gè)元素是否應(yīng)該顯示出來,所以在界面中并沒有發(fā)生改變,但是當(dāng)我們加入Key之后,對比的對象多了一個(gè),并且是和之前不一樣的,F(xiàn)lutter察覺到之后,立即改變了元素的狀態(tài),讓它變?yōu)椤簾o用狀態(tài)』(deactivate),當(dāng)遍歷完之后,F(xiàn)lutter會瀏覽(look through)這些不匹配的元素(non-matched children)通過相應(yīng)的引用為之找到對應(yīng)的組件。當(dāng)所有的元素都匹配完成之后,F(xiàn)lutter會刷新界面,展現(xiàn)出我們預(yù)想的。

Widget Tree and Element Tree
那么Key到底應(yīng)該用到哪呢?
我們再來一個(gè)例子,swap_color_4,我們把色塊用Padding包裝一下。運(yùn)行之后會發(fā)現(xiàn),色塊并沒有交換,而是以隨機(jī)的形式在變換顏色。為什么呢?

Padding Stateful
結(jié)合我們上面的理論,我們分析一下這次的Widget Tree 和 Element Tree,當(dāng)我們交換元素后,F(xiàn)lutter element-to-widget matching algorithm,(元素-組件匹配算法),開始進(jìn)行對比,算法每次只對比一層,即Padding這一層。顯然,Padding并沒有發(fā)生本質(zhì)的變化。

Padding Widget Tree
于是開始進(jìn)行第二層對比,在對比時(shí)Flutter發(fā)現(xiàn)元素與組件的Key并不匹配,于是,把它設(shè)置成不可用狀態(tài),但是這里所使用的Key只是本地Key(Local Key),F(xiàn)lutter并不能找到另一層里面的Key(即另外一個(gè)Padding Widget中的Key)所以,F(xiàn)lutter就創(chuàng)建了一個(gè)新的Widget,而這個(gè)Widget的顏色就成了我們看到的『隨機(jī)色』。

Stateful Widget Tree
通過上面的示例,我們能明顯的看出,我們的Key要設(shè)置到組件樹的頂層,而這一層在改變時(shí),才能復(fù)用或者更新狀態(tài)。
用哪一種Key?
Flutter中有很多Key,但是總體分為兩種 Local Key和Global Key兩種。

Key UML
對于Key的研究還不是特別多,有時(shí)間再補(bǔ)一篇,上面的例子,因?yàn)闆]有數(shù)據(jù),所以使用了UniqueKey,在真實(shí)的開發(fā)中,我們可以用Model中的id作為ObjectKey。
GlobalKey其實(shí)是對應(yīng)于LocalKey,上面我們說Padding中的就是LocalKey,Global即可以在多個(gè)頁面或者層級復(fù)用,比如兩個(gè)頁面也可也同時(shí)保持一個(gè)狀態(tài)。
還處在學(xué)習(xí)Flutter狀態(tài),如有錯(cuò)誤請您及時(shí)指正,謝謝。
作者:森碼
鏈接:http://www.itdecent.cn/p/57debb89a24f
來源:簡書
著作權(quán)歸作者所有。商業(yè)轉(zhuǎn)載請聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請注明出處。