平台惯用法
了解如何创建能响应屏幕尺寸变化的响应式应用。
自适应应用还需考虑的最后一块是平台标准。各平台有自己的惯用法与规范;这些名义上或事实上的标准塑造用户对应用应如何行为的预期。部分得益于 Web,用户已习惯更定制的体验,但体现这些平台标准仍能带来显著好处:
-
降低认知负担
通过匹配用户既有心智模型,完成任务更直观,减少思考、提升效率并降低挫败感。 -
建立信任
应用不符合预期时用户可能警惕或怀疑。反之,熟悉的 UI 能建立信任并提升质量感知,往往还能带来更好的应用商店评分——我们都乐见其成!
考虑各平台的预期行为
#第一步是花时间思考该平台上的预期外观、呈现或行为。尽量忘记当前实现的限制,只设想理想用户体验,再倒推实现。
另一种思考方式是问:「该平台用户会如何预期达成此目标?」然后设想在你的应用中如何无妥协地实现。
若你不是该平台的常用用户,这可能较难。你可能不了解具体惯用法并完全遗漏。例如,长期使用 Android 的用户可能不了解 iOS 平台惯例,macOS、Linux、Windows 同理。这些差异对你可能细微,对资深用户却可能非常明显。
寻找平台倡导者
#若可能,为每个平台指定一名倡导者。理想情况下,倡导者以该平台为主要设备,并能提供挑剔用户的视角。为减少人数可合并角色:一人负责 Windows 与 Android,一人负责 Linux 与 Web,一人负责 Mac 与 iOS。
目标是获得持续、专业的反馈,使应用在各平台都出色。应鼓励倡导者挑剔,指出与设备上典型应用的差异。简单例子:对话框默认按钮在 Mac 与 Linux 通常在左侧,Windows 则在右侧。若不常用某平台,此类细节易被忽略。
保持独特
#符合预期行为并不意味着必须使用默认 widget 或样式。许多热门多平台应用有鲜明、主观的 UI,包括自定义按钮、上下文菜单与标题栏。
跨平台整合样式与行为越多,开发与测试越轻松。诀窍是在打造强识别度的独特体验与尊重各平台规范之间取得平衡。
常见惯用法与规范
#快速浏览你可能要考虑的若干规范与惯用法,以及在 Flutter 中的实现思路。
滚动条外观与行为
#桌面与移动用户都预期有滚动条,但各平台行为不同。移动用户预期较小、仅在滚动时出现的滚动条;桌面用户通常预期常驻、较大且可点击或拖动的滚动条。
Flutter 自带 Scrollbar widget,已根据当前平台支持自适应颜色与尺寸。你可能只需在桌面平台切换 alwaysShown:
return Scrollbar(
thumbVisibility: DeviceType.isDesktop,
controller: _scrollController,
child: GridView.count(
controller: _scrollController,
padding: const EdgeInsets.all(Insets.extraLarge),
childAspectRatio: 1,
crossAxisCount: colCount,
children: listChildren,
),
);
这些细节关注能让应用在给定平台上更舒适。
多选
#列表内多选是另一存在跨平台细微差异的领域:
static bool get isSpanSelectModifierDown =>
isKeyDown({LogicalKeyboardKey.shiftLeft, LogicalKeyboardKey.shiftRight});
要进行平台感知的 Control 或 Command 检查,可编写类似代码:
static bool get isMultiSelectModifierDown {
bool isDown = false;
if (Platform.isMacOS) {
isDown = isKeyDown({
LogicalKeyboardKey.metaLeft,
LogicalKeyboardKey.metaRight,
});
} else {
isDown = isKeyDown({
LogicalKeyboardKey.controlLeft,
LogicalKeyboardKey.controlRight,
});
}
return isDown;
}
键盘用户还需考虑 全选 操作。若有大量可选列表项,许多键盘用户会预期可用 Control+A 全选。
触控设备
#
在触控设备上,多选通常更简单,预期行为类似桌面按住 isMultiSelectModifier。可用单击选择或取消选择,通常还有 全选 或 清除 当前选择的按钮。
不同设备上的多选处理取决于具体用例,重要的是为各平台提供最佳交互模型。
可选中文本
#Web(以及程度较轻的桌面)上常见预期是:大部分可见文字可用鼠标选中。文字不可选时,Web 用户往往反感。
幸运的是,用 SelectableText
widget 即可轻松支持:
return const SelectableText('Select me!');
要支持富文本,请使用 TextSpan:
return const SelectableText.rich(
TextSpan(
children: [
TextSpan(text: 'Hello'),
TextSpan(
text: 'Bold',
style: TextStyle(fontWeight: FontWeight.bold),
),
],
),
);
标题栏
#在现代桌面应用中,常见做法是自定义应用窗口标题栏,添加 Logo 强化品牌或上下文控件以节省主 UI 垂直空间。
Flutter 不直接支持,但可用 bits_dojo package 禁用原生标题栏并替换为自定义实现。
该 package 底层使用纯 Flutter widget,可在 TitleBar 中添加任意 widget,便于在应用不同区域导航时适配标题栏。
上下文菜单与工具提示
#在桌面上,若干交互以叠加层中的 widget 呈现,但触发、关闭与定位方式不同:
-
上下文菜单
通常由右键触发,靠近鼠标定位,通过任意点击、选择菜单项或点击外部关闭。 -
Tooltip(工具提示)
通常在交互元素上悬停 200–400ms 触发,一般锚定到 widget(而非鼠标位置),鼠标离开该 widget 时关闭。 -
弹出面板(亦称 flyout)
与工具提示类似,通常锚定到 widget。主要区别是面板多在点击时显示,光标离开通常不会自动隐藏,而是通过点击面板外部或按 关闭 / 提交 按钮关闭。
在 Flutter 中显示基本工具提示,请使用内置 Tooltip
widget:
return const Tooltip(
message: 'I am a Tooltip',
child: Text('Hover over the text to show a tooltip.'),
);
Flutter 在编辑或选择文字时也提供内置上下文菜单。
要显示更高级的工具提示、弹出面板或创建自定义上下文菜单,可使用现有 package,或用 Stack 或 Overlay 自行构建。
部分可用 package 包括:
这些控件对触控用户可作为加速器很有价值,对鼠标用户则必不可少。他们预期右键、就地编辑、悬停获取更多信息。未满足这些预期会令用户失望,或至少觉得「哪里不对」。
水平按钮顺序
#在 Windows 上,一行按钮中确认按钮在行首(左侧)。其他平台相反,确认按钮在行尾(右侧)。
在 Flutter 中可用 Row 的 TextDirection 属性轻松处理:
TextDirection btnDirection = DeviceType.isWindows
? TextDirection.rtl
: TextDirection.ltr;
return Row(
children: [
const Spacer(),
Row(
textDirection: btnDirection,
children: [
DialogButton(
label: 'Cancel',
onPressed: () => Navigator.pop(context, false),
),
DialogButton(
label: 'Ok',
onPressed: () => Navigator.pop(context, true),
),
],
),
],
);
菜单栏
#桌面应用另一常见模式是菜单栏。 Windows 与 Linux 上菜单是 Chrome 标题栏的一部分, macOS 上则位于主屏幕顶部。
目前可用原型插件指定自定义菜单栏项,预计该功能最终会并入主 SDK。
值得一提的是,Windows 与 Linux 上无法将自定义标题栏与菜单栏并存。创建自定义标题栏会完全替换原生标题栏,因而也会失去集成的原生菜单栏。
若同时需要自定义标题栏与菜单栏,可在 Flutter 中实现,类似自定义上下文菜单。
拖放
#触控与指针输入的核心交互之一是拖放。两种输入都预期有此交互,但在可滚动可拖放列表上存在重要差异。
一般而言,触控用户预期看到拖动手柄以区分可拖区域与可滚动区域,或通过长按启动拖动,因为滚动与拖动共用单指输入。
鼠标用户输入选项更多,可用滚轮或滚动条滚动,通常无需专用拖动手柄。 macOS Finder 或 Windows 资源管理器即如此:选中项即可拖动。
在 Flutter 中可用多种方式实现拖放。具体实现超出本文范围,高层选项包括:
-
直接使用
Draggable与DragTargetAPI 实现自定义外观。 -
接入
onPan手势事件,在父Stack中自行移动对象。 -
使用 pub.dev 上的 预制列表 package。
除非另有说明,本文档之所提及适用于 Flutter 3.44.0 版本。本页面最后更新时间:2026-06-16。查看文档源码 或者 为本页面内容提出建议。