确保应用能够被广大用户访问是构建高质量应用的重要组成部分。设计不佳的应用会为各个年龄段的人群制造障碍。《联合国残疾人权利公约》指出,确保信息系统普遍可访问是一项道德和法律义务;世界各国都将无障碍性作为一项要求强制执行;而公司也认识到最大化服务访问所带来的商业优势。

我们强烈建议您在发布应用之前,将无障碍功能清单作为一项关键标准。Flutter 致力于支持开发者使其应用更具无障碍性,除了底层操作系统提供的支持外,还包括一流的框架无障碍功能支持,例如:

大字体
根据用户指定的字体大小渲染文本组件
屏幕阅读器
针对 UI 内容提供语音反馈
足够的对比度
使用具有足够对比度的颜色渲染组件

这些功能的详细信息将在下文讨论。

检查无障碍支持

#

除了针对这些特定主题进行测试之外,我们还建议使用自动化无障碍扫描工具。

  • 适用于 Android

    1. 安装适用于 Android 的 无障碍扫描工具
    2. Android 设置 > 无障碍功能 > 无障碍扫描工具 > 开启 中启用无障碍扫描工具
    3. 导航到无障碍扫描工具的“复选框”图标按钮以启动扫描
  • 适用于 iOS

    1. 在 Xcode 中打开 Flutter 应用的 iOS 文件夹
    2. 选择一个模拟器作为目标,然后点击 运行 按钮
    3. 在 Xcode 中,选择 Xcode > 打开开发者工具 > 无障碍检查器
    4. 在无障碍检查器中,选择 检查 > 启用指针检查,然后选择正在运行的 Flutter 应用中的各种用户界面元素来检查它们的无障碍属性
    5. 在无障碍检查器中,在工具栏中选择 审核,然后选择 运行审核 以获取潜在问题的报告
  • 适用于 web

    1. 打开 Chrome 开发者工具(或其它浏览器中的类似工具)
    2. 检查包含 Flutter 生成的 ARIA 属性的 HTML 树。
    3. 在 Chrome 中,“元素”选项卡有一个“无障碍”子选项卡,可用于检查导出到语义树的数据

大字体

#

Android 和 iOS 都包含用于配置应用所需字体大小的系统设置。Flutter 文本组件在确定字体大小时会遵循此操作系统设置。

字体大小由 Flutter 根据操作系统设置自动计算。但是,作为开发者,您应该确保在字体大小增加时,您的布局有足够的空间来渲染所有内容。例如,您可以在配置为使用最大字体设置的小屏幕设备上测试应用的所有部分。

示例

#

以下两张截图显示了在默认 iOS 字体设置下渲染的标准 Flutter 应用模板,以及在 iOS 无障碍设置中选择了最大字体设置后的效果。

Default font setting
默认字体设置
Largest accessibility font setting
最大无障碍字体设置

屏幕阅读器

#

对于移动设备,屏幕阅读器(TalkBackVoiceOver)能帮助视障用户通过语音反馈获取屏幕内容,并通过移动设备上的手势和桌面上的键盘快捷键与 UI 交互。在您的移动设备上打开 VoiceOver 或 TalkBack,并在您的应用中进行导航。

要在您的设备上打开屏幕阅读器,请完成以下步骤:

  1. 在您的设备上,打开 设置
  2. 选择 无障碍功能,然后选择 TalkBack
  3. 开启或关闭“使用 TalkBack”。
  4. 选择确定。

要了解如何查找和自定义 Android 的无障碍功能,请观看以下视频。

在新标签页中在 YouTube 上观看:“自定义 Pixel 和 Android 无障碍功能”

  1. 在您的设备上,打开 设置 > 无障碍功能 > VoiceOver
  2. 开启或关闭 VoiceOver 设置

要了解如何查找和自定义 iOS 无障碍功能,请观看以下视频。

在新标签页中在 YouTube 上观看:“如何使用 VoiceOver 导航您的 iPhone 或 iPad”

