什么是key
Key
能夠幫助開發(fā)者在 Widget tree
中保存狀態(tài)钩杰。
Flutter | 深入淺出Key 中使用 key
來解決widget交換的問題?
@immutable
abstract class Widget extends DiagnosticableTree {
const Widget({ this.key });
final Key key;
···
static bool canUpdate(Widget oldWidget, Widget newWidget) {
return oldWidget.runtimeType == newWidget.runtimeType
&& oldWidget.key == newWidget.key;
}
}
我們知道 Widget
只是一個配置且無法修改,而 Element
才是真正被使用的對象熟掂,并可以修改。
當新的 Widget
到來時將會調用 canUpdate
方法铝宵,來確定這個 Element
是否需要更新打掘。
canUpdate
對兩個(新老) Widget
的 runtimeType
和 key
進行比較华畏,從而判斷出當前的 Element
是否需要更新鹏秋。若 canUpdate
方法返回 true
說明不需要替換 Element
尊蚁,直接更新 Widget
就可以了。
StatelessWidget 內部中如何交換 Widget
只比較它們的 runtimeType
侣夷。這里 runtimeType
一致横朋,canUpdate
方法返回 true
,兩個 Widget 被交換了位置百拓,StatelessElement 調用新持有 Widget 的 build 方法重新構建琴锭,在屏幕上兩個 Widget 便被正確的交換了順序。
- 交換流程:
-
Row Widget
為它的子Widget
提供了一組有序的插槽衙传。對于每一個Widget决帖,F(xiàn)lutter
都會構建一個對應的Element
。構建的這個Element Tree
相當簡單蓖捶,僅保存有關每個Widget
類型的信息以及對子Widget
的引用地回。你可以將這個Element Tree
當做就像你的Flutter App
的骨架。它展示了App
的結構俊鱼,但其他信息需要通過引用原始Widget
來查找刻像。
交換色塊時,Flutter
遍歷Widget
樹并闲。它從Row Widget
開始细睡,然后移動到它的子 Widget
,Element
樹檢查 Widget
是否與舊Widget
是相同類型和 Key
帝火。
canUpdate()
它會更新對新 widget
的引用溜徙。這里,Widget
沒有設置Key
犀填,所以Flutter
只是檢查類型萌京。它對第二個孩子做同樣的事情。所以 Element
樹將根據(jù) Widget
樹進行對應的更新宏浩。
StatefulWidget 中如何交換
color
的定義放在了State
中知残,Widget
并不保存State
,真正 hold State
的引用的是 Stateful Element比庄。
(1) 交換控件的次序求妹,Flutter
將遍歷 Element
樹,檢查 Widget
樹中 Row
控件并且更新Element
樹中的引用佳窑,然后第一個 Tile
控件檢查它是相同類型制恍,說明不需要更新 Element
,Element
會根據(jù)當前 State
展示內容神凑。所以顏色沒有發(fā)生交換
StatefullWidget 結合 Key
添加 Key
之后的結構
(1) 當現(xiàn)在執(zhí)行 swap
時, Element
數(shù)中 StatafulWidget
控件除了比較類型外净神,還會比較 key
是否相等:
只有類型和 key
都匹配時何吝,才算找到對應的 Widget
。于是在 Widget Tree
發(fā)生交換后鹃唯,Element Tree
中子控件和原始控件對應關系就被打亂了爱榕,所以Flutter
會重建 Element Tree
,直到控件們正確對應上坡慌。
Element
位置被正確更新了
Where: 在哪設置 Key
如果把 Key
設置到內部會發(fā)生什么
@override
void initState() {
super.initState();
tiles = [
Padding(
padding: const EdgeInsets.all(8.0),
child: StatefulColorfulTile(key: UniqueKey()),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: StatefulColorfulTile(key: UniqueKey()),
),
];
}
當點擊按鈕發(fā)生交換之后黔酥,可以看到兩個色塊的顏色會隨機改變,但是我的預期是兩個固定的顏色彼此交換洪橘。
為什么
當Widget 樹中兩個 Padding 發(fā)生了交換跪者,它們包裹的色塊也就發(fā)生了交換:
然后 Flutter 將進行檢查,以便對 Element 樹進行對應的更新: Flutter 的 Elemetn to Widget 匹配算法將一次只檢查樹的一個層級:
在第一級熄求,Padding Widget 都正確匹配渣玲。
在第二級,Flutter
注意到 Tile 控件的
Key` 不匹配弟晚,就停用該 Tile Element忘衍,刪除 Widget 和 Element 之間的連接
解決問題的方法,將 Key
添加到 Padding
處
總結
-
canUpdate()
方法根據(jù)(新老)Widget
的runtimeType
和key
來更新 Widget && Element指巡。 -
LocalKey
的意思是: 當Widget
與Element
匹配時淑履,Flutter
只在樹中特定級別內查找匹配的Key
。因此Flutter
無法在同級中找到具有該Key
的Tile Widget
藻雪,所以它會創(chuàng)建一個新Element
并初始化一個新State
秘噪。 就是這個原因,造成色塊顏色發(fā)生隨機改變勉耀,每次交換相當于生成了兩個新的Widget
指煎。