目录
LocalKey
1、说明
LocalKey(ValueKey)只在同一个父节点下生效, diff 范围:updateChildren() 的 兄弟节点数组。
查找成本:O(n),范围小
2、无 Key(index 驱动)
oldWidgets: [A, B, C]
newWidgets: [D, A, B, C]
Element 变化图
EA(StateA - A) → (StateA - D)
EB(StateB - B) → (StateB - A)
EC(StateC - C) → (StateC - B)
NewE(newState) → (newState - C)
❌ State 错位
3、有 ValueKey(key 驱动)
oldWidgets: [A, B, C]
newWidgets: [D, A, B, C]
按 key 建 Map
oldChildrenMap = {
'A' → Element0
'B' → Element1
'C' → Element2
}
遍历新列表
key = 'D'
map 中不存在
→ 创建新 Element
Element 变化图
NewE(newState) → (newState - D)
EA(StateA - A) → (StateA - A)
EB(StateB - B) → (StateB - B)
EC(StateC - C) → (StateC - C)
✅ State 正确
GlobalKey
GlobalKey 可以跨父节点匹配 Element。
GlobalKey 是“全局映射表”,Flutter 内部维护了一个类似这样的结构:
Map<GlobalKey, Element> globalKeyRegistry;
一、场景一:获取state
1、说明
GlobalKey在创建的时候可以指定一个State
final GlobalKey<_ChildPageState> _globalKey = GlobalKey();
创建widget时将key传入
ChildPage(key: _globalKey)
在任何地方我们都可以拿到state的数据了
_ChildPageState? state = _globalKey.currentState;
if (state != null) {
state.setState(() {
state.data = 'old:' + state.count.toString();
state.count++;
});
}
2、使用场景
在创建Widget树时,当需要更新的树(statefullWidget)位于叶子节点处时,存在一个问题statefullWidget的setState方法获取不到没办法进行局部更新,这个时候就可以使用GlobalKey
class GlobalKeyDemo extends StatelessWidget {
GlobalKeyDemo({Key? key}) : super(key: key);
final GlobalKey<_ChildPageState> _globalKey = GlobalKey();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('GlobalKeyDemo'),
),
body: ChildPage(key: _globalKey),
floatingActionButton: FloatingActionButton(
child: const Icon(Icons.add),
onPressed: () {
_ChildPageState? state = _globalKey.currentState;
if (state != null) {
state.setState(() {
state.data = 'old:' + state.count.toString();
state.count++;
});
}
},
),
);
}
}
class ChildPage extends StatefulWidget {
const ChildPage({Key? key}) : super(key: key);
@override
_ChildPageState createState() => _ChildPageState();
}
class _ChildPageState extends State<ChildPage> {
int count = 0;
String data = 'hello';
@override
Widget build(BuildContext context) {
return Center(
child: Column(
children: <Widget>[
Text(count.toString()),
Text(data),
],
),
);
}
}
二、场景二:获取widget的位置和大小
final GlobalKey boxKey = GlobalKey();
Widget build(BuildContext context) {
return Container(
key: boxKey,
width: 100,
height: 100,
color: Colors.red,
);
}
void getPosition() {
final renderBox =
boxKey.currentContext!.findRenderObject() as RenderBox;
final position = renderBox.localToGlobal(Offset.zero);
final size = renderBox.size;
print('position: $position, size: $size');
}
行者常至,为者常成!