基本概念GlobalKey是 Flutter 中一个特殊的标识符它可以全局唯一地标识一个 Widget并允许你在应用的任何地方访问这个 Widget 的状态、位置和大小。类似于GPS。主要的属性和方法GlobalKey _key GlobalKey(); // currentContext - 获取该 Widget 的 BuildContext BuildContext? context _key.currentContext; // currentState - 获取该 Widget 的 State仅 StatefulWidget State? state _key.currentState; // currentWidget - 获取该 Widget 实例 Widget? widget _key.currentWidget; // findRenderObject() - 获取 RenderObject RenderObject? renderObject _key.currentContext?.findRenderObject();通过一个长按唤醒删除按钮的案例来学习GlobalKey效果图流程图新建(空) → 赋值(绑定Widget) → 挂载(自动关联Element) → 渲染(创建RenderBox) → 获取(位置信息)代码执行流程 1.初始化时创建GlobalKey for (var i 0; i _items.length; i) { _itemKeys[i.toString()] GlobalKey(); // 创建20个GlobalKey存起来 } 2.构建时绑定GlobalKey Container( key: _itemKeys[id], // ← 把GlobalKey绑定到Container上 ) 3.长按时通过GlobalKey获取位置 void _showDeleteButton(String id) { setState(() _selectedId id); // 1. 高亮当前项 WidgetsBinding.instance.addPostFrameCallback((_) { // 2. 等布局完成 final key _itemKeys[id]; // 3. 取出GlobalKey final box key!.currentContext!.findRenderObject() as RenderBox; // 4. 找到位置 final position box.localToGlobal(Offset.zero); // 5. 获取坐标 setState(() _buttonTop position.dy - 35); // 6. 计算按钮位置 }); }用户长按第3个列表项 ↓ 触发 onLongPress ↓ 调用 _showDeleteButton(2) ↓ setState → _selectedId 2 (立即高亮该项) ↓ addPostFrameCallback 等待布局完成 ↓ 通过 _itemKeys[2] 获取 GlobalKey ↓ GlobalKey.currentContext 获取 BuildContext ↓ findRenderObject() 获取 RenderBox ↓ localToGlobal() 获取屏幕位置 (x16, y300) ↓ 计算按钮位置: _buttonTop 300 - 35 265 ↓ setState → 显示删除按钮 ↓ 用户点击删除按钮 ↓ 调用 _deleteItem(2) ↓ _items.removeAt(2) (删除数据) _itemKeys.remove(2) (清理 GlobalKey) _selectedId null (取消高亮) _buttonTop null (隐藏按钮) ↓ setState → 重建 UI列表更新难点底层数据剖析───────────────────────────────────────────────── 1. GlobalKey myKey GlobalKey() ↓ Flutter 内部创建空壳_element null 2. Container(key: myKey) ↓ Flutter 内部 ├─ 创建 Element#123 (Element是Flutter中连接Widget(配置信息)和RenderObject(渲染对象的桥梁 ├─ Element#123._widget Container (将 Container 这个 Widget 配置信息存储到 Element#123 这个对象的 _widget 变量中) ├─ myKey._element Element#123 (将 Element 对象存储到 GlobalKey 的内部变量中) └─ _registry[myKey] Element#123 (将 GlobalKey 和 Element 的关联关系存储到全局注册表中) 3. Element#123 挂载 RenderObject ↓ Element#123._renderObject RenderBox#456 RenderBox#456 包含位置x0, y229, width300, height50 4. 用户长按执行你的代码 ↓ myKey.currentContext // → Element#123 ↓ Element#123.findRenderObject() // → RenderBox#456 ↓ RenderBox#456.localToGlobal() // → Offset(0, 229)运行结果代码实例import package:flutter/material.dart; class DemoPage extends StatefulWidget { const DemoPage({super.key}); override StateDemoPage createState() _DemoPageState(); } class _DemoPageState extends StateDemoPage { //变量 final MapString, GlobalKey _itemKeys {}; //存储每个列表的GlobalKey String? _selectedId; //当前被长按的项的ID double? _buttonTop; //删除按钮距离屏幕顶部的距离 //数据 ListString _items List.generate(20, (i) 任务 ${i 1}); override void initState() { super.initState(); //为每个列表项创建唯一的GlobalKey for (var i 0; i _items.length; i) { _itemKeys[i.toString()] GlobalKey(); } } //显示删除列表 void _showDeleteButton(String id) { //高亮被选中的项 setState(() _selectedId id); print(当前被选中的项的下标为:$id); //等待布局完成后再计算位置 WidgetsBinding.instance.addPostFrameCallback((_) { //addPostFrameCallback是一个在帧绘制之后执行的回调函数 //获取该子项的GlobalKey final key _itemKeys[id]; print(该子项的GlobalKey为$key); //检查Key是否有效 if (key?.currentContext ! null) { print(该子项的key为${key!.currentContext}); //获取RenderBox包含位置和大小信息 final box key!.currentContext!.findRenderObject() as RenderBox; print(获取到的RenderBox信息为:$box); //获取该Widget在屏幕上的位置 final position box.localToGlobal(Offset.zero); //计算删除按钮的位置 setState( () _buttonTop position.dy - 35 //这里可以根据自己的需要调试按钮位置 ); print(该子项删除按钮的子项为:$_buttonTop); } }); } //删除逻辑 void _deleteItem(String id) { setState(() { _items.removeAt(int.parse(id)); //从数据源删除 _itemKeys.remove(id);//清理对应的GlobalKey _selectedId null;//清除选中状态 _buttonTop null;//隐藏删除按钮 }); } void _cancelDelete() { setState(() { _selectedId null; _buttonTop null; }); } override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text(长按删除示例)), body: Stack( children: [ ListView.builder( itemCount: _items.length, itemBuilder: (ctx, i) { final id i.toString(); return GestureDetector( onTap: _cancelDelete, //点击其他地方取消 child: Container( key: _itemKeys[id], //绑定GlobalKey赋给 Widget 的 key 属性 color: _selectedId id ? Colors.blue.withOpacity(0.1) : null,//高亮被选中的项 child: ListTile( title: Text(_items[i]), onLongPress: () _showDeleteButton(id), //长按触发删除按钮 ), ), ); }, ), //悬浮按钮 if (_selectedId ! null _buttonTop ! null) Positioned( left: 50, right: 50, top: _buttonTop!, child: ElevatedButton( onPressed: () _deleteItem(_selectedId!), style: ElevatedButton.styleFrom( backgroundColor: Colors.red, foregroundColor: Colors.white, ), child: Text(删除), ), ), ], ), ); } }
Flutter---GlobalKey
发布时间:2026/6/10 8:12:11
基本概念GlobalKey是 Flutter 中一个特殊的标识符它可以全局唯一地标识一个 Widget并允许你在应用的任何地方访问这个 Widget 的状态、位置和大小。类似于GPS。主要的属性和方法GlobalKey _key GlobalKey(); // currentContext - 获取该 Widget 的 BuildContext BuildContext? context _key.currentContext; // currentState - 获取该 Widget 的 State仅 StatefulWidget State? state _key.currentState; // currentWidget - 获取该 Widget 实例 Widget? widget _key.currentWidget; // findRenderObject() - 获取 RenderObject RenderObject? renderObject _key.currentContext?.findRenderObject();通过一个长按唤醒删除按钮的案例来学习GlobalKey效果图流程图新建(空) → 赋值(绑定Widget) → 挂载(自动关联Element) → 渲染(创建RenderBox) → 获取(位置信息)代码执行流程 1.初始化时创建GlobalKey for (var i 0; i _items.length; i) { _itemKeys[i.toString()] GlobalKey(); // 创建20个GlobalKey存起来 } 2.构建时绑定GlobalKey Container( key: _itemKeys[id], // ← 把GlobalKey绑定到Container上 ) 3.长按时通过GlobalKey获取位置 void _showDeleteButton(String id) { setState(() _selectedId id); // 1. 高亮当前项 WidgetsBinding.instance.addPostFrameCallback((_) { // 2. 等布局完成 final key _itemKeys[id]; // 3. 取出GlobalKey final box key!.currentContext!.findRenderObject() as RenderBox; // 4. 找到位置 final position box.localToGlobal(Offset.zero); // 5. 获取坐标 setState(() _buttonTop position.dy - 35); // 6. 计算按钮位置 }); }用户长按第3个列表项 ↓ 触发 onLongPress ↓ 调用 _showDeleteButton(2) ↓ setState → _selectedId 2 (立即高亮该项) ↓ addPostFrameCallback 等待布局完成 ↓ 通过 _itemKeys[2] 获取 GlobalKey ↓ GlobalKey.currentContext 获取 BuildContext ↓ findRenderObject() 获取 RenderBox ↓ localToGlobal() 获取屏幕位置 (x16, y300) ↓ 计算按钮位置: _buttonTop 300 - 35 265 ↓ setState → 显示删除按钮 ↓ 用户点击删除按钮 ↓ 调用 _deleteItem(2) ↓ _items.removeAt(2) (删除数据) _itemKeys.remove(2) (清理 GlobalKey) _selectedId null (取消高亮) _buttonTop null (隐藏按钮) ↓ setState → 重建 UI列表更新难点底层数据剖析───────────────────────────────────────────────── 1. GlobalKey myKey GlobalKey() ↓ Flutter 内部创建空壳_element null 2. Container(key: myKey) ↓ Flutter 内部 ├─ 创建 Element#123 (Element是Flutter中连接Widget(配置信息)和RenderObject(渲染对象的桥梁 ├─ Element#123._widget Container (将 Container 这个 Widget 配置信息存储到 Element#123 这个对象的 _widget 变量中) ├─ myKey._element Element#123 (将 Element 对象存储到 GlobalKey 的内部变量中) └─ _registry[myKey] Element#123 (将 GlobalKey 和 Element 的关联关系存储到全局注册表中) 3. Element#123 挂载 RenderObject ↓ Element#123._renderObject RenderBox#456 RenderBox#456 包含位置x0, y229, width300, height50 4. 用户长按执行你的代码 ↓ myKey.currentContext // → Element#123 ↓ Element#123.findRenderObject() // → RenderBox#456 ↓ RenderBox#456.localToGlobal() // → Offset(0, 229)运行结果代码实例import package:flutter/material.dart; class DemoPage extends StatefulWidget { const DemoPage({super.key}); override StateDemoPage createState() _DemoPageState(); } class _DemoPageState extends StateDemoPage { //变量 final MapString, GlobalKey _itemKeys {}; //存储每个列表的GlobalKey String? _selectedId; //当前被长按的项的ID double? _buttonTop; //删除按钮距离屏幕顶部的距离 //数据 ListString _items List.generate(20, (i) 任务 ${i 1}); override void initState() { super.initState(); //为每个列表项创建唯一的GlobalKey for (var i 0; i _items.length; i) { _itemKeys[i.toString()] GlobalKey(); } } //显示删除列表 void _showDeleteButton(String id) { //高亮被选中的项 setState(() _selectedId id); print(当前被选中的项的下标为:$id); //等待布局完成后再计算位置 WidgetsBinding.instance.addPostFrameCallback((_) { //addPostFrameCallback是一个在帧绘制之后执行的回调函数 //获取该子项的GlobalKey final key _itemKeys[id]; print(该子项的GlobalKey为$key); //检查Key是否有效 if (key?.currentContext ! null) { print(该子项的key为${key!.currentContext}); //获取RenderBox包含位置和大小信息 final box key!.currentContext!.findRenderObject() as RenderBox; print(获取到的RenderBox信息为:$box); //获取该Widget在屏幕上的位置 final position box.localToGlobal(Offset.zero); //计算删除按钮的位置 setState( () _buttonTop position.dy - 35 //这里可以根据自己的需要调试按钮位置 ); print(该子项删除按钮的子项为:$_buttonTop); } }); } //删除逻辑 void _deleteItem(String id) { setState(() { _items.removeAt(int.parse(id)); //从数据源删除 _itemKeys.remove(id);//清理对应的GlobalKey _selectedId null;//清除选中状态 _buttonTop null;//隐藏删除按钮 }); } void _cancelDelete() { setState(() { _selectedId null; _buttonTop null; }); } override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text(长按删除示例)), body: Stack( children: [ ListView.builder( itemCount: _items.length, itemBuilder: (ctx, i) { final id i.toString(); return GestureDetector( onTap: _cancelDelete, //点击其他地方取消 child: Container( key: _itemKeys[id], //绑定GlobalKey赋给 Widget 的 key 属性 color: _selectedId id ? Colors.blue.withOpacity(0.1) : null,//高亮被选中的项 child: ListTile( title: Text(_items[i]), onLongPress: () _showDeleteButton(id), //长按触发删除按钮 ), ), ); }, ), //悬浮按钮 if (_selectedId ! null _buttonTop ! null) Positioned( left: 50, right: 50, top: _buttonTop!, child: ElevatedButton( onPressed: () _deleteItem(_selectedId!), style: ElevatedButton.styleFrom( backgroundColor: Colors.red, foregroundColor: Colors.white, ), child: Text(删除), ), ), ], ), ); } }