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

Flutter中如何为多个PopupMenuButton正确使用Global Key?

解决PopupMenuButton悬停弹出及GlobalKey复用问题

问题说明

实现鼠标悬停PopupMenuButton时自动弹出菜单,单个按钮用GlobalKey可以实现,但复用同一个GlobalKey会报错——一个GlobalKey只能绑定一个Widget。

解决思路

1. 给每个PopupMenuButton分配独立GlobalKey

不能复用同一个GlobalKey,每个需要悬停触发的PopupMenuButton都要创建专属的GlobalKey。

2. 重构触发方法,支持传入对应GlobalKey

修改openPopUpItem方法,让它接收一个GlobalKey参数,这样每个InkWell的onHover逻辑可以针对对应按钮触发。

3. 封装自定义组件(推荐)

把悬停逻辑和PopupMenuButton封装成自定义组件,避免重复代码,后续直接复用组件即可。

代码实现

方案1:独立GlobalKey+重构触发方法

// 为每个需要的按钮创建独立GlobalKey
GlobalKey popUpButtonKeyAgenturen = GlobalKey();
GlobalKey popUpButtonKeyWerben = GlobalKey();

// 重构openPopUpItem,接收GlobalKey参数
void openPopUpItem(GlobalKey key) {
  GestureDetector? detector;
  void searchForGestureDetector(BuildContext element) {
    element.visitChildElements((element) {
      if (element.widget is GestureDetector) {
        detector = element.widget as GestureDetector;
      } else {
        searchForGestureDetector(element);
      }
    });
  }

  final currentContext = key.currentContext;
  if (currentContext != null) {
    searchForGestureDetector(currentContext);
    detector?.onTap?.call();
  }
}

// 使用时传入对应GlobalKey
body: ListView(
  children: [
    // ... 其他代码
    if (!Responsive.isMobile(context))
      InkWell(
        onHover: (value) {
          if (value) openPopUpItem(popUpButtonKeyAgenturen);
        },
        onTap: () {},
        child: PopupMenuButton(
          key: popUpButtonKeyAgenturen,
          tooltip: '',
          color: Color(0xFF262533),
          position: PopupMenuPosition.under,
          child: Text(
            'Agenturen & Clubs',
            style: TextStyle(
              color: Colors.white,
              fontSize: 24,
              fontFamily: 'Poppins',
            ),
          ),
          itemBuilder: (BuildContext context) => <PopupMenuEntry>[
            // ... 菜单项
          ],
        ),
      ),
    // ... 其他代码
    if (!Responsive.isMobile(context))
      InkWell(
        onHover: (value) {
          if (value) openPopUpItem(popUpButtonKeyWerben);
        },
        onTap: () {},
        child: PopupMenuButton(
          key: popUpButtonKeyWerben,
          tooltip: '',
          color: Color(0xFF262533),
          position: PopupMenuPosition.under,
          child: Text(
            'Werben',
            style: TextStyle(
              color: Colors.white,
              fontSize: 24,
              fontFamily: 'Poppins',
            ),
          ),
          itemBuilder: (BuildContext context) => <PopupMenuEntry>[
            // ... 菜单项
          ],
        ),
      ),
  ],
)

方案2:自定义HoverPopupMenuButton组件(推荐)

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 _popupKey = GlobalKey();

  void _openMenu() {
    GestureDetector? detector;
    void searchDetector(BuildContext element) {
      element.visitChildElements((element) {
        if (element.widget is GestureDetector) {
          detector = element.widget as GestureDetector;
        } else {
          searchDetector(element);
        }
      });
    }

    final context = _popupKey.currentContext;
    if (context != null) {
      searchDetector(context);
      detector?.onTap?.call();
    }
  }

  @override
  Widget build(BuildContext context) {
    return InkWell(
      onHover: (isHovering) {
        if (isHovering) _openMenu();
      },
      onTap: () {},
      child: PopupMenuButton(
        key: _popupKey,
        tooltip: '',
        color: widget.color,
        position: widget.position,
        child: widget.child,
        itemBuilder: widget.itemBuilder,
      ),
    );
  }
}

使用自定义组件

body: ListView(
  children: [
    // ... 其他代码
    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)))),
          // ... 其他菜单项
        ],
      ),
    // ... 其他代码
    if (!Responsive.isMobile(context))
      HoverPopupMenuButton(
        color: Color(0xFF262533),
        position: PopupMenuPosition.under,
        child: Text(
          'Werben',
          style: TextStyle(
            color: Colors.white,
            fontSize: 24,
            fontFamily: 'Poppins',
          ),
        ),
        itemBuilder: (BuildContext context) => <PopupMenuEntry>[
          const PopupMenuItem(child: ListTile(title: Text('Werbenformate', style: TextStyle(color: Colors.white)))),
          const PopupMenuItem(child: ListTile(title: Text('Preise', 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:45:35