As developers, product owners and project stakeholders, we want to assure that our applications are available and usable to a larger demographic. As a result, there’s a key requirement for any product to maintain a focus on accessibility. This is traditionally thought of as making our applications usable by people with disabilities and special requirements by facilitating the use of auditory and visual support tools, such as screen readers, voice control, high-contrast display settings, enlarged text settings, etc. Access to information and communications technologies is defined as a basic human right in the United Nations Convention of the Right of Persons with Disabilities, and we must provide equal access to all people.

A lot of different people use applications for a lot of different things!

From a technical point of view, making our applications accessible means optimizing our user interfaces to be used by semantic analysis software such as search engines and various accessibility tools. People may require accessibility tools for many different reasons, and it’s our mission to provide these tools with easy-to-understand, structured and logically structured information. With initiatives like Apple’s upcoming voice control in iOS 13.0 and Google’s upcoming Live Caption, platforms are evolving their accessibility tooling, and as application publishers, we need to evolve as well.

Built-in support 🏡

More often than not, developers need to tune their interfaces to support accessibility tools. However, Flutter has some great built-in features to help you from the start:

Large fonts — Android, iOS and most browsers contain system settings to configure the desired font sizes used by apps. Flutter text widgets respect this setting when determining font sizes.

Android, iOS and most browsers contain system settings to configure the desired font sizes used by apps. Flutter text widgets respect this setting when determining font sizes. Semantic definitions — Flutter automatically provides basic semantic information about different widgets. For example, buttons are labeled as ‘button’, tabs in tab bars are labeled ‘tab x of y’ and all basic semantic features are available without any further work.

Flutter automatically provides basic semantic information about different widgets. For example, buttons are labeled as ‘button’, tabs in tab bars are labeled ‘tab x of y’ and all basic semantic features are available without any further work. Contrast — Sufficient color contrast makes text and images easier to read. While benefitting users with visual impairments, this can in practice help all users when viewing an interface in extreme lighting conditions, such as the screen being exposed to direct sunlight or dim displays. By default, the ThemeData widget adjusts colors automatically to provide sufficient color contrast between backgrounds and foregrounds.

Sufficient color contrast makes text and images easier to read. While benefitting users with visual impairments, this can in practice help all users when viewing an interface in extreme lighting conditions, such as the screen being exposed to direct sunlight or dim displays. By default, the ThemeData widget adjusts colors automatically to provide sufficient color contrast between backgrounds and foregrounds. Semantic debugger — This is a widget that visualizes the semantics for its child widget. By default, your MaterialApp or WidgetsApp includes one for its child, which we can enable using the showSemanticDebugger property.

An example of the semantic debugger using a tab bar

There are also additional ease-of-use widgets like Tooltip that provides extra information for all kinds of users. In addition to this, there are some automated tools we can use for debugging our accessibility performance manually or in a CI/CD pipeline. For Android, we can use the Accessibility Scanner, and on iOS, we can use the Accessibility Inspector tool in Xcode. There’s also some cross-platform for both mobile and web, such as axe, which Google’s Lighthouse is built upon.

Accessibility widgets 🔮

In Flutter, there are some core widgets we’ll often use when building accessible applications. These are well documented, but here’s a list of the basic capabilities:

Semantics — A widget that annotates the widget tree with a description of the meaning of the widgets. This is the most common type of accessibility widgets, and its job is well defined. We can use this widget to annotate any widget tree, like annotating buttons with context and description. Just wrap it in a Semantics widget! This is particularly helpful for people with visual impairments, as it provides screen readers and semantic analysis software with more meaningful information. In addition to this, the Semantics widget provides a lot of useful properties, callbacks, and custom accessibility actions, allowing us to tailor our application for screen readers. When defining custom widgets, we should always be sure to wrap them in a Semantic widget with the appropriate setup for screen reader navigation, gestures, hints, cursors, etc.