对于 web,目前支持以下屏幕阅读器:

移动浏览器

  • iOS - VoiceOver
  • Android - TalkBack

桌面浏览器

  • macOS - VoiceOver
  • Windows - JAWs 和 NVDA

Web 上的屏幕阅读器用户必须切换“启用无障碍功能”按钮来构建语义树。如果您使用此 API 以编程方式自动为您的应用启用无障碍功能,用户可以跳过此步骤。

dart
import 'package:flutter/material.dart';
import 'package:flutter/semantics.dart';

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

Windows 自带一个名为 Narrator 的屏幕阅读器,但有些开发者推荐使用更流行的 NVDA 屏幕阅读器。要了解如何使用 NVDA 测试 Windows 应用,请查阅 面向前端开发者的屏幕阅读器基础 101 (Windows)

在 Mac 上,您可以使用 macOS 中包含的桌面版 VoiceOver。

在新标签页中在 YouTube 上观看:“屏幕阅读器基础:VoiceOver”

在 Linux 上,一个流行的屏幕阅读器名为 Orca。它预装在一些发行版中,并且可以在 apt 等软件包仓库中获取。要了解如何使用 Orca,请查阅 在 Gnome 桌面开始使用 Orca 屏幕阅读器


观看以下视频演示,了解 Victor Tsaran 如何在使用 VoiceOver 时操作现已存档的 Flutter Gallery web 应用。

Flutter 的标准组件会自动生成无障碍树。但是,如果您的应用需要不同的行为,可以使用 Semantics 组件进行自定义。

当您的应用中有文本需要以特定语音朗读时,通过调用 TextSpan.locale 告知屏幕阅读器使用哪种语音。请注意,MaterialApp.localeLocalizations.override 不会影响屏幕阅读器使用的语音。通常,屏幕阅读器会使用系统语音,除非您使用 TextSpan.locale 显式设置。

足够的对比度

#

足够的色彩对比度使文本和图像更易于阅读。除了使有各种视力障碍的用户受益外,足够的色彩对比度还有助于所有用户在极端光照条件下(例如在阳光直射下或在低亮度显示屏上)查看界面。

W3C 建议

  • 小文本(低于 18 磅常规字体或 14 磅粗体)至少 4.5:1
  • 大文本(18 磅及以上常规字体或 14 磅及以上粗体)至少 3.0:1

考虑无障碍性进行构建

#

确保您的应用能被所有人使用意味着从一开始就将其构建为具有无障碍功能。对于某些应用来说,这说起来容易做起来难。在下面的视频中,我们的两名工程师将一个移动应用从糟糕的无障碍状态改进为利用 Flutter 内置组件提供显著更具无障碍性的体验。

在新标签页中在 YouTube 上观看:“构建具有无障碍功能的 Flutter 应用”

利用语义角色提升无障碍功能

#

什么是语义角色?

#

语义角色定义了 UI 元素的用途,帮助屏幕阅读器和其他辅助工具有效地向用户解释和呈现您的应用。例如,一个角色可以指示某个组件是按钮、链接、标题、滑块还是表格的一部分。

尽管 Flutter 的标准组件通常会自动提供这些语义信息,但对于屏幕阅读器用户来说,一个没有明确定义角色的自定义组件可能是难以理解的。

通过分配适当的角色,您可以确保:

  • 屏幕阅读器可以正确地播报元素的类型和用途。
  • 用户可以使用辅助技术更有效地导航您的应用。
  • 您的应用符合 web 无障碍标准,提升了可用性。

在 Flutter web 应用中使用 SemanticsRole

#

Flutter 提供了 Semantics 组件SemanticsRole 枚举,允许开发者为其组件分配特定角色。当您的 Flutter web 应用渲染时,这些 Flutter 特定的角色会被转换为网页 HTML 结构中相应的 ARIA 角色。

1. 标准组件的自动语义

许多标准 Flutter 组件,如 TabBarMenuAnchorTable,都会自动包含语义信息及其角色。在可能的情况下,优先使用这些标准组件,因为它们开箱即用地处理了许多无障碍性方面的问题。

