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

Flutter 实现点击PopupMenuButton选项后关闭当前并打开新菜单

实现方案

核心原理

Flutter 中 PopupMenuButton/弹出菜单本质是向导航栈压入了一个PopupRoute,调用Navigator.of(context).pop()即可关闭当前菜单。系统提供的showMenu方法返回值为Future,会在菜单完全关闭后触发回调,刚好可以用来实现「关闭当前菜单后再打开新菜单」的需求。

具体实现代码

推荐直接手动控制菜单的弹出逻辑,比默认PopupMenuButton组件灵活度更高:

1. 定义菜单类型枚举

// 区分不同层级的菜单
enum MenuType { normal, expanded }

2. 界面按钮部分

把右上角的入口按钮替换为普通IconButton,点击时触发第一级菜单弹出:

IconButton(
  icon: const Icon(Icons.more_vert),
  onPressed: () => _showCustomMenu(context, MenuType.normal),
)

3. 菜单弹出逻辑实现

Future<void> _showCustomMenu(BuildContext context, MenuType menuType) async {
  // 计算菜单弹出位置,和默认PopupMenuButton的弹出位置对齐
  final RenderBox buttonBox = context.findRenderObject() as RenderBox;
  final Offset buttonPos = buttonBox.localToGlobal(Offset.zero);
  final RelativeRect menuPosition = RelativeRect.fromLTRB(
    buttonPos.dx,
    buttonPos.dy + buttonBox.size.height,
    buttonPos.dx + buttonBox.size.width,
    buttonPos.dy,
  );

  // 根据当前菜单类型生成对应菜单项
  List<PopupMenuEntry> menuItems;
  if (menuType == MenuType.normal) {
    menuItems = [
      const PopupMenuItem(value: 'new_chat', child: Text('新建聊天')),
      const PopupMenuItem(value: 'new_group', child: Text('新建群组')),
      const PopupMenuItem(value: 'more', child: Text('更多')),
    ];
  } else {
    menuItems = [
      const PopupMenuItem(value: 'settings', child: Text('设置')),
      const PopupMenuItem(value: 'help', child: Text('帮助与反馈')),
      const PopupMenuItem(value: 'invite', child: Text('邀请好友')),
    ];
  }

  // 弹出菜单,等待菜单关闭后拿到选中值
  final selectedItem = await showMenu(
    context: context,
    position: menuPosition,
    items: menuItems,
    elevation: 8,
  );

  if (selectedItem == null) return;

  // 处理菜单项点击逻辑
  switch (selectedItem) {
    case 'new_chat':
      // 自行实现新建聊天逻辑
      break;
    case 'new_group':
      // 自行实现新建群组逻辑
      break;
    case 'more':
      // 选中「更多」选项,当前菜单已关闭,100ms延迟适配关闭动画后弹出新菜单
      Future.delayed(const Duration(milliseconds: 100), () {
        _showCustomMenu(context, MenuType.expanded);
      });
      break;
    case 'settings':
      // 自行实现设置跳转逻辑
      break;
    // 其他菜单项处理
  }
}

注意事项

  • 计算菜单位置时必须使用按钮本身的context,否则会出现弹出位置偏移的问题
  • 100ms的延迟刚好匹配PopupMenu默认的关闭动画时长,用户不会感知到明显卡顿,也可以根据自己的动画配置调整
  • 如果一定要用原生PopupMenuButton组件,直接在onSelected回调中处理「更多」的逻辑即可,该回调触发时框架已经自动关闭了当前菜单

内容的提问来源于stack exchange,提问作者genericUser

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.30 08:15:04