A widget that annotates the widget tree with a description of the meaning of the widgets. This is the most common type of accessibility widgets, and its job is well defined. We can use this widget to annotate any widget tree, like annotating buttons with context and description. Just wrap it in a Semantics widget! This is particularly helpful for people with visual impairments, as it provides screen readers and semantic analysis software with more meaningful information. In addition to this, the Semantics widget provides a lot of useful properties, callbacks, and custom accessibility actions, allowing us to tailor our application for screen readers. When defining custom widgets, we should always be sure to wrap them in a Semantic widget with the appropriate setup for screen reader navigation, gestures, hints, cursors, etc. ExcludeSemantics — A widget that drops all the semantics of its descendants. This can be useful if some widgets otherwise would be reported but would be confusing to the user. An example of how some of the built-in widgets in Flutter uses it is the Chip widget, which hides the avatar since it is redundant with the chip label. The same can apply to an image with a caption, where the semantics of the image would be redundant.

A widget that drops all the semantics of its descendants. This can be useful if some widgets otherwise would be reported but would be confusing to the user. An example of how some of the built-in widgets in Flutter uses it is the Chip widget, which hides the avatar since it is redundant with the chip label. The same can apply to an image with a caption, where the semantics of the image would be redundant. MergeSemantics — A widget that merges the semantics of its descendants. This can be useful when two separate semantic widgets would be better understood in the context of one another. An example of this could be a setting toggle with both a text label and a checkbox. Instead of the accessibility tools interpreting these as two separate nodes, we can merge the label from the Text widget with the “checked” semantic state of the checkbox. This way, the user will know that they are related.

A widget that merges the semantics of its descendants. This can be useful when two separate semantic widgets would be better understood in the context of one another. An example of this could be a setting toggle with both a text label and a checkbox. Instead of the accessibility tools interpreting these as two separate nodes, we can merge the label from the Text widget with the “checked” semantic state of the checkbox. This way, the user will know that they are related. BlockSemantics — A widget that drops the semantics of all widgets that were painted before it in the same semantic container. This is useful for hiding unwanted semantics from accessibility tools, e.g. an alert or dialog should usually disallow interaction with any widget located “behind” the alert (even though they are still partially visible).

The use and combination of these built-in widgets can help your application reach out to a group of people who otherwise would have a hard time with usability.

Shifting left on accessibility ⬅️

We’ve now gone through some basic accessibility features in Flutter. While built-in features and tools can help a long way, an effective way of making our applications accessible is introducing accessibility earlier in the development cycle. As the importance of accessibility increases, companies and organizations are expressing more interest in how they can leverage automated testing tools to improve accessibility performance, reduce costs, track compliance and mitigate risks. Establishing a pipeline in which accessibility features are built and tested early in the development cycle allows for early detection of accessibility issues before they hit production, produces higher quality accessibility features, increases efficiency and reduces the costs of maintaining an accessible application.

Summary 🎉

There’s no denying that both web- and mobile applications are increasingly important resources in many aspects of life, like education, employment, commerce, health care, recreation and more. These applications must be accessible and provide equal access and equal opportunity to people with diverse disabilities. There is also a strong business case for accessibility. Accessible design improves overall user experience and satisfaction especially in a variety of situations, across different devices, and for older users. In short, accessibility can enhance your brand and extend your market reach. In addition to the user experience, accessibility is required by law in some situations, which further drives the importance of accessible applications.

Flutter provides built-in tooling and features that enhance the debugging and development process of accessible applications. This comes on top of the already existing accessibility tooling that each respective platform provides, which overall makes accessibility development a pain-free experience. The feature-rich accessibility widgets provided in Flutter has different options that allow for a tailored experience. The modular and extensible nature of widgets in Flutter allows for easy separation, merging and blocking of semantics, which can enable us to develop accessible applications. It is also worth noting that these tools, features, and widgets will be of the same value when targeting web with Flutter in the future. Let’s not forget the diversity of users. Start incorporating these features earlier in development, and aim for the highest quality accessibility experience possible!