2. 显式添加或覆盖角色

对于自定义组件或默认语义不足时,使用 Semantics 组件定义角色

下面是一个如何显式定义列表及其各项的示例:

dart
import 'package:flutter/material.dart';
import 'package:flutter/semantics.dart';


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

  @override
  Widget build(BuildContext context) {
    // This example shows how to explicitly assign list and listitem roles
    // when building a custom list structure. 
    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.'),
            ),
          ),
        ],
      ),
    );
  }
}

在移动设备上测试无障碍功能

#

使用 Flutter 的 无障碍指南 API 测试您的应用。此 API 检查您应用的 UI 是否符合 Flutter 的无障碍建议。这些建议涵盖了文本对比度、目标大小和目标标签。

以下代码片段展示了如何在名为 AccessibleApp 的示例组件上使用指南 API

test/a11y_test.dart
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());

    // Checks that tappable nodes have a minimum size of 48 by 48 pixels
    // for Android.
    await expectLater(tester, meetsGuideline(androidTapTargetGuideline));

    // Checks that tappable nodes have a minimum size of 44 by 44 pixels
    // for iOS.
    await expectLater(tester, meetsGuideline(iOSTapTargetGuideline));

    // Checks that touch targets with a tap or long press action are labeled.
    await expectLater(tester, meetsGuideline(labeledTapTargetGuideline));

    // Checks whether semantic nodes meet the minimum text contrast levels.
    // The recommended text contrast is 3:1 for larger text
    // (18 point and above regular).
    await expectLater(tester, meetsGuideline(textContrastGuideline));
    handle.dispose();
  });
}

要尝试这些测试,请在您在编写您的第一个 Flutter 应用 Codelab 中创建的应用上运行它们。该应用主屏幕上的每个按钮都作为一个可点击目标,其文本以 18 磅字体渲染。

您可以将指南 API 测试添加到其他组件测试旁边,或者放入单独的文件中,例如本例中的 test/a11y_test.dart

在 web 上测试无障碍功能

#

您可以通过在 profile 和 release 模式下使用以下命令行标志来可视化为您的 web 应用创建的语义节点,从而调试无障碍功能

flutter run -d chrome --profile --dart-define=FLUTTER_WEB_DEBUG_SHOW_SEMANTICS=true

激活该标志后,语义节点将显示在组件上方;您可以验证语义元素是否放置在正确的位置。如果语义节点放置不正确,请提交错误报告

无障碍功能发布清单

#

以下是您在准备发布应用时需要考虑的一些事项,此列表并非详尽无遗。

  • 活跃交互。确保所有活跃交互都能执行某些操作。任何可以按下的按钮在按下时都应该有所响应。例如,如果您的 onPressed 事件有一个空操作回调,请将其更改为在屏幕上显示 SnackBar,解释您刚刚按下了哪个控件。
  • 屏幕阅读器测试。当您点击页面上的所有控件时,屏幕阅读器应该能够描述它们,并且描述应该是清晰易懂的。使用 TalkBack (Android) 和 VoiceOver (iOS) 测试您的应用。
  • 对比度。我们鼓励您在控件或文本与背景之间保持至少 4.5:1 的对比度,禁用组件除外。图像也应进行审查以确保足够的对比度。
  • 上下文切换。在输入信息时,不应有任何东西自动改变用户的上下文。通常,组件应避免在没有某种确认操作的情况下改变用户的上下文。
  • 可点击目标。所有可点击目标应至少为 48x48 像素。
  • 错误。重要的操作应该能够撤销。在显示错误的字段中,如果可能,请建议更正。
  • 色觉缺陷测试。控件在色盲和灰度模式下应保持可用和清晰。
  • 缩放因子。在文本大小和显示缩放的非常大缩放因子下,UI 仍应保持清晰和可用。

了解更多

#

要了解更多关于 Flutter 和无障碍功能的信息,请查阅社区成员撰写的以下文章: