You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter:解决多PopupMenuButton使用GlobalKey的冲突问题

解决Flutter多个PopupMenuButton悬停显示菜单的问题

问题原因

GlobalKey在Flutter Widget树中必须保持唯一,你之前共用一个GlobalKey绑定多个PopupMenuButton,导致框架报错。每个需要控制的PopupMenuButton都得有独立的GlobalKey。

解决方案1:独立GlobalKey + 通用触发方法

  1. 为每个需要悬停的PopupMenuButton创建单独的GlobalKey:
GlobalKey _agenturenMenuKey = GlobalKey();
GlobalKey _werbenMenuKey = GlobalKey();
  1. 修改触发方法,让它接收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();
  }
}
  1. 在每个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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 09:00:50