JHHK

欢迎来到我的个人网站
行者常至 为者常成

10 key⭐️

目录

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');
}

行者常至,为者常成!





R
Valine - A simple comment system based on Leancloud.