轉(zhuǎn)載 原文鏈接:https://juejin.im/post/6863300824660082701
作者: fgyong
github:https://github.com/ifgyong
Key 是什么
用官方的說(shuō)法就是:
key
是用來(lái)作為Widget
构韵、Element
和SemanticsNode
的標(biāo)示,僅僅用來(lái)更新widget->key
相同的小部件的狀態(tài)蛙讥。
Key
子類(lèi)包含LocalKey
和GlobalKey
候醒。
LocalKey
看下LocalKey
的定義:
abstract class LocalKey extends Key {
const LocalKey() : super.empty();
}
LocalKey
定義了初始化函數(shù),默認(rèn)為值空哼绑。
LocalKey
子類(lèi)包含ValueKey
/ObjectKey
/UniqueKey
岩馍,如圖所示:
ValueKey
ValueKey
顧名思義是比較的是值
看下關(guān)鍵函數(shù)
@override
bool operator ==(Object other) {
if (other.runtimeType != runtimeType)
return false;
return other is ValueKey<T>
&& other.value == value;
}
那么使用起來(lái)也是很簡(jiǎn)單的,當(dāng)我們想要系統(tǒng)根據(jù)我們所給的key
來(lái)判斷是否可以刷新時(shí),可以使用該key
抖韩。
TextField(
key: ValueKey('value1'),
),
TextField(
key: ValueKey('value2'),
),
當(dāng)我們來(lái)交換順序時(shí),TextField
的值也交換了蛀恩,也就是我們的key
帶走了值。
TextField(
key: ValueKey('value2'),
),
TextField(
key: ValueKey('value1'),
),
如果我們使用其他類(lèi)來(lái)傳值呢茂浮?我們把類(lèi)Student
作為value
傳值進(jìn)去双谆。
class Student {
final String name;
Student(this.name);
@override
int get hashCode => name.hashCode;
}
TextField(
key: ObjectKey(Student('老王')),
),
TextField(
key: ObjectKey(Student('老王')),
),
刷新之后并無(wú)報(bào)錯(cuò),使用正常席揽。
當(dāng)我們?cè)?code>Student重寫(xiě)了操作符==
之后再看下,我們將Student
代碼稍微改動(dòng)下
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
,結(jié)果報(bào)錯(cuò)了
If multiple keyed nodes exist as children of another node, they must have unique keys.
剛才我們所改的Student
操作符==
導(dǎo)致了顽馋,在Key
對(duì)比Value
的時(shí)候重載了Student
的操作符,才導(dǎo)致的報(bào)錯(cuò)幌羞,我們需要設(shè)置不同姓名的同學(xué)寸谜,來(lái)區(qū)分不同的同學(xué)。
ObjectKey
顧名思義是比較對(duì)象的key
,那么這個(gè)key
是如何比較對(duì)象呢属桦?我們看下源碼;
@override
bool operator ==(Object other) {
if (other.runtimeType != runtimeType)
return false;
return other is ObjectKey
&& identical(other.value, value);
}
官方顯示比較類(lèi)型熊痴,當(dāng)類(lèi)型不一致,判定為不是通過(guò)一個(gè)對(duì)象地啰,如果另外一個(gè)也是ObjectKey
,則判斷地址是否相同愁拭,只有地址相同才判定為同一個(gè)對(duì)象。
測(cè)試數(shù)據(jù)亏吝;
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('老王')),
),
刷新界面之后岭埠,并無(wú)報(bào)錯(cuò)。
ObjectKey
稍微修改
_student = Student('老王');
TextField(
key: ObjectKey(_student),
),
TextField(
key: ObjectKey(_student),
),
刷新之后報(bào)錯(cuò)了蔚鸥,存在了相同的key
惜论。
If multiple keyed nodes exist as children of another node, they must have unique keys.
UniqueKey
每次生成不同的值,當(dāng)我們每次刷新都需要一個(gè)新的值止喷,那么正是這個(gè)存在的意義馆类。
我們每次刷新就生成一個(gè)新的 顏色,并且漸隱漸顯效果弹谁。
AnimatedSwitcher(
duration: Duration(milliseconds: 1000),
child: Container(
key: UniqueKey(),
height: 100,
width: 100,
color: Colors.primaries[count % Colors.primaries.length],
),
)
效果:
GlobalKey & GlobalObjectKey
作為全局使用的key
,當(dāng)跨小部件我們通城桑可以使用GlobalKey
來(lái)刷新其他小部件句喜。
GlobalObjectKey
和ObjectKey
是否相等的判定條件是一致的,GlobalObjectKey
繼承GlobalKey
,通過(guò)GlobalKey<T extends State<StatefulWidget>>
來(lái)指定繼承state
沟于,并實(shí)現(xiàn)StatefulWidget
接口的類(lèi)咳胃,然后可以通過(guò)GlobalKey.currentState
來(lái)獲取當(dāng)前state
,然后調(diào)用state.setState((){})
完成當(dāng)前小部件標(biāo)記為dirty
,在下一幀刷新當(dāng)前小部件旷太。
例子
點(diǎn)擊按鈕刷新小部件的背景顏色展懈。
GlobalKey _key = GlobalKey();
_Container(_key),
OutlineButton(
child: Text('global key 刷新'),
onPressed: () {
_key.currentState.setState(() {});
},
點(diǎn)擊globalKey
刷新局部小部件,點(diǎn)擊右下角刷新整個(gè)頁(yè)面供璧〈嫜拢可以看到局部刷新時(shí),只有下邊的小部件改變顏色睡毒,整個(gè)頁(yè)面刷新時(shí)来惧。
效果:
參考
- 例子代碼庫(kù)
- 官方源碼