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

如何在Flutter中创建标题指定宽度、选项同宽且可点击标题行打开的下拉菜单

实现点击标题行任意位置触发的等宽下拉菜单

核心实现思路

  • 借助MenuAnchor将标题行设为触发区域,通过手动控制菜单控制器,实现点击标题任意位置唤起/关闭菜单
  • 通过获取标题行的实际渲染宽度,将该宽度赋值给菜单容器,让菜单选项与标题/父容器保持等宽

完整代码示例

import 'package:flutter/material.dart';

class EqualWidthDropdown extends StatefulWidget {
  const EqualWidthDropdown({super.key});

  @override
  State<EqualWidthDropdown> createState() => _EqualWidthDropdownState();
}

class _EqualWidthDropdownState extends State<EqualWidthDropdown> {
  final GlobalKey _titleContainerKey = GlobalKey();
  double _titleWidth = 0;

  @override
  void initState() {
    super.initState();
    // 组件渲染完成后获取标题容器的实际宽度
    WidgetsBinding.instance.addPostFrameCallback((_) {
      final renderBox = _titleContainerKey.currentContext?.findRenderObject() as RenderBox?;
      if (renderBox != null) {
        setState(() {
          _titleWidth = renderBox.size.width;
        });
      }
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text("等宽下拉菜单示例")),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: MenuAnchor(
          menuChildren: [
            // 强制菜单容器宽度与标题一致
            SizedBox(
              width: _titleWidth,
              child: Column(
                mainAxisSize: MainAxisSize.min,
                children: [
                  ListTile(
                    title: const Text("选项1"),
                    onTap: () => Navigator.of(context).pop(),
                  ),
                  ListTile(
                    title: const Text("选项2"),
                    onTap: () => Navigator.of(context).pop(),
                  ),
                  ListTile(
                    title: const Text("选项3"),
                    onTap: () => Navigator.of(context).pop(),
                  ),
                ],
              ),
            ),
          ],
          builder: (context, controller, child) {
            return GestureDetector(
              onTap: () {
                controller.isOpen ? controller.close() : controller.open();
              },
              child: Container(
                key: _titleContainerKey,
                padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
                decoration: BoxDecoration(
                  border: Border.all(color: Colors.grey.shade300),
                  borderRadius: BorderRadius.circular(4),
                ),
                child: Row(
                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
                  children: const [
                    Text("点击任意位置打开菜单", style: TextStyle(fontSize: 16)),
                    Icon(Icons.arrow_drop_down),
                  ],
                ),
              ),
            );
          },
        ),
      ),
    );
  }
}

关键细节说明

  • 触发区域适配:用GestureDetector包裹标题容器,配合MenuAnchor的控制器手动切换菜单状态,确保标题行的每一处都能触发菜单
  • 宽度精准匹配:通过GlobalKey获取标题容器的渲染宽度,postFrameCallback确保组件完成布局后再获取宽度值,避免拿到0值
  • 菜单宽度控制:用SizedBox固定菜单容器宽度,内部的ListTile会自动填充该宽度,实现与标题完全等宽的效果

简化替代方案(无需GlobalKey)

如果希望直接基于父容器宽度设置菜单宽度,可使用LayoutBuilder获取约束:

MenuAnchor(
  menuChildren: [
    SizedBox(
      width: constraints.maxWidth,
      child: Column(
        mainAxisSize: MainAxisSize.min,
        children: [/* 菜单选项 */],
      ),
    ),
  ],
  builder: (context, controller, child) {
    return LayoutBuilder(
      builder: (context, constraints) {
        return GestureDetector(
          onTap: () => controller.isOpen ? controller.close() : controller.open(),
          child: Container(
            width: constraints.maxWidth,
            padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
            decoration: BoxDecoration(
              border: Border.all(color: Colors.grey.shade300),
              borderRadius: BorderRadius.circular(4),
            ),
            child: const Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: [
                Text("点击任意位置打开菜单", style: TextStyle(fontSize: 16)),
                Icon(Icons.arrow_drop_down),
              ],
            ),
          ),
        );
      },
    );
  },
)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 04:45:05