flutter-accessibility by flutter/skills
npx skills add https://github.com/flutter/skills --skill flutter-accessibility在 Flutter 应用程序中实现、审核并强制执行无障碍访问(a11y)和自适应设计标准。通过应用正确的语义角色、对比度、点击目标尺寸和辅助技术集成,确保符合 WCAG 2 和 EN 301 549 规范。构建能够响应可用屏幕空间和输入方式(触摸、鼠标、键盘)的自适应布局,而不依赖于特定硬件的检查或锁定方向。
在实现 UI 组件时,请遵循以下决策树来确定所需的无障碍访问和自适应设计实现:
SemanticsBinding.instance.ensureSemantics();。将自定义小部件显式映射到 SemanticsRole 以生成正确的 ARIA 标签。button: true 或适当的角色将其包装在 Semantics 中。确保点击目标尺寸 $\ge$ 48x48 逻辑像素。广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
LayoutBuilder 或 MediaQuery.sizeOf(context)。不要使用 MediaQuery.orientation 或硬件类型检查(例如 isTablet)。Expanded、Flexible)来填充可用空间。FocusableActionDetector、Shortcuts 和 MouseRegion。初始化 Web 无障碍访问(如适用) 对于 Web 目标,出于性能考虑,默认禁用无障碍访问。请在入口点强制启用它。
import 'package:flutter/foundation.dart';
import 'package:flutter/semantics.dart';
void main() {
runApp(const MyApp());
if (kIsWeb) {
SemanticsBinding.instance.ensureSemantics();
}
}
应用语义注解 使用 Semantics、MergeSemantics 和 ExcludeSemantics 来构建清晰的无障碍访问树。对于自定义 Web 组件,请显式定义 SemanticsRole。
Semantics(
role: SemanticsRole.button,
label: '提交表单',
hint: '按下以发送您的申请',
button: true,
child: GestureDetector(
onTap: _submit,
child: const CustomButtonUI(),
),
)
强制执行点击目标和对比度标准 确保所有交互元素满足 48x48 dp(Android)或 44x44 pt(iOS/Web)的最小尺寸要求。
// 强制执行最小点击目标尺寸的示例
ConstrainedBox(
constraints: const BoxConstraints(
minWidth: 48.0,
minHeight: 48.0,
),
child: IconButton(
icon: const Icon(Icons.info),
onPressed: () {},
tooltip: '信息', // Tooltip.message 在语义树中跟随 Tooltip.child
),
)
实现自适应布局 使用 LayoutBuilder 来响应可用空间,而不是设备类型。
LayoutBuilder(
builder: (context, constraints) {
if (constraints.maxWidth > 600) {
return const WideDesktopLayout();
} else {
return const NarrowMobileLayout();
}
},
)
实现键盘和鼠标支持 对于自定义控件,使用 FocusableActionDetector 来同时处理焦点、悬停和键盘快捷键。
FocusableActionDetector(
onFocusChange: (hasFocus) => setState(() => _hasFocus = hasFocus),
onShowHoverHighlight: (hasHover) => setState(() => _hasHover = hasHover),
actions: <Type, Action<Intent>>{
ActivateIntent: CallbackAction<Intent>(
onInvoke: (intent) {
_performAction();
return null;
},
),
},
child: MouseRegion(
cursor: SystemMouseCursors.click,
child: CustomWidget(isHovered: _hasHover, isFocused: _hasFocus),
),
)
管理焦点遍历 使用 FocusTraversalGroup 对相关小部件进行分组,以确保键盘用户有逻辑的 Tab 键顺序。
FocusTraversalGroup(
policy: OrderedTraversalPolicy(),
child: Column(
children: [
FocusTraversalOrder(
order: const NumericFocusOrder(1.0),
child: TextField(),
),
FocusTraversalOrder(
order: const NumericFocusOrder(2.0),
child: ElevatedButton(onPressed: () {}, child: Text('提交')),
),
],
),
)
通过自动化测试验证无障碍访问 请暂停并询问用户: "您是否希望我为您的 UI 生成小部件测试,以验证无障碍访问指南(对比度、点击目标)?" 如果是,请在测试套件中实现 AccessibilityGuideline API:
import 'package:flutter_test/flutter_test.dart';
void main() {
testWidgets('验证 a11y 指南', (WidgetTester tester) async {
final SemanticsHandle handle = tester.ensureSemantics();
await tester.pumpWidget(const MyApp());
await expectLater(tester, meetsGuideline(androidTapTargetGuideline));
await expectLater(tester, meetsGuideline(iOSTapTargetGuideline));
await expectLater(tester, meetsGuideline(labeledTapTargetGuideline));
await expectLater(tester, meetsGuideline(textContrastGuideline));
handle.dispose();
});
}
MediaQuery.sizeOf 或 LayoutBuilder。MediaQuery.orientation 来切换布局。应依赖于可用的宽度/高度断点。Semantics 标签。PageStorageKey 来保存滚动状态。Tooltip.message 会紧接着 Tooltip.child 之后被访问。请相应更新测试。973
792
2026年3月4日
codex930
cursor927
opencode926
gemini-cli925
github-copilot924
kimi-cli923
Implements, audits, and enforces accessibility (a11y) and adaptive design standards in Flutter applications. Ensures compliance with WCAG 2 and EN 301 549 by applying proper semantic roles, contrast ratios, tap target sizes, and assistive technology integrations. Constructs adaptive layouts that respond to available screen space and input modalities (touch, mouse, keyboard) without relying on hardware-specific checks or locked orientations.
When implementing UI components, follow this decision tree to determine the required accessibility and adaptive design implementations:
SemanticsBinding.instance.ensureSemantics(); is called at startup. Explicitly map custom widgets to SemanticsRole to generate correct ARIA tags.Semantics with button: true or appropriate role. Ensure tap target is $\ge$ 48x48 logical pixels.LayoutBuilder or MediaQuery.sizeOf(context). Do NOT use MediaQuery.orientation or hardware type checks (e.g., isTablet).Expanded, Flexible) to fill available space.FocusableActionDetector, Shortcuts, and MouseRegion for hover states and keyboard traversal.Initialize Web Accessibility (If Applicable) For web targets, accessibility is disabled by default for performance. Force enable it at the entry point.
import 'package:flutter/foundation.dart';
import 'package:flutter/semantics.dart';
void main() {
runApp(const MyApp());
if (kIsWeb) {
SemanticsBinding.instance.ensureSemantics();
}
}
Apply Semantic Annotations Use Semantics, MergeSemantics, and ExcludeSemantics to build a clean accessibility tree. For custom web components, explicitly define the SemanticsRole.
Semantics(
role: SemanticsRole.button,
label: 'Submit Form',
hint: 'Press to send your application',
button: true,
child: GestureDetector(
onTap: _submit,
child: const CustomButtonUI(),
),
)
Enforce Tap Target and Contrast Standards Ensure all interactive elements meet the 48x48 dp minimum (Android) or 44x44 pt minimum (iOS/Web).
MediaQuery.sizeOf or LayoutBuilder.MediaQuery.orientation near the top of the widget tree to switch layouts. Rely on available width/height breakpoints.Semantics labels for custom interactive widgets or images that convey meaning.PageStorageKey on scrollable lists that do not change layout during orientation shifts to preserve scroll state.Tooltip.message is visited immediately in the semantics tree. Update tests accordingly.Weekly Installs
973
Repository
GitHub Stars
792
First Seen
Mar 4, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
codex930
cursor927
opencode926
gemini-cli925
github-copilot924
kimi-cli923
React 组合模式指南:Vercel 组件架构最佳实践,提升代码可维护性
102,200 周安装
Grimoire CLI 使用指南:区块链法术编写、验证与执行全流程
940 周安装
Grimoire Uniswap 技能:查询 Uniswap 元数据与生成代币/资金池快照的 CLI 工具
940 周安装
Grimoire Aave 技能:查询 Aave V3 元数据和储备快照的 CLI 工具
941 周安装
Railway CLI 部署指南:使用 railway up 命令快速部署代码到 Railway 平台
942 周安装
n8n Python 代码节点使用指南:在自动化工作流中编写 Python 脚本
943 周安装
Flutter Platform Views 实现指南:Android/iOS/macOS原生视图与Web嵌入教程
943 周安装
// Example of enforcing minimum tap target size
ConstrainedBox(
constraints: const BoxConstraints(
minWidth: 48.0,
minHeight: 48.0,
),
child: IconButton(
icon: const Icon(Icons.info),
onPressed: () {},
tooltip: 'Information', // Tooltip.message follows Tooltip.child in semantics tree
),
)
Implement Adaptive Layouts Use LayoutBuilder to respond to available space rather than device type.
LayoutBuilder(
builder: (context, constraints) {
if (constraints.maxWidth > 600) {
return const WideDesktopLayout();
} else {
return const NarrowMobileLayout();
}
},
)
Implement Keyboard and Mouse Support Use FocusableActionDetector for custom controls to handle focus, hover, and keyboard shortcuts simultaneously.
FocusableActionDetector(
onFocusChange: (hasFocus) => setState(() => _hasFocus = hasFocus),
onShowHoverHighlight: (hasHover) => setState(() => _hasHover = hasHover),
actions: <Type, Action<Intent>>{
ActivateIntent: CallbackAction<Intent>(
onInvoke: (intent) {
_performAction();
return null;
},
),
},
child: MouseRegion(
cursor: SystemMouseCursors.click,
child: CustomWidget(isHovered: _hasHover, isFocused: _hasFocus),
),
)
Manage Focus Traversal Group related widgets using FocusTraversalGroup to ensure logical tab order for keyboard users.
FocusTraversalGroup(
policy: OrderedTraversalPolicy(),
child: Column(
children: [
FocusTraversalOrder(
order: const NumericFocusOrder(1.0),
child: TextField(),
),
FocusTraversalOrder(
order: const NumericFocusOrder(2.0),
child: ElevatedButton(onPressed: () {}, child: Text('Submit')),
),
],
),
)
Validate Accessibility via Automated Tests STOP AND ASK THE USER: "Would you like me to generate widget tests to validate accessibility guidelines (contrast, tap targets) for your UI?" If yes, implement the AccessibilityGuideline API in the test suite:
import 'package:flutter_test/flutter_test.dart';
void main() {
testWidgets('Validates a11y guidelines', (WidgetTester tester) async {
final SemanticsHandle handle = tester.ensureSemantics();
await tester.pumpWidget(const MyApp());
await expectLater(tester, meetsGuideline(androidTapTargetGuideline));
await expectLater(tester, meetsGuideline(iOSTapTargetGuideline));
await expectLater(tester, meetsGuideline(labeledTapTargetGuideline));
await expectLater(tester, meetsGuideline(textContrastGuideline));
handle.dispose();
});
}
Tooltip.child