基本概念:GlobalKey 是 Flutter 中一个特殊的标识符,它可以全局唯一地标识一个 Widget,并允许你在应用的任何地方访问这个 Widget 的状态、位置和大小。类似于GPS。
主要的属性和方法
Dart
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
效果图

流程图
Dart
新建(空) → 赋值(绑定Widget) → 挂载(自动关联Element) → 渲染(创建RenderBox) → 获取(位置信息)
Dart
代码执行流程
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. 计算按钮位置
});
}
Dart
用户长按第3个列表项
↓
触发 onLongPress
↓
调用 _showDeleteButton('2')
↓
setState → _selectedId = '2' (立即高亮该项)
↓
addPostFrameCallback 等待布局完成
↓
通过 _itemKeys['2'] 获取 GlobalKey
↓
GlobalKey.currentContext 获取 BuildContext
↓
findRenderObject() 获取 RenderBox
↓
localToGlobal() 获取屏幕位置 (x=16, y=300)
↓
计算按钮位置: _buttonTop = 300 - 35 = 265
↓
setState → 显示删除按钮
↓
用户点击删除按钮
↓
调用 _deleteItem('2')
↓
_items.removeAt(2) (删除数据)
_itemKeys.remove('2') (清理 GlobalKey)
_selectedId = null (取消高亮)
_buttonTop = null (隐藏按钮)
↓
setState → 重建 UI,列表更新
难点

底层数据剖析
Dart
─────────────────────────────────────────────────
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 包含位置:x=0, y=229, width=300, height=50
4. 用户长按,执行你的代码:
↓
myKey.currentContext // → Element#123
↓
Element#123.findRenderObject() // → RenderBox#456
↓
RenderBox#456.localToGlobal() // → Offset(0, 229)
运行结果

代码实例
Dart
import 'package:flutter/material.dart';
class DemoPage extends StatefulWidget {
const DemoPage({super.key});
@override
State<DemoPage> createState() => _DemoPageState();
}
class _DemoPageState extends State<DemoPage> {
//变量
final Map<String, GlobalKey> _itemKeys = {}; //存储每个列表的GlobalKey
String? _selectedId; //当前被长按的项的ID
double? _buttonTop; //删除按钮距离屏幕顶部的距离
//数据
List<String> _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('删除'),
),
),
],
),
);
}
}