- 前端
- 跨平台
- 桌面应用
- 移动开发
【免费下载链接】flet
Build realtime web, mobile and desktop apps in Python only. No frontend experience required.
flet.MenuStyle是 Flet 中专门用于定义菜单视觉样式的数据类,负责控制MenuBar级联菜单、SubmenuButton子菜单以及Dropdown下拉菜单的背景、阴影、边框、形状、尺寸与鼠标光标等全部外观细节。本文以该类的源码 docstring 为依据,逐项讲解 12 个属性及其默认行为,并结合控件使用方式与 Flutter 端解析实现(parseMenuStyle)给出可运行的实战示例,帮助你构建从简单到高度定制的菜单系统。
MenuStyle 是什么:一个用于描述菜单外观的值对象
MenuStyle是一个被@value装饰器标记的不可变值对象(value class),定义于 menu_bar.py,其类 docstring 只有一句话:"Defines the visual style/appearance of a menu."(定义菜单的视觉样式/外观)。
它不是独立控件,不能添加到页面布局中,而是作为样式参数注入到需要使用菜单外观的控件或主题上。从当前仓库源码可以确认,它出现在以下四处:
| 使用位置 | 所在源码 | 作用 |
|---|---|---|
MenuBar.style | menu_bar.py | 菜单栏自身的样式 |
SubmenuButton.menu_style | submenu_button.py | 点击按钮后弹出的级联子菜单样式 |
Dropdown.menu_style | dropdown.py | 下拉菜单弹出面板样式 |
DropdownTheme.menu_style | theme.py | 全局覆盖所有Dropdown的默认菜单样式 |
也就是说,凡是 Flet 中"点击后弹出菜单面板"的控件,几乎都通过MenuStyle来统一配置这块面板的视觉表现。一个最基础的使用方式如下:
import flet as ft def main(page: ft.Page): page.add( ft.MenuBar( controls=[ ft.SubmenuButton( content=ft.Text("文件"), menu_style=ft.MenuStyle( bgcolor=ft.Colors.SURFACE_CONTAINER_HIGH, elevation=4, side=ft.BorderSide(1, ft.Colors.OUTLINE), shape=ft.RoundedRectangleBorder(radius=8), ), controls=[ ft.MenuItemButton(content=ft.Text("打开")), ft.MenuItemButton(content=ft.Text("保存")), ], ) ] ) ) ft.app(main)12 个属性逐项详解
以下内容完整对应 menu_bar.py 中MenuStyle的字段定义及其 docstring 说明。
alignment:子菜单相对按钮的对齐方式
alignment: Optional[Alignment] = None决定子菜单打开时相对于打开它的按钮的期望对齐位置(例如ft.Alignment.BOTTOM_LEFT)。源码 docstring 特别说明了两个重要的自动补偿行为:
- 若按给定对齐方式没有足够空间,而按钮另一侧有空间,则对齐方向会自动交换为相反方向(如
1变为-1),菜单会尝试出现在按钮另一侧; - 若两侧都不够,菜单会被尽量推挤以显示自身尽可能多的部分,甚至可能覆盖父按钮。
因此该属性给出的是"期望"而非"强制"对齐,Flet 会基于屏幕空间自动兜底。
bgcolor:菜单背景填充色
bgcolor: Optional[ControlStateValue[ColorValue]] = None菜单面板的背景颜色,支持状态感知值(ControlStateValue),例如悬停时切换背景色。未设置时使用主题默认背景。
shadow_color:菜单阴影颜色
shadow_color: Optional[ControlStateValue[ColorValue]] = NoneMaterial 的 elevation 阴影在深色主题下往往难以看清,因此 docstring 明确说明:默认情况下菜单类会额外添加一层半透明覆盖层来表示高度。设置该属性可以自定义这层阴影的颜色,以便在深色背景下仍能区分菜单层级。
elevation:菜单高度(阴影大小)
elevation: Optional[ControlStateValue[Optional[Number]]] = None菜单的 elevation,即菜单下方阴影的大小,直观表现为"浮起"程度。数值越大,菜单与背景的层次感越强。同样支持状态感知值。
padding:菜单边界与子内容之间的内边距
padding: Optional[ControlStateValue[PaddingValue]] = None菜单面板边界到其内部子控件之间的留白,例如ft.Padding(8, 4, 8, 4)。增大该值会让菜单项四周更"透气"。
side:菜单轮廓(边框)
side: Optional[ControlStateValue[BorderSide]] = None菜单外轮廓的颜色和粗细,例如ft.BorderSide(1, ft.Colors.OUTLINE)。docstring 指出,该值会与shape组合生成一个带描边的形状。从 Dart 端实现看,parseMenuStyle在解析side时使用Colors.black作为默认描边颜色(见 menu.dart)。
shape:菜单形状
shape: Optional[ControlStateValue[OutlinedBorder]] = None菜单的形状,例如ft.RoundedRectangleBorder(radius=8)实现圆角。与side组合后得到"带描边的圆角矩形"等外观。形状与描边分别独立配置,便于自由组合。
mouse_cursor:悬停鼠标光标
mouse_cursor: Optional[ControlStateValue[MouseCursor]] = None鼠标指针进入或悬停在菜单上时显示的光标,如ft.MouseCursor.CLICK、ft.MouseCursor.HAND。用于向用户提示菜单的可交互性。
fixed_size:菜单固定尺寸
fixed_size: Optional[ControlStateValue[Size]] = None菜单的固定尺寸。docstring 给出三条关键约束:
- 该尺寸仍受
min_size和max_size约束,三者同时生效时以约束为准; - 维度值为
float('inf')(无限大)时该维度被忽略; - 固定宽度 + 默认高度用
Size.from_width(320),固定高度 + 默认宽度用Size.from_height(100)。
ft.MenuStyle( fixed_size=ft.Size.from_width(320), # 固定宽度 320,高度自适应 )max_size:菜单最大尺寸
max_size: Optional[ControlStateValue[Size]] = None菜单自身的最大尺寸。ft.Size.infinite()或None表示不约束最大尺寸。该值必须大于或等于min_size。
min_size:菜单最小尺寸
min_size: Optional[ControlStateValue[Size]] = None菜单自身的最小尺寸,必须小于或等于max_size。配合max_size可限定菜单面板在一个尺寸区间内浮动。
visual_density:布局紧凑程度
visual_density: Optional[VisualDensity] = None定义菜单布局的紧凑程度(如ft.VisualDensity.COMPACT更紧凑、ft.VisualDensity.COMFORTABLE更宽松),影响菜单项内部的间距与控件密度,用于在有限空间内展示更多菜单项。
状态感知样式:一份样式响应多种交互状态
MenuStyle的大部分颜色、边框、尺寸类属性类型都是ControlStateValue,这意味着同一个属性可以针对不同控件状态给出不同值。状态枚举来自 control_state.py 中的ControlState,常见包括默认(DEFAULT)、悬停(HOVERED)、聚焦(FOCUSED)、按下(PRESSED)、禁用(DISABLED)、选中(SELECTED)等。
传值方式有两种:直接传单个值(对所有状态生效),或传以状态为键的字典(按状态细分):
ft.MenuStyle( # 对所有状态统一生效 bgcolor=ft.Colors.SURFACE_CONTAINER, # 按状态细分 elevation={ ft.ControlState.DEFAULT: 2, ft.ControlState.HOVERED: 6, # 悬停时浮起更高 ft.ControlState.DISABLED: 0, }, side={ ft.ControlState.FOCUSED: ft.BorderSide(2, ft.Colors.PRIMARY), ft.ControlState.DEFAULT: ft.BorderSide(1, ft.Colors.OUTLINE), }, )在 Flutter 端,这些字典被转换为 Flutter 的WidgetStateProperty/MaterialStateProperty,通过parseWidgetStateColor、parseWidgetStateDouble、parseWidgetStateSize等工具完成映射(见 menu.dart)。
实战:构造一个完整定制的子菜单
下面把常用属性组合起来,构建一个带圆角、描边、状态反馈与固定宽度的子菜单:
import flet as ft def main(page: ft.Page): page.theme = ft.Theme( dropdown_theme=ft.DropdownTheme( menu_style=ft.MenuStyle( # 全局统一下拉菜单样式 shape=ft.RoundedRectangleBorder(radius=8), side=ft.BorderSide(1, ft.Colors.OUTLINE_VARIANT), elevation=3, ) ) ) page.add( ft.MenuBar( expand=True, controls=[ ft.SubmenuButton( content=ft.Text("视图"), menu_style=ft.MenuStyle( alignment=ft.Alignment.BOTTOM_LEFT, bgcolor=ft.Colors.SURFACE_CONTAINER_HIGH, shadow_color=ft.Colors.SHADOW, padding=ft.Padding(12, 8, 12, 8), min_size=ft.Size(160, 0), mouse_cursor=ft.MouseCursor.CLICK, visual_density=ft.VisualDensity.COMPACT, shape=ft.RoundedRectangleBorder(radius=10), side={ ft.ControlState.HOVERED: ft.BorderSide( 1.5, ft.Colors.PRIMARY ), ft.ControlState.DEFAULT: ft.BorderSide( 1, ft.Colors.OUTLINE ), }, ), controls=[ ft.MenuItemButton(content=ft.Text("放大")), ft.MenuItemButton(content=ft.Text("缩小")), ], ), ft.Dropdown( options=[ft.dropdown.Option("a"), ft.dropdown.Option("b")], menu_style=ft.MenuStyle(elevation=4), ), ], ) ) ft.app(main)该示例同时演示了三种注入方式:DropdownTheme.menu_style做全局兜底、SubmenuButton.menu_style定制子菜单、Dropdown.menu_style单独覆盖。当控件自身未设置menu_style时会回退到主题级配置。
底层原理:Python 属性如何变成 Flutter 的 MenuStyle
Flet 的 Python 侧只负责声明式地描述样式,真正的渲染发生在 Flutter 客户端。MenuStyle的每个字段与 FlutterMenuStyle的对应关系,可以在 menu.dart 的parseMenuStyle函数中逐一找到:
| Flet 属性 | Flutter 字段 | 解析函数 |
|---|---|---|
alignment | alignment | parseAlignment |
bgcolor | backgroundColor | parseWidgetStateColor |
shadow_color | shadowColor | parseWidgetStateColor |
elevation | elevation | parseWidgetStateDouble |
padding | padding | parseEdgeInsets |
side | side | parseBorderSide(默认描边色Colors.black) |
shape | shape | parseWidgetStateOutlinedBorder |
mouse_cursor | mouseCursor | parseWidgetStateMouseCursor |
min_size | minimumSize | parseWidgetStateSize |
max_size | maximumSize | parseWidgetStateSize |
fixed_size | fixedSize | parseWidgetStateSize |
visual_density | visualDensity | parseVisualDensity |
值得注意的是,parseMenuStyle接受defaultBackgroundColor、defaultElevation、defaultAlignment、defaultPadding、defaultBorderSide、defaultShape、defaultMinimumSize等大量默认值参数,说明同一个解析函数被MenuBar、Dropdown、SubmenuButton等不同控件复用——各控件在未显式传值时,可以注入各自的平台默认外观,再由 Flet 主题或控件级MenuStyle逐层覆盖。这也解释了为什么MenuStyle中任何属性都可以缺省:缺失的属性最终会落到 Flutter 主题的默认值上。
小结
MenuStyle是 Flet 菜单体系外观的"单一事实来源",12 个属性分别覆盖对齐、填充、阴影、描边、形状、光标、尺寸与密度八个维度,并借助ControlStateValue实现悬停、聚焦、按下等交互状态的精细反馈。掌握它之后,你既可以在单个菜单上做局部定制(SubmenuButton.menu_style、Dropdown.menu_style),也可以通过DropdownTheme.menu_style在主题层做全局统一,从而保持整个应用菜单视觉风格的一致性。
- 前端
- 跨平台
- 桌面应用
- 移动开发
【免费下载链接】flet
Build realtime web, mobile and desktop apps in Python only. No frontend experience required.
相关推荐
Flet SliderTheme 完全指南:用 Python 统一定制 Slider 与 RangeSlider 外观
Flet SliderTheme 完全指南:用 Python 统一定制 Slider 与 RangeSlider 外观 本指南以 Flet 官方类型文档 sli
前端跨平台桌面应用移动开发Flet CheckboxTheme 完整指南:用全局主题统一定制 Checkbox 外观
Flet CheckboxTheme 完整指南:用全局主题统一定制 Checkbox 外观 flet.CheckboxTheme 是 Flet 主题系统( fl
前端跨平台桌面应用移动开发Flet AnimationStyle 完全指南:精确控制控件与主题过渡动画
Flet AnimationStyle 完全指南:精确控制控件与主题过渡动画 导读 flet.AnimationStyle 是 Flet 中用于精细覆盖动画默认
前端跨平台桌面应用移动开发
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考