轉載 原文鏈接:https://juejin.im/post/6863300824660082701
作者: fgyong
github:https://github.com/ifgyong
Key 是什么
用官方的說法就是:
key
是用來作為Widget
、Element
和SemanticsNode
的標示,僅僅用來更新widget->key
相同的小部件的狀態。
Key
子類包含LocalKey
和GlobalKey
。
LocalKey
看下LocalKey
的定義:
abstract class LocalKey extends Key {
const LocalKey() : super.empty();
}
LocalKey
定義了初始化函數,默認為值空。
LocalKey
子類包含ValueKey
/ObjectKey
/UniqueKey
,如圖所示:
ValueKey
ValueKey
顧名思義是比較的是值
看下關鍵函數
@override
bool operator ==(Object other) {
if (other.runtimeType != runtimeType)
return false;
return other is ValueKey<T>
&& other.value == value;
}
那么使用起來也是很簡單的,當我們想要系統根據我們所給的key
來判斷是否可以刷新時,可以使用該key
。
TextField(
key: ValueKey('value1'),
),
TextField(
key: ValueKey('value2'),
),
當我們來交換順序時,TextField
的值也交換了,也就是我們的key
帶走了值。
TextField(
key: ValueKey('value2'),
),
TextField(
key: ValueKey('value1'),
),
如果我們使用其他類來傳值呢?我們把類Student
作為value
傳值進去。
class Student {
final String name;
Student(this.name);
@override
int get hashCode => name.hashCode;
}
TextField(
key: ObjectKey(Student('老王')),
),
TextField(
key: ObjectKey(Student('老王')),
),
刷新之后并無報錯,使用正常。
當我們在Student
重寫了操作符==
之后再看下,我們將Student
代碼稍微改動下
class Student {
final String name;
Student(this.name);
@override
int get hashCode => name.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is Student &&
runtimeType == other.runtimeType &&
name == other.name;
}
然后hot reload
,結果報錯了
If multiple keyed nodes exist as children of another node, they must have unique keys.
剛才我們所改的Student
操作符==
導致了,在Key
對比Value
的時候重載了Student
的操作符,才導致的報錯,我們需要設置不同姓名的同學,來區分不同的同學。
ObjectKey
顧名思義是比較對象的key
,那么這個key
是如何比較對象呢?我們看下源碼;
@override
bool operator ==(Object other) {
if (other.runtimeType != runtimeType)
return false;
return other is ObjectKey
&& identical(other.value, value);
}
官方顯示比較類型,當類型不一致,判定為不是通過一個對象,如果另外一個也是ObjectKey
,則判斷地址是否相同,只有地址相同才判定為同一個對象。
測試數據;
class Student {
final String name;
Student(this.name);
@override
int get hashCode => name.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is Student &&
runtimeType == other.runtimeType &&
name == other.name;
}
TextField(
key: ObjectKey(Student('老王')),
),
TextField(
key: ObjectKey(Student('老王')),
),
刷新界面之后,并無報錯。
ObjectKey
稍微修改
_student = Student('老王');
TextField(
key: ObjectKey(_student),
),
TextField(
key: ObjectKey(_student),
),
刷新之后報錯了,存在了相同的key
。
If multiple keyed nodes exist as children of another node, they must have unique keys.
UniqueKey
每次生成不同的值,當我們每次刷新都需要一個新的值,那么正是這個存在的意義。
我們每次刷新就生成一個新的 顏色,并且漸隱漸顯效果。
AnimatedSwitcher(
duration: Duration(milliseconds: 1000),
child: Container(
key: UniqueKey(),
height: 100,
width: 100,
color: Colors.primaries[count % Colors.primaries.length],
),
)
效果:
GlobalKey & GlobalObjectKey
作為全局使用的key
,當跨小部件我們通常可以使用GlobalKey
來刷新其他小部件。
GlobalObjectKey
和ObjectKey
是否相等的判定條件是一致的,GlobalObjectKey
繼承GlobalKey
,通過GlobalKey<T extends State<StatefulWidget>>
來指定繼承state
,并實現StatefulWidget
接口的類,然后可以通過GlobalKey.currentState
來獲取當前state
,然后調用state.setState((){})
完成當前小部件標記為dirty
,在下一幀刷新當前小部件。
例子
點擊按鈕刷新小部件的背景顏色。
GlobalKey _key = GlobalKey();
_Container(_key),
OutlineButton(
child: Text('global key 刷新'),
onPressed: () {
_key.currentState.setState(() {});
},
點擊globalKey
刷新局部小部件,點擊右下角刷新整個頁面。可以看到局部刷新時,只有下邊的小部件改變顏色,整個頁面刷新時。
效果:
參考
- 例子代碼庫
- 官方源碼