Let’s start by checking out the constructor:You can see the standard constructor for Semantics adds a lot of properties when it extends its base class, the SingleChildRenderObjectWidget.
Its other constructor, Semantics.
fromProperties, needs a required SemanticsProperties object called “properties”.
According to the docs, if you want to make your Semantics object constant then this is the way you want to go.
Properties in the SemanticsProperties class are used to generate a SemanticsNode in the tree; but we’ll get back to that later.
We want to take the time to really understand these properties because that’s what’s going to allow us to most effectively implement accessibility in our apps and create the best experiences for our users.
Let’s check out the table below.
Keep in mind that these properties are null by default.
The explanations have been written in a way that I hope is more easily understood by everyone.
As you see, it gives us a lot of ways to describe the related widget.
Let’s take an example from the Flutter SDK of how the Flutter team used SemanticsProperties.
Here are the Semantics of a ListTile widget.
You can think of the ListTile as a List Item; it’s an individual item inside of a list like a single tweet in Twitter’s home screen.
Let’s go over this and see what it does for our users.
First of all, we can see that we don’t need to create a SemanticsProperties object separately (although we can create one by using thefromProperties named constructor of the Semantics class).
We can also pass some status information about the widget while creating it.
We’re able to see enabled and selected flags getting triggered with the values that were defined within the widget’s constructor.
If we create a ListTile now, it will read the text inside of it aloud, and tell us if it’s enabled or disabled / selected or not selected.
We can dynamically set the values for each ListTile in order to create custom semantics for each individual tile:The code snippet above will create a list view with 5 elements and disable all of them except the second element and, it will set the selected state of the first element to true.
When we run the app with the screen reader enabled (I will run Android’s TalkBack now), it will say: “Selected main title for 0 item, sub title for 0 item disabled”.
As you see, this gives the user the information we provided about the each item.
But of course, we need to test the other cases to be sure it is working.
If we click once on the second item, we will hear : “Main title for 1 item, sub title for 1 item”.
Since our second element is not selected and enabled we can be sure that it is working correctly too.
With TalkBack, one click causes the screen reader but it takes a double tap to actually trigger the onTap.
Let’s run the third item to test the last case.
We will hear now “Main title for 2 item, sub item for 2 item disabled”.
Since it is not selected and it is disabled, it checks out and we can be sure that it is working correctly.
Now that we have a basic understanding of Semantics and how to create them, let’s take it up a notch.
But, before doing that, let’s learn about the concept that we pinned earlier: SemanticsNode.
As we stated above, when we create our widget tree we create a Semantics tree along with it, and this tree is what gets used by screen readers.
In the programming world, a tree is a data structure consists of node and leaf.
In our case, the SemanticsNodes will be our nodes.
Each SemanticsNode is a node that represent semantic data.
A node might cover semantic data for one or several widgets.
Each SemanticsNode will have some values that can be triggered by the SemanticsAction.
: SemanticsProperties has parameters called increasedValue and decreasedValue for increase and decrease actions.
It also has a key for identifying it in the list of nodes.
These are used during the tree deconstruction to get the correct node during a rebuild.
There is also anid value for identification .
id is 0 for the root node, and this value is auto-generated as we create child nodes.
Besides this, we can also find out information about the node and its relationship with the other nodes.
We can check if it’s merging with other nodes at any given moment with theisPartOfNodeMerging flag.
Or, we can check if it’s already been merged with isMergedIntoParent.
If one widget has multiple children that each have their own node, we can use mergeAllDescendantsIntoThisNode to merge all of those nodes into a single node.
Now that we have a better understanding of the SemanticsNode, SemanticsProperties and Semantics, we can create our own custom Semantics.
With the code above, we’re using the semantics label to describe each Container that is being used in the ListView.
Each is a red box with 200 height and 200 width.
We’ll keep the enable and selected values from the example before.
We’ll add more controls, though.
We’ll create an onTap callback for double clicks and onScrollDown to test the gestures.
In general, our application will show us a Snackbar that says: “Item <related position> Clicked!”.
If onTap is triggered or when you scroll down (by swiping first left then right on Android), it will create a log entry stating showing the callback was triggered.
So far it’s been super cool to see how all this works, but as we’ve gone deeper we’ve come up with more and more questions.
What happens when we want to merge multiple semantics into one, or if we don’t want to include certain semantics information to the user?Don’t worry about it, Flutter’s got you covered.
You can merge the semantics of your widget’s descendents withMergeSemantics, and you can even exclude some of them by using ExcludeSemantics.
In addition to these Flutter comes with even more widgets for semantics, such as BlockSemantics and IndexedSemantics .
Let’s check them out.
For that, I want to extend the example we’ve been using:We’ve changed the code a little bit.
We added MergeSemantics as our root.
This means it merges all the available child semantics into one, and the screen reader will handle all of them at once.
Also, we put a Column with four children inside of our Container.
In the second child in the list item, meaning the second Container, you can see we have used BlockSemantics.
Therefore, the widgets before this node will be omitted and not read by the screen readers.
In the third child in the list item, there is also ExcludeSemantics.
The child widget of this semantics widget will not be part of the semantics tree.
Let’s run the application and click the first element.
The screen reader should say, “Selected Container with 200 width 200 height and red background second inside text of item 0 fourth inside text of item 0 disabled.
” As you can see, it gathered all of the semantics into one, while excluding the child that we do not want to share.
We’re still missing one Semantics that we talked about.
IndexedSemantics helps us keep track of the relevant information that’s passed to accessibility screen readers.
For example, with a ListView it will create an IndexedSemantics for each individual element.
But in a ListView, we might have some elements that have zero use-cases.
, we might have dividers in the list and these have no use-case other than visual representation.
To prevent the dividers from being read to the user, we might use IndexedSemantics like this:For this example, accessibility tools will only consider the elements with IndexedSemantics while they read it out loud or jump between elements.
ConclusionAccessibility is an important topic that should never be neglected.
We should always take it into account, ensuring that we add accessibility to our applications and make them available to everyone who uses a smartphone.
We can make a lot of people’s lives easier with just a little extra effort.
Since the Flutter team has already implemented Semantics in most widgets, that makes it much easier for us.
But, when we create something custom, we should always be sure to add Semantics for it.
Remember, every person deserves to be able to use your application; so help them to use it!Thank you and see you soon!P.
If you want to discuss this please send me a DM on Twitter or leave a comment here.
Also, I would like to thank Norbert for proofreading and Scott Stoll for spending a lot of time helping me out with proper English grammar :).