Flutter:解决多PopupMenuButton使用GlobalKey的冲突问题
问题原因
GlobalKey在Flutter Widget树中必须保持唯一,你之前共用一个GlobalKey绑定多个PopupMenuButton,导致框架报错。每个需要控制的PopupMenuButton都得有独立的GlobalKey。
解决方案1:独立GlobalKey + 通用触发方法
- 为每个需要悬停的PopupMenuButton创建单独的GlobalKey:
GlobalKey _agenturenMenuKey = GlobalKey(); GlobalKey _werbenMenuKey = GlobalKey();
- 修改触发方法,让它接收GlobalKey参数,动态控制对应菜单:
void _openPopupMenu(GlobalKey key) { GestureDetector? detector; void searchForGestureDetector(BuildContext element) { element.visitChildElements((child) { if (child.widget is GestureDetector) { detector = child.widget as GestureDetector; } else { searchForGestureDetector(child); } }); } final context = key.currentContext; if (context != null) { searchForGestureDetector(context); detector?.onTap?.call(); } }
- 在每个InkWell的onHover中调用对应Key的触发方法:
// Agenturen & Clubs 菜单 if (!Responsive.isMobile(context)) InkWell( onHover: (isHovering) { if (isHovering) _openPopupMenu(_agenturenMenuKey); }, onTap: () {}, child: PopupMenuButton( key: _agenturenMenuKey, tooltip: '', color: Color(0xFF262533), position: PopupMenuPosition.under, child: Text( 'Agenturen & Clubs', style: TextStyle( color: Colors.white, fontSize: 24, fontFamily: 'Poppins', ), ), itemBuilder: (BuildContext context) => <PopupMenuEntry>[ // 菜单项内容 ], ), ), // Werben 菜单 if (!Responsive.isMobile(context)) InkWell( onHover: (isHovering) { if (isHovering) _openPopupMenu(_werbenMenuKey); }, onTap: () {}, child: PopupMenuButton( key: _werbenMenuKey, tooltip: '', color: Color(0xFF262533), position: PopupMenuPosition.under, child: Text( 'Werben', style: TextStyle( color: Colors.white, fontSize: 24, fontFamily: 'Poppins', ), ), itemBuilder: (BuildContext context) => <PopupMenuEntry>[ // 菜单项内容 ], ), ),
解决方案2:封装自定义组件(更简洁)
把悬停逻辑和PopupMenuButton封装成独立组件,避免重复代码:
class HoverPopupMenuButton extends StatefulWidget { final Widget child; final List<PopupMenuEntry> Function(BuildContext) itemBuilder; final Color? color; final PopupMenuPosition? position; const HoverPopupMenuButton({ super.key, required this.child, required this.itemBuilder, this.color, this.position, }); @override State<HoverPopupMenuButton> createState() => _HoverPopupMenuButtonState(); } class _HoverPopupMenuButtonState extends State<HoverPopupMenuButton> { final GlobalKey _menuKey = GlobalKey(); void _openMenu() { GestureDetector? detector; void searchForGestureDetector(BuildContext element) { element.visitChildElements((child) { if (child.widget is GestureDetector) { detector = child.widget as GestureDetector; } else { searchForGestureDetector(child); } }); } final context = _menuKey.currentContext; if (context != null) { searchForGestureDetector(context); detector?.onTap?.call(); } } @override Widget build(BuildContext context) { return InkWell( onHover: (isHovering) { if (isHovering) _openMenu(); }, onTap: () {}, child: PopupMenuButton( key: _menuKey, tooltip: '', color: widget.color, position: widget.position, child: widget.child, itemBuilder: widget.itemBuilder, ), ); } }
使用时直接替换原来的InkWell+PopupMenuButton组合:
if (!Responsive.isMobile(context)) HoverPopupMenuButton( color: Color(0xFF262533), position: PopupMenuPosition.under, child: Text( 'Agenturen & Clubs', style: TextStyle( color: Colors.white, fontSize: 24, fontFamily: 'Poppins', ), ), itemBuilder: (BuildContext context) => <PopupMenuEntry>[ const PopupMenuItem(child: ListTile(title: Text('Escortagenturen', style: TextStyle(color: Colors.white)))), const PopupMenuItem(child: ListTile(title: Text('Bordelle', style: TextStyle(color: Colors.white)))), // 其他菜单项 ], ),
内容的提问来源于stack exchange,提问作者James James
相关产品推荐
相关产品推荐

