Mobile Tabs Part 3 – Flutter

Tabs on Flutter

If you haven’t read Parts 1 and 2, I recommend doing so since we’re going to be building on prior knowledge from those as we dive into Flutter.

Flutter is fairly new in the grand scheme of cross platform frameworks for mobile apps. Version 1.0 was released in late 2018, and I only started hearing about it within the last year or so. Flutter works very different to React Native with respect to how views are created on the screen. With React Native, views are highly customized view classes that already exist on the platform. With Flutter on Android, all of the views are painted on a surface or canvas layer. If you look at a Flutter app with the Android Layout Inspector, you will see a single view named FlutterSurfaceView. iOS is a little different in that you can actually see a view hierarchy if you use something like Appium. This may be due to XCUITest triggering Flutter to build a view hierarchy though.

With all that being said, Flutter is different with how it displays views, but it does build an accessibility tree for assistive technology on both iOS and Android. But before we get too far into this, it is important to highlight that Full Keyboard Access (FKA) on Flutter does not exist. If you are planning on building an app using Flutter, FKA users will not be able to use your app. There has been an open issue for FKA support since 2021.

We’ll be using the same baseline that we used in Part 2 for React Native. The baseline apps that we used are the Apple App Store and the Google Play Store. We’re looking for a few things, but as before, the order doesn’t matter since each platform will do its own thing on the order that the announcements are made.

  1. Name
  2. State
  3. Role
  4. Contextual information

Implementation of Tabs

I’ll cut right to the chase. Implementing accessible tabs on both platforms is not an easy task (depending on the solution you take) because it’s just not supported within the base API. There is an open issue in the Flutter GitHub for adding proper semantics to tab controls. However, we can take a look at how things are working and some possible solutions until the team adds the necessary semantics.

The default way to implement tabs in Flutter is to use a TabBar with the tabs defined using the tabs property.

const TabBar(
tabs: <Tab>[
Tab(text: 'Home'),
Tab(text: 'Settings'),
],
…
)

There’s a lot more that actually goes into implementing tabs in Flutter, but for our purposes, we’re only worried about the tabs themselves. This will create two tabs in a tab bar named “Home” and “Settings”.

Let’s take a look at how screen readers are announcing the tabs.

Android

On Android, the selected tab is announced as “selected. Home Tab 1 of 2”. That satisfies our previous requirements for a name, state, role, and contextual information.

iOS

Now let’s look at iOS. The selected tab is announced as “selected, Home Tab 1 of 2”. However, if you have VoiceOver captions turned on, you’ll notice something weird: there is no space between “Home” and “Tab”.

Let’s Inspect

The behavior is weird, so let’s take a look at what’s going on under the hood. For Android, the accessibility tree is reporting the Home tab as follows:

  • Role: View
  • Content description: Home Tab 1 of 2
  • Actions: click, a11y focus
  • Properties: focusable, selected, clickable
  • Size: 540 x 105 px

Well that’s no good. Its dumping all of the information that we need into the content description except for the selected state.

Let’s see what iOS is doing:

  • Label: Home\nTab 1 of 2
  • Value: None
  • Traits: Selected
  • Identifier: None
  • Hint: None
  • User Input Labels: Home\nTab 1 of 2
Values showing in XCode Accessibility Inspector

It’s the same deal with iOS. The information that we’re looking for is being put into the accessibility label, except for the selected state. The “\n” in the label properties denotes a new line character which isn’t a new concept if you’re familiar with writing code. However, this is the culprit for the lack of a space in the VoiceOver captions that I mentioned earlier. VoiceOver announces it correctly, but captions strip this character out, so it appears as a single line. Braille displays may be a different story though. I don’t read braille so this may appear as two lines using a Braille display.

Flutter Semantics

Before we get too far into a rabbit hole, it is important to understand how semantics work within Flutter. Semantics, as in name, role, and value, can be applied either by using the corresponding built-in widget such as a TextButton or by using the Semantics widget to apply a name, role, or value to a particular widget.

If we look in the Flutter Semantics documentation, we can see that tabs are nowhere to be seen. This only leaves us to use the built-in tab widget to produce the proper semantics, but we’ve already been down that road and it doesn’t work. Sure, it announces what we’re looking for, but it’s applying almost everything in the accessible name which is not good for robustness and will likely cause issues for assistive technology. It’s also worth noting that the lack of tab semantics has been an open issue since July 2022. So, what are we to do?

Workarounds

There are two solutions. One is difficult, the other is less difficult, and both are related to each other. The difficult workaround is to build your own native tab views for iOS and Android and inject those into the Flutter app. The less difficult workaround is to use a community package. The native_tab_bar package will create tabs with the correct semantics for each platform and just requires a package to be imported into your project. I have tested the native_tab_bar package and it behaves exactly like the tabs in the Apple App Store and the Google Play Store with the name, role, and value applied in the correct places.

Is Flutter Viable for Accessible Apps?

This leaves us with the question of whether or not we can use Flutter to create accessible apps. Given the current state of Full Keyboard Access with no activity in over a year and the lack of support for implementing accessible controls such as tab controls without relying on community packages, I would say not just yet. Flutter has more growing to do in the accessibility department before it can be fully utilized by users of assistive technology.

Your Experiences

I hope this three-part series helped to shed a little light on native, React Native, and Flutter tab controls. However, there are many other frameworks out there that allow developers to write apps for multiple platforms with a single codebase, and this series just scratches the surface. What’s your experience been with the frameworks covered in the series as well as ones not covered in the series? Tell me in the comments.

Categories: Technical

About John Lilly

John is an accessibility engineer specializing in mobile accessibility. He started his career making accessible apps for iOS and Android. He has a passion for tinkering and hacking mobile devices in order to better understand how the accessibility services function and make tools to help diagnose accessibility issues. When he's not doing that, you'll probably find him hiking, backpacking, or hiding somewhere in the mountains away from large crowds.

Comments

Add Your Comment