关于 flutter-improving-accessibility
面向 Flutter 开发的无障碍检查与改进技能,强调复用 Material 和 Cupertino 的内置语义,并覆盖读屏、文字缩放、对比度及测试。
主要功能
- 01复用现有无障碍约定
- 02为自定义控件添加语义
- 03处理文字缩放与对比度
- 04检查触控区域和遍历顺序
- 05运行无障碍规范测试
适用场景
- 01修复 Flutter 屏幕的无障碍问题
- 02标注自绘或仅图标控件
- 03检查大字号下的布局
- 04测试语义节点和触控区域
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ thesimpleapp/ flutter-app-skills --skill flutter-improving-accessibility安装后在 Claude Code 中输入/flutter-improving-accessibility确认已加载。
附带文件references/recipes.md
详细介绍
Flutter 无障碍功能
优先使用内置的 Material 和 Cupertino 组件——它们已能为 TalkBack / VoiceOver / JAWS / NVDA 生成语义树。仅在自定义绘制或手势组件上添加 Semantics(或 Semantics.fromProperties)。
如果此代码库已有 Semantics 辅助工具、无障碍测试框架、带标签控件的约定,或带有 semanticLabel 的可按压组件,请扩展现有方案。不要再套一层 Semantics(label:),重复显示文本。
官方文档:无障碍功能、辅助技术、UI 设计与样式、无障碍测试。
参考资料
实现自定义控件、合并/排除/阻断语义、连接 MediaQuery 或编写指南测试时,请阅读 references/recipes.md。
先检查
在代码库中搜索 Semantics(、MergeSemantics、ExcludeSemantics、BlockSemantics、semanticLabel、excludeFromSemantics、tooltip:、meetsGuideline(、ensureSemantics(、highContrastTheme、accessibleNavigationOf、textScalerOf。打开附近的生产环境页面,并沿用其模式。
硬性规则
语义
- 自定义绘制 /
GestureDetector/InkWell:设置label、角色标志(button、slider、textField)、tooltip/hint和onTap,使节点被播报为控件。 - 角色是一个标志:
Semantics(button: true, label: 'Submit')。不要在标签中写入“按钮”。label/hint/tooltip需要环境中存在Directionality,或显式设置textDirection。瞬时状态:liveRegion: true。 container: true会引入一个SemanticsNode——用于建立独立的 TalkBack/VoiceOver 焦点停留点。
合并、排除、阻挡
- 一个控件及其可见的
Text:将两者包在MergeSemantics中。绝不要合并两个复选框、两个可点击的子项,或selected/checked值冲突的节点。 ExcludeSemantics仅用于装饰性或冗余的后代节点。Semantics(excludeSemantics: true)会用此节点替换所有子节点语义。隐藏绘制在模态框或Drawer后面的 Widget 时,应在叠层上使用BlockSemantics,而不是在叠层子项上使用ExcludeSemantics。
点击目标和名称
- 每个可点击目标至少为 48×48 逻辑像素(
kMinInteractiveDimension)。Cupertino 的下限为kMinInteractiveDimensionCupertino(44.0)。优先使用IconButton,确保点击区域始终为 48,不受iconSize影响。不要传入会将尺寸缩小至 48 以下的IconButton.constraints或紧凑型VisualDensity。 - 仅含图标的
IconButton/ FAB:tooltip应为简短的操作名称。自定义图标点击项:Semantics(label: ..., button: true)。装饰性Icon:semanticLabel。有意义的Image:semanticLabel。装饰性Image:excludeFromSemantics: true。
文本缩放
- 使用
MediaQuery.textScalerOf(context)读取,并用textScaler.scale(fontSize)计算尺寸。绝不要使用textScaleFactor——它已弃用,且无法表示 Android 14 的非线性缩放。 Text已遵循操作系统的字号设置。在小屏设备上使用操作系统的最大字号测试每个屏幕。仅使用MediaQuery.withNoTextScaling阻止图标字体缩放。绝不要在MaterialApp根节点禁用缩放。
MediaQuery 标志
- 调用
MediaQuery.accessibleNavigationOf、highContrastOf、disableAnimationsOf和textScalerOf,不要调用MediaQuery.of。 - 当
accessibleNavigationOf为 true 时,禁用超时,或提高超时时长下限。 - 当
highContrastOf为 true 时,设置MaterialApp.highContrastTheme和highContrastDarkTheme。在子树外层使用MediaQuery(data: copyWith(highContrast: true))并不会切换这些主题。 disableAnimationsOf对应 Android 的移除动画。AnimationController已遵循SemanticsBinding.disableAnimations。自定义显式动画仍必须读取disableAnimationsOf。iOS 的减少动态效果设置对应AccessibilityFeatures.reduceMotion,不会设置disableAnimations。
遍历顺序
- TalkBack/VoiceOver 的同级节点顺序:
Semantics(sortKey: OrdinalSortKey(n))。键盘/Tab 顺序属于不同的树:使用FocusTraversalGroup/FocusTraversalOrder。不要用sortKey修正 Tab 顺序。
对比度
- 文本/控件与背景之间的对比度至少为 4.5:1(禁用状态除外)。大号文本(常规字重 18pt 及以上,或粗体 14pt 及以上)为 3.0:1。不要仅靠颜色传达状态。
测试和设备
- 先调用
tester.ensureSemantics(),再调用expectLater(tester, meetsGuideline(...)),最后调用handle.dispose()。指南:androidTapTargetGuideline、iOSTapTargetGuideline、labeledTapTargetGuideline、textContrastGuideline。 - 使用 TalkBack 和 VoiceOver 遍历每个有改动的屏幕。Flutter Web:在
main()中调用SemanticsBinding.instance.ensureSemantics()。使用showSemanticsDebugger或flutter run -d chrome --profile --dart-define=FLUTTER_WEB_DEBUG_SHOW_SEMANTICS=true进行调试。
示例
Semantics(
button: true,
label: 'Submit',
hint: 'Sends the form',
onTap: onSubmit,
child: CustomPaint(painter: SubmitPainter()),
)
IconButton(
tooltip: 'Search',
icon: const Icon(Icons.search),
onPressed: onSearch,
)
final TextScaler scaler = MediaQuery.textScalerOf(context);
final bool screenReader = MediaQuery.accessibleNavigationOf(context);
不要
- 不要在已有语义的 Material/Cupertino 控件外包一层
Semantics(label:),重复显示的文本。 - 不要在可交互内容外包一层
ExcludeSemantics,也不要用它代替BlockSemantics来隐藏对话框后面的控件。 - 不要对两个复选框或两个可点击的子项使用
MergeSemantics。 - 不要使用没有
tooltip、没有Semantics.label且没有Icon.semanticLabel的纯图标控件。 - 不要使用
MediaQuery.of(context).textScaleFactor或Text(textScaleFactor:)。 - 不要在应用根节点禁用文本缩放,也不要将
IconButton缩小到 48 以下。 - 不要假设
MediaQuery(data: copyWith(disableAnimations: true))会停止AnimationController。 - 不要将角色写入标签(
label: 'Submit button')。 - 不要使用
Semantics.sortKey修复键盘 Tab 顺序。
修复屏幕
- 检测现有的无障碍约定并沿用。
- 除非是自定义绘制的控件或纯图标控件,否则不要为 Material/Cupertino 内置控件添加标签。
- 为每个纯图标控件提供
tooltip或Semantics(label:, button: true)。将触控目标填充到 48,而不是图标本身。 - 读取
MediaQuery.*Of。兼顾最大字号、高对比度主题、屏幕阅读器超时设置和减少动态效果。 - 添加指南测试,并使用 TalkBack 和 VoiceOver 逐项检查。
验证
- 在你修改的屏幕上调用
tester.ensureSemantics()、进行四项meetsGuideline检查,并调用handle.dispose()。 - 在小屏设备上,布局仍能适配操作系统的最大字号。
- 对比度 ≥ 4.5:1(大号文本为 3:1);状态不能仅通过颜色区分。
- 在 TalkBack 和 VoiceOver 中,每个控件都有清晰易懂的名称和正确的角色。
- Web:从
main()启用语义。