Web accessibility
Information about web accessibility
Background
#Flutter supports web accessibility by translating its internal Semantics tree into an accessible HTML DOM structure that screen readers can understand. Since Flutter renders its UI on a single canvas, it needs a special layer to expose the UI's meaning and structure to web browsers.
Opt-in web accessibility
#Invisible button
#
For performance reasons, Flutter's web accessibility is not on by default.
To turn on accessibility, the user needs to press an invisible button with
aria-label="Enable accessibility".
After pressing the button, the DOM tree will reflect all accessibility
information for the widgets.
In embedded or multi-view web apps on mobile browsers, Flutter scopes this
placeholder button to each Flutter view so it doesn't block taps on the
surrounding HTML page.
Turn on accessibility mode in code
#An alternative approach is to turn on accessibility mode by adding the following code when running an app.
import 'package:flutter/semantics.dart';
void main() {
runApp(const MyApp());
if (kIsWeb) {
SemanticsBinding.instance.ensureSemantics();
}
}
Enhancing Accessibility with Semantic Roles
#What are Semantic Roles?
#Semantic roles define the purpose of a UI element, helping screen readers and other assistive tools interpret and present your application effectively to users. For example, a role can indicate whether a widget is a button, a link, a heading, a slider, or part of a table.
While Flutter's standard widgets often provide these semantics automatically, a custom component without a clearly defined role can be incomprehensible to a screen reader user.
By assigning appropriate roles, you ensure that:
- Screen readers can announce the type and purpose of elements correctly.
- Users can navigate your application more effectively using assistive technologies.
- Your application adheres to web accessibility standards, improving usability.
Using SemanticsRole in Flutter for web
#
Flutter provides the Semantics widget
with the SemanticsRole enum
to allow developers to assign specific roles to their widgets. When your
Flutter web app is rendered, these Flutter-specific roles are translated into
corresponding ARIA roles in the web page's HTML structure.
1. Automatic Semantics from Standard Widgets
Many standard Flutter widgets, like TabBar, MenuAnchor, Slider,
RangeSlider, and Table, automatically include semantic information along
with their roles:
-
Sliders:
SliderandRangeSlider(usingSemanticsFlag.isSlider) and custom widgets withSemanticsRole.sliderrender a focusable<input type="range" role="slider">element with synchronizedaria-label,aria-valuenow,aria-valuetext, and keyboard arrow navigation. -
Landmarks and menus: Named routes with
scopesRoute: true,namesRoute: true, and a non-emptylabelemitrole="region"landmarks, and scrollable menus (MenuAnchor,DropdownMenu) expose direct item counts to screen readers without intermediategroupwrappers.
In addition, the web engine preserves DOM focus across semantic role changes
without scrolling, restores focus when dialogs or modal routes close in
WidgetsApp.router apps, and dismisses visible Tooltip widgets when the user
presses Escape.
Whenever possible, prefer using these standard widgets as they handle many accessibility aspects out-of-the-box.
2. Explicitly adding or overriding roles
For custom components or when the default semantics aren't sufficient,
use the Semantics widget to define the role:
Here's an example of how you might explicitly define a list and its items:
import 'package:material_ui/material_ui.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.'),
),
),
],
),
);
}
}
Viewport zoom and mobile text inputs
#
By default in full-page mode, Flutter configures the page's
<meta name="viewport" flt-viewport> tag with maximum-scale=5.0
and without
user-scalable=no. This allows users to pinch-to-zoom up to 500% to meet
WCAG 2.1 AA text-resizing guidelines.
On iOS Safari, Flutter also automatically sets a minimum 16px font size on
its hidden semantic <input> and <textarea> elements so the browser doesn't
unexpectedly auto-zoom when a user focuses a text field.
Unless stated otherwise, the documentation on this site reflects Flutter 3.47. Page last updated on 2026-10-23. View source or report an issue.