當Widget
在Widget Tree
中移動時,Key
會保留其狀態。它們可用於保留使用者的滾動位置等資訊。
Key的種類
Key
主要分為Local Keys
和Global Keys
;
Local Keys
- ValueKey : ValueKey('value')
- ObjectKey : ObjectKey(MutableRectangle(1,2,3,4))
- UniqueKey : UniqueKey()
- PageStorageKey : PageStorageKey(scrollLocation)
Global Keys
- GlobalKey : GlobalKey()
什麼時候使用Key
大多數情況下,我們並不需要使用Key
。但是如果你發現自己需要新增、刪除或者重新排序處於某種狀態的相同型別的Widget
集合就會用到Key
。
import 'package:flutter/material.dart';
class KeyDemoApp extends StatelessWidget{
@override
Widget build(BuildContext context) => MaterialApp(
home: _HomePage(),
);
}
class _HomePage extends StatefulWidget{
@override
State<StatefulWidget> createState() =>_HomePageState();
}
class _HomePageState extends State<_HomePage>{
List<Widget> _widgets;
@override
void initState() {
super.initState();
_widgets= [
RectStateless(Color.fromARGB(255, 0, 0, 0)),
RectStateless(Color.fromARGB(255, 255, 0, 0))
];
}
@override
Widget build(BuildContext context) => Scaffold(
body: Center(
child: Row(children: _widgets,),
),
floatingActionButton: FloatingActionButton(onPressed: swapWidget),
);
void swapWidget(){
setState(() {
_widgets.insert(1, _widgets.removeAt(0));
});
}
}
class RectStateless extends StatelessWidget{
final Color _bg;
RectStateless(this._bg,[Key key]):super(key:key);
@override
Widget build(BuildContext context) {
return Container(color:_bg,
width: 100,
height: 100,
);
}
}
複製程式碼
在上面的程式碼中,如果點選按鈕則兩個Widget
可以自由的更換位置。如果將兩個Widget
更換為StatefulWidget
的Widget
後:
class RectStateful extends StatefulWidget{
final Color _bg;
RectStateful(this._bg,[Key key]):super(key:key);
@override
State<StatefulWidget> createState() =>RectStatefulState(_bg);
}
class RectStatefulState extends State<RectStateful>{
final Color _bg;
RectStatefulState(this._bg);
@override
Widget build(BuildContext context) {
return Container(color:_bg,
width: 100,
height: 100,
);
}
}
複製程式碼
當點選時兩個色塊都不會交換,當新增上Key後則又可以交換位置:
_widgets= [
RectStateful(Color.fromARGB(255, 0, 0, 0),UniqueKey()),
RectStateful(Color.fromARGB(255, 255, 0, 0),UniqueKey())
];
複製程式碼
從上面的例子可以看出: 如果集合中的整個小部件子樹是無狀態的,則不需要使用Key。
Key的運作方式
在Stateless
的例子中,Row
為子Widget提供了一組有序的插槽。對於每個小部件,Flutter都會構建一個相應的Element
,Element tree
只會儲存每個Widget型別以及對子Element的引用資訊。可以將Element tree
視為Flutter應用程式的骨架,它顯示了應用程式的結構。當交換Row
中的Widget時,Flutter會遍歷Element tree
來檢視骨架結構是否相同,即檢查新的Widget是否和舊的Widget的型別和Key是否相同,如果是一樣的,它會更新對新Widget的引用,對於其他Widget也是對應的步驟。 在Stateless
的例子中,Widget
沒有Key
,Flutter只會檢查其型別。
在Stateful
沒有Key
例子中,每個Widget都會有一個State物件來儲存相應的資訊,這些資訊而不是儲存在小部件本身中。Flutter會檢查Row
小部件的型別,型別是一樣的會更新引用,第二個Widget也如此。Flutter會遍歷Element tree
以及其對應的State
來確定在裝置上顯示的實際內容。因此,看起來相應的Widget並沒有正確的交換。
在有Key
的Stateful
例子中,向小部件新增來key
的屬性。在交換Row
小部件時會像之前一樣匹配。但是新的Widget的金鑰和之前小部件Element
對應的Key不匹配。因此,Flutter會停用這些Element
,從第一個不匹配的Element
開始,然後Flutter會檢視不匹配的子項,查詢具有相同金鑰的Element,它會找到匹配項,並更新對相應Widget
的引用。然後Flutter會為後續的子項做相同的事情。