flutter-accessibility-audit

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Implementing Flutter Accessibility

Flutter无障碍功能实现

Contents

目录

Managing Semantics

语义管理

Rely on Flutter's standard widgets (e.g.,
TabBar
,
MenuAnchor
) for automatic semantic role assignment whenever possible. When building custom components or overriding default behaviors, explicitly define the UI element's purpose using the
Semantics
widget.
  • Wrap custom UI components in a
    Semantics
    widget.
  • Assign the appropriate
    SemanticsRole
    enum value to the
    role
    property to define the element's purpose (e.g., button, list, heading).
  • If building for Flutter Web, note that Flutter translates these roles into corresponding ARIA roles in the HTML DOM.
  • Enable web accessibility explicitly. It is disabled by default for performance. Either instruct users to press the invisible
    aria-label="Enable accessibility"
    button, or force it programmatically in your
    main()
    function.
尽可能依赖Flutter的标准组件(如
TabBar
MenuAnchor
)来自动分配语义角色。在构建自定义组件或覆盖默认行为时,使用
Semantics
组件明确定义UI元素的用途。
  • 将自定义UI组件包裹在
    Semantics
    组件中。
  • role
    属性分配合适的
    SemanticsRole
    枚举值,以定义元素用途(如按钮、列表、标题)。
  • 如果针对Flutter Web开发,请注意Flutter会将这些角色转换为HTML DOM中对应的ARIA角色。
  • 显式启用Web无障碍功能,它默认处于禁用状态以提升性能。可以指导用户点击隐藏的
    aria-label="Enable accessibility"
    按钮,或在
    main()
    函数中通过代码强制启用。

Auditing Accessibility

无障碍审计

Implement the following workflows to verify that your application meets accessibility standards.
实施以下工作流,验证应用是否符合无障碍标准。

Task Progress: Platform-Specific Scanning

任务进度:平台专属扫描

Copy this checklist to track your manual auditing progress across target platforms:
  • If testing on Android:
    1. Install the Accessibility Scanner from Google Play.
    2. Enable it via Settings > Accessibility > Accessibility Scanner > On.
    3. Tap the Accessibility Scanner checkmark icon over your running app to initiate the scan.
  • If testing on iOS:
    1. Open the
      ios
      folder in Xcode and run the app on a Simulator.
    2. Navigate to Xcode > Open Developer Tools > Accessibility Inspector.
    3. Select Inspection > Enable Point to Inspect and click UI elements to verify attributes.
    4. Select Audit > Run Audit to generate an issue report.
  • If testing on Web:
    1. Open Chrome DevTools.
    2. Inspect the HTML tree under the
      semantics host
      node.
    3. Navigate to the Elements tab and open the Accessibility sub-tab to inspect exported ARIA data.
    4. Visualize semantic nodes by running the app with:
      flutter run -d chrome --profile --dart-define=FLUTTER_WEB_DEBUG_SHOW_SEMANTICS=true
      .
复制此检查清单,跟踪各目标平台的手动审计进度:
  • Android测试:
    1. 从Google Play安装Accessibility Scanner。
    2. 通过设置 > 无障碍 > Accessibility Scanner > 开启启用它。
    3. 在运行的应用上方点击Accessibility Scanner的对勾图标,启动扫描。
  • iOS测试:
    1. 在Xcode中打开
      ios
      文件夹,在模拟器上运行应用。
    2. 导航至Xcode > 打开开发者工具 > Accessibility Inspector
    3. 选择检查 > 启用点选检查,点击UI元素验证属性。
    4. 选择审计 > 运行审计生成问题报告。
  • Web测试:
    1. 打开Chrome DevTools。
    2. 检查
      semantics host
      节点下的HTML树。
    3. 导航至元素标签,打开无障碍子标签检查导出的ARIA数据。
    4. 通过以下命令运行应用,可视化语义节点:
      flutter run -d chrome --profile --dart-define=FLUTTER_WEB_DEBUG_SHOW_SEMANTICS=true

Task Progress: Automated Testing

任务进度:自动化测试

Integrate Flutter's Accessibility Guideline API into your widget tests to catch contrast, target size, and labeling issues automatically.
  • Create a dedicated test file (e.g.,
    test/a11y_test.dart
    ).
  • Initialize the semantics handle using
    tester.ensureSemantics()
    .
  • Assert against
    androidTapTargetGuideline
    (48x48px minimum).
  • Assert against
    iOSTapTargetGuideline
    (44x44px minimum).
  • Assert against
    labeledTapTargetGuideline
    .
  • Assert against
    textContrastGuideline
    (3:1 minimum for large text).
  • Dispose of the semantics handle at the end of the test.
将Flutter的无障碍指南API集成到组件测试中,自动检测对比度、目标尺寸和标签问题。
  • 创建专用测试文件(如
    test/a11y_test.dart
    )。
  • 使用
    tester.ensureSemantics()
    初始化语义句柄。
  • 断言符合
    androidTapTargetGuideline
    (最小48x48px)。
  • 断言符合
    iOSTapTargetGuideline
    (最小44x44px)。
  • 断言符合
    labeledTapTargetGuideline
  • 断言符合
    textContrastGuideline
    (大文本最小对比度3:1)。
  • 测试结束时销毁语义句柄。

Debugging the Semantics Tree

语义树调试

