引言
key是控件構(gòu)造函數(shù)的一個(gè)可選參數(shù),key保存了控件的狀態(tài)义钉,我們可以利用key修改控件的狀態(tài)昧绣。
一個(gè)例子解釋何時(shí)使用key
其實(shí)大多數(shù)時(shí)候,我們是不需要關(guān)心key對(duì)捶闸,但是如果我們需要對(duì)處于某種狀態(tài)的相同類型的控件集合進(jìn)行添加夜畴,刪除或重新排序時(shí),我們就需要用到key了删壮。下面是一個(gè)何時(shí)需要key的簡(jiǎn)單例子贪绘。
上面例子展示的效果是,點(diǎn)擊右下角的切換央碟,可以對(duì)集合里的控件進(jìn)行位置切換税灌。
主頁面的代碼如下所示:
class _MyHomePageState extends State<MyHomePage> {
List<Widget> widgets;
@override
void initState() {
super.initState();
widgets = [
StatelessColorTile(),
StatelessColorTile(),
];
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Row(
children: widgets,
)),
floatingActionButton: FloatingActionButton(
onPressed: () {
changePosition();
},
child: Text('交換'),
),
);
}
changePosition() {
setState(() {
widgets.insert(1, widgets.removeAt(0));
});
}
}
StatelessColorTile控件
class StatelessColorTile extends StatelessWidget {
final Color color = RandomColor().randomColor();
@override
Widget build(BuildContext context) {
return Container(
width: 100,
height: 100,
color: color,
);
}
}
上面代碼中我們可以看到顏色方塊是一個(gè)無狀態(tài)的控件。
接下來我們將無狀態(tài)的StatelessColorTile改成有狀態(tài)的StatefulColorTile,如下所示:
class StatefulColorTile extends StatefulWidget {
StatefulColorTile({Key key}) : super(key: key);
@override
_StatefulColorTileState createState() => _StatefulColorTileState();
}
class _StatefulColorTileState extends State<StatefulColorTile> {
final Color color = RandomColor().randomColor();
@override
Widget build(BuildContext context) {
return Container(
width: 100,
height: 100,
color: color,
);
}
}
主頁面發(fā)生改變的代碼:
@override
void initState() {
super.initState();
widgets = [
StatefulColorContainer(),
StatefulColorContainer(),
];
}
改變成有狀態(tài)的方塊之后菱涤,我們點(diǎn)擊右下角的切換按鈕苞也,發(fā)現(xiàn)集合中的方塊位置不能進(jìn)行交換了,但是如果我們將有狀態(tài)的方塊添加一個(gè)key,集合中的方法位置就能進(jìn)行切換了粘秆,如下所示:
@override
void initState() {
super.initState();
widgets = [
StatefulColorTile(
key: UniqueKey(),
),
StatefulColorTile(
key: UniqueKey(),
),
];
}
針對(duì)上面的實(shí)例我們接下來可以用圖解的方式了解key的運(yùn)作方式墩朦。
1. 當(dāng)方塊為無狀態(tài)的控件時(shí)
在Flutter中,對(duì)于每個(gè)控件翻擒,flutter都會(huì)構(gòu)建一個(gè)相應(yīng)的元素,而這個(gè)元素只會(huì)保存每個(gè)控件的類型牛哺,以及對(duì)子元素的引用信息陋气。我們上面例子的控件樹和對(duì)應(yīng)的元素樹,如圖1所示引润,
圖2:交換Row中的方塊控件的順序巩趁;
圖3:flutter會(huì)遍歷元素樹以查看骨架結(jié)構(gòu)是否相同,它以Row元素開頭淳附,然后移動(dòng)到其子元素议慰,元素樹檢查新的小部件是否與舊的小部件類型和key相同(但在這種情況下,方塊widget沒有key奴曙,因此flutter只檢查其類型)别凹,因?yàn)樾屡f方塊控件的類型是相同,所以元素會(huì)更新對(duì)新控件的引用洽糟;
圖4:就會(huì)達(dá)到預(yù)期的切換效果(Row對(duì)第二個(gè)子元素的檢查也是如此)炉菲。
2. 當(dāng)方塊為有狀態(tài)但無key的控件時(shí)
圖5:當(dāng)方塊為有狀態(tài)但無key的控件時(shí),我們發(fā)現(xiàn)顏色值存儲(chǔ)在對(duì)應(yīng)控件的元素中坤溃,而不是控件本身中拍霜。
圖6:交換Row中的方塊控件的順序
圖7:交換兩個(gè)方塊控件后,F(xiàn)lutter會(huì)遍歷元素樹薪介,因?yàn)榉綁K控件沒有key祠饺,所以Flutter只會(huì)檢查其類型,因?yàn)轭愋拖嗤栽馗聦?duì)新控件對(duì)引用道偷,但是由于兩個(gè)方塊對(duì)顏色是存儲(chǔ)在元素中的,所以只是兩個(gè)方塊控件發(fā)生了變化烂完,而其對(duì)應(yīng)的顏色并不會(huì)發(fā)生變化试疙。
3. 當(dāng)方塊為有狀態(tài)且有key的控件時(shí)
圖8:當(dāng)方塊為有狀態(tài)且有key的控件時(shí),我們發(fā)現(xiàn)元素中除了存儲(chǔ)控件的顏色值抠蚣,還存儲(chǔ)了控件的key值祝旷。
圖9:交換Row中的方塊控件的順序
圖10:交換兩個(gè)方塊控件后,會(huì)像上面說的一樣匹配,但是我們發(fā)現(xiàn)元素中的key和相應(yīng)方塊控件的key不匹配.
圖11:當(dāng)發(fā)現(xiàn)元素中的key和相應(yīng)方塊控件的key不匹配后怀跛,F(xiàn)lutter會(huì)停用這些元素距贷。
圖12:Flutter會(huì)從第一個(gè)不匹配的方塊控件,從元素樹中查找具有相應(yīng)key的元素吻谋,找到之后會(huì)更新其對(duì)應(yīng)方塊控件的引用忠蝗。
圖13:匹配完第一個(gè)方塊控件后,F(xiàn)Lutter會(huì)為第二個(gè)方塊控件做同樣的事情漓拾。
圖14:最后阁最,F(xiàn)lutter將會(huì)顯示我們預(yù)期的內(nèi)容,兩個(gè)方塊交換位置并更新其顏色骇两。
總結(jié)
通過上面例子我們可以學(xué)習(xí)到速种,如果我們需要修改集合中的控件的順序或數(shù)量,則我們應(yīng)該使用key低千。
參考資料:
When to Use Keys-Flutter Widgets 101 Ep.4