DEV Community

Mubarak Basha
Mubarak Basha

Posted on • Originally published at proandroiddev.com on

Building Accessible Android UIs with Jetpack Compose

Building Accessible Android UIs with Jetpack Compose
IMG Credit (Mubarak Basha)

In this blog, we are going to learn the importance of accessible UI and practical ways to make your app accessible in Compose for users with disabilities, with complete, real code examples. Let’s Begin.

Why Accessibility Matters

More than a billion, or 1 in 6 of us people live with some form of disability. Accessible apps support:

  • Screen reader users (TalkBack)
  • Motor-impaired users (who use switch devices)
  • Color blind and low-vision users

Compose brings built-in support for accessibility with testing using various API’s, like Semantics, TalkBack, making this faster and seamless.

Semantics: Providing Meaning to UI Elements

Accessibility services such as screen readers rely on semantic information to interpret UI components correctly. It is important to provide meaningful descriptions rather than relying solely on visual things.

Icon(
    imageVector = Icons.Default.Home,
    contentDescription = "Navigate to home screen"
)
Enter fullscreen mode Exit fullscreen mode

SettingcontentDescription to null to interactive elements (unless they are purely decorative items) leaves assistive technologies without contextual information, impairing the experience of users with disabilities.

Note: Most of the Material Compose Components like Checkbox_,_ RadioButton_,_ Switch_,_ Slider_, and_ Surface implement accessibility by default and come with the built-in semantics, but based on our use case, we might want to do it manually. Even for that, compose has a range of useful API’s

Custom Composables and Accessibility Roles

When creating custom interactive UI components, explicitly define their semantic roles (UI Meanings) and content descriptions to ensure they are announced properly. Jetpack Compose’s semantics modifier is key here (see merging and clearing semantics).

Box(
    modifier = Modifier
        .clickable { /* Handle upload action */ }
        .semantics {
            contentDescription = "Upload file"
            role = Role.Button
        }
        .padding(16.dp)
) {
    Icon(Icons.Default.CloudUpload, contentDescription = null)
    Text("Upload")
}
Enter fullscreen mode Exit fullscreen mode

This approach allows TalkBack and similar tools to identify the composable as a button with the specified action.

Adequate Touch Target Size

Provide Adequate Touch Target Size
IMG Credit: (Developer Docs) Touch Target Size

Ensure touch targets meet the recommended minimum size of 48x48dp for comfortable interaction, following Android accessibility design guidelines (Material Accessible Design).

Button(
    onClick = { /* Handle click */ },
    modifier = Modifier.sizeIn(minWidth = 48.dp, minHeight = 48.dp)
) {
    Icon(Icons.Default.Favorite, contentDescription = "Like")
}
Enter fullscreen mode Exit fullscreen mode

This improves accessibility for users with motor impairments.

Managing Focus with Keyboard and Assistive Devices

Not all users interact via touch. Keyboard navigation and switches require explicit focus order management, which Compose supports via focusRequesterand focusOrder (detailed in the focus traversal order docs).

val focusRequester1 = remember { FocusRequester() }
val focusRequester2 = remember { FocusRequester() }

TextField(
    value = username,
    onValueChange = { username = it },
    label = { Text("Username") },
    modifier = Modifier
        .focusRequester(focusRequester1)
        .focusOrder { next = focusRequester2 }
)

TextField(
    value = password,
    onValueChange = { password = it },
    label = { Text("Password") },
    modifier = Modifier.focusRequester(focusRequester2)
)
Enter fullscreen mode Exit fullscreen mode

This code ensures a logical order of focus between inputs.

Maintaining Adequate Color Contrast

Maintain Adequate Color Contrast
IMG Credit: (Developer Docs) Increase text visibility

Using your theme’s color scheme to maintain appropriate contrast ratios in both light and dark modes, as emphasized by Material Design accessibility standards.

Text(
    text = "Welcome",
    style = MaterialTheme.typography.titleLarge,
    color = MaterialTheme.colorScheme.onBackground
)
Enter fullscreen mode Exit fullscreen mode

Proper contrast improves readability for users with visual impairments.

Testing Accessibility

Use both automated checks and manual tools for testing accessibility:

@get:Rule
val composeTestRule = createComposeRule()

@Before
fun setup() {
    AccessibilityChecks.enable()
}
Enter fullscreen mode Exit fullscreen mode
  • Manual testing tools:
  1. TalkBack

  2. Accessibility Scanner

  3. Switch Access

Reference

Conclusion

In this quick blog, we learned the importance of accessible UI and also learned its practical uses with an example_._ I hope you will like this article. If so, please like👏 this story and share it with your friends and family, don’t forget to follow me. And I will see you in the next upcoming article with an interesting topic.

Signing off, Mubarak Basha

LinkedIn, GitHub, X


Top comments (0)