When semantic nodes are incorrectly placed or missing, execute the following feedback loop to identify and resolve the discrepancies.
  1. Run validator: Trigger a dump of the Semantics tree to the console.
    • Enable accessibility via a system tool or
      SemanticsDebugger
      .
    • Invoke
      debugDumpSemanticsTree()
      (e.g., bind it to a
      GestureDetector
      's
      onTap
      callback for easy triggering during debugging).
  2. Review errors: Analyze the console output to locate missing labels, incorrect roles, or improperly nested semantic nodes.
  3. Fix: Wrap the offending widgets in
    Semantics
    or
    MergeSemantics
    widgets, apply the correct
    SemanticsRole
    , and repeat step 1 until the tree accurately reflects the visual UI.
当语义节点位置错误或缺失时,执行以下反馈循环来识别并解决问题。
  1. 运行验证器: 将语义树转储到控制台。
    • 通过系统工具或
      SemanticsDebugger
      启用无障碍功能。
    • 调用
      debugDumpSemanticsTree()
      (例如,将其绑定到
      GestureDetector
      onTap
      回调,方便调试时触发)。
  2. 查看错误: 分析控制台输出,定位缺失的标签、错误的角色或嵌套不当的语义节点。
  3. 修复: 将有问题的组件包裹在
    Semantics
    MergeSemantics
    组件中,应用正确的
    SemanticsRole
    ,重复步骤1直到语义树准确反映视觉UI。

Examples

示例

Programmatically Enabling Web Accessibility

通过代码启用Web无障碍功能

Force the Semantics tree to build immediately on Flutter Web.
dart
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/semantics.dart';

void main() {
  runApp(const MyApp());
  if (kIsWeb) {
    SemanticsBinding.instance.ensureSemantics();
  }
}
强制Flutter Web立即构建语义树。
dart
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/semantics.dart';

void main() {
  runApp(const MyApp());
  if (kIsWeb) {
    SemanticsBinding.instance.ensureSemantics();
  }
}

Explicitly Defining Semantic Roles

显式定义语义角色

Assign explicit list and list-item roles to a custom layout.
dart
import 'package:flutter/material.dart';
import 'package:flutter/semantics.dart';

class MyCustomListWidget extends StatelessWidget {
  const MyCustomListWidget({Key? key}) : super(key: key);

  
  Widget build(BuildContext context) {
    return Semantics(
      role: SemanticsRole.list,
      explicitChildNodes: true,
      child: Column(
        children: <Widget>[
          Semantics(
            role: SemanticsRole.listItem,
            child: const Padding(
              padding: EdgeInsets.all(8.0),
              child: Text('Content of the first custom list item.'),
            ),
          ),
          Semantics(
            role: SemanticsRole.listItem,
            child: const Padding(
              padding: EdgeInsets.all(8.0),
              child: Text('Content of the second custom list item.'),
            ),
          ),
        ],
      ),
    );
  }
}
为自定义布局分配明确的列表和列表项角色。
dart
import 'package:flutter/material.dart';
import 'package:flutter/semantics.dart';

class MyCustomListWidget extends StatelessWidget {
  const MyCustomListWidget({Key? key}) : super(key: key);

  
  Widget build(BuildContext context) {
    return Semantics(
      role: SemanticsRole.list,
      explicitChildNodes: true,
      child: Column(
        children: <Widget>[
          Semantics(
            role: SemanticsRole.listItem,
            child: const Padding(
              padding: EdgeInsets.all(8.0),
              child: Text('Content of the first custom list item.'),
            ),
          ),
          Semantics(
            role: SemanticsRole.listItem,
            child: const Padding(
              padding: EdgeInsets.all(8.0),
              child: Text('Content of the second custom list item.'),
            ),
          ),
        ],
      ),
    );
  }
}

Automated Accessibility Testing

无障碍自动化测试

Implement the Accessibility Guideline API in a widget test.
dart
import 'package:flutter_test/flutter_test.dart';
import 'package:your_accessible_app/main.dart';

void main() {
  testWidgets('Follows a11y guidelines', (tester) async {
    final SemanticsHandle handle = tester.ensureSemantics();
    await tester.pumpWidget(const AccessibleApp());

    // Check tap target sizes
    await expectLater(tester, meetsGuideline(androidTapTargetGuideline));
    await expectLater(tester, meetsGuideline(iOSTapTargetGuideline));

    // Check labels and contrast
    await expectLater(tester, meetsGuideline(labeledTapTargetGuideline));
    await expectLater(tester, meetsGuideline(textContrastGuideline));
    
    handle.dispose();
  });
}
在组件测试中实现无障碍指南API。
dart
import 'package:flutter_test/flutter_test.dart';
import 'package:your_accessible_app/main.dart';

void main() {
  testWidgets('Follows a11y guidelines', (tester) async {
    final SemanticsHandle handle = tester.ensureSemantics();
    await tester.pumpWidget(const AccessibleApp());

    // 检查点击目标尺寸
    await expectLater(tester, meetsGuideline(androidTapTargetGuideline));
    await expectLater(tester, meetsGuideline(iOSTapTargetGuideline));

    // 检查标签和对比度
    await expectLater(tester, meetsGuideline(labeledTapTargetGuideline));
    await expectLater(tester, meetsGuideline(textContrastGuideline));
    
    handle.dispose();
  });
}