跳转至正文

Web 无障碍

关于 Web 无障碍的信息

背景

#

Flutter 通过将内部 Semantics 树转换为屏幕阅读器可理解的 HTML DOM 结构来支持 Web 无障碍。由于 Flutter 在单一 canvas 上渲染 UI,需要特殊层向 Web 浏览器暴露 UI 的含义与结构。

可选启用的 Web 无障碍

#

隐形按钮

#

出于性能考虑,Flutter 的 Web 无障碍默认未开启。要启用无障碍,用户需按下带有 aria-label="Enable accessibility" 的隐形按钮。按下后,DOM 树将反映 widget 的全部无障碍信息。

在代码中开启无障碍模式

#

另一种方式是在运行应用时添加以下代码以开启无障碍模式。

dart
import 'package:flutter/semantics.dart';
​
void main() {
  runApp(const MyApp());
  if (kIsWeb) {
    SemanticsBinding.instance.ensureSemantics();
  }
}

使用 Semantic Role 增强无障碍

#

什么是 Semantic Role?

#

Semantic Role 定义 UI 元素的用途,帮助屏幕阅读器和其他辅助工具向用户有效解释和呈现你的应用。例如,角色可表明 widget 是按钮、链接、标题、滑块还是表格的一部分。

Flutter 的标准 widget 通常会自动提供这些语义,但没有明确定义角色的自定义 widget 可能对屏幕阅读器用户难以理解。

通过分配合适的角色,你可以确保:

  • 屏幕阅读器能正确播报元素的类型与用途。

  • 用户能借助辅助技术更高效地在应用中导航。

  • 应用符合 Web 无障碍标准,提升可用性。

在 Flutter Web 中使用 SemanticsRole

#

Flutter 提供 Semantics widget 与 SemanticsRole enum,让开发者为 widget 分配特定角色。Flutter Web 应用渲染时,这些 Flutter 特有角色会转换为网页 HTML 结构中对应的 ARIA 角色。

1. 标准 widget 的自动语义

许多标准 Flutter widget(如 TabBar、MenuAnchor、Table)会自动包含语义信息及其角色。尽可能优先使用这些标准 widget,它们开箱即用地处理许多无障碍方面。

2. 显式添加或覆盖角色

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

以下示例演示如何显式定义列表及其列表项:

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.'),
            ),
          ),
        ],
      ),
    );
  }
}