software-mansion / software-mansion/react-native-screens

[Android] Accessibility: Keyboard focus cleared during navigation with native-stack

Open
#3,147 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

missing-repro platform:android
Dominant language
TypeScript
Stars
3.7k
Forks
714
Avg merge
2d 23h
Merged PRs (30d)
71

Description

Description

I’m building an accessible audiobook app designed for blind users. Navigation is intentionally simple to match what they’re used to on PC, and I need to support not only iOS and Android touch devices, but also Android phones with hardware keypads (e.g. Qin F22 Pro, SmartVision 3) and Android TVs.

While testing with native-stack, I’ve run into several keyboard focus issues (it also have some of its own issues , i posted there too with a video):

  1. Focus cleared before navigating

When using native-stack, react-native-screens clears keyboard focus before navigation happens and talkback focus is all over because of it.
If I disable clearFocus() , the issue disappears (though I’m not sure what other side effects this might cause and slide_from_bottom still gets somehow bugged with talkback focus , slide_from_left , default and fade seems to work):

override fun clearFocus() {
    if (visibility != INVISIBLE) {
        // super.clearFocus()
    }
}

With stack (non-native) navigation, I don’t have this problem, but then I can’t restore keyboard focus to the last item when going back.

  1. goBack / hardware back button clears focus too early

When calling navigation.goBack() (or pressing the hardware back button, which triggers it internally), the focus is cleared before the screen is removed.

This causes TalkBack to first focus on the disappearing screen, and then move focus to the destination screen, which is confusing for blind users.

I tested by delaying removeScreenAt:

override fun removeViewAt(parent: ScreenStack, index: Int) {
println("REMOVING SCREEN BEFORE ACTION")
val screen = parent.getScreenAt(index)

screen.postDelayed({
    println("REMOVING SCREEN")
    prepareOutTransition(screen)
    parent.removeScreenAt(index)
    NativeProxy.removeScreenFromMap(screen.id)
}, 5000)

}

I also tried the Android 13+ back dispatcher, which works correctly:

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
requireActivity().onBackInvokedDispatcher.registerOnBackInvokedCallback(
OnBackInvokedDispatcher.PRIORITY_DEFAULT
) {
dismissFromContainer()
}
}

Unfortunately, most of my target devices are still on Android 12, so I can’t rely on this.

  1. Modals / transparent modals

When using modals or transparent modals, keyboard focus incorrectly moves to the background screen, instead of staying within the modal.
I haven’t debugged this yet, but it currently makes modals unusable with a hardware keyboard.
I think its a timing issue.

Summary

react-native-screens clears keyboard focus too early when navigating, especially on goBack.

This causes major issues for screen reader and keyboard users on Android. (i have not tested on IOS)

Disabling clearFocus() works for navigating forward, but may have side effects.

The Android 13+ back dispatcher fixes the issue when going back, but older versions (common on keypad phones) are still affected.

Expected behavior:

Keyboard focus should remain stable across navigations.

Going back should restore focus to the previously focused element on the destination screen.

Modals should trap focus inside themselves.

Some type of animations have big problems with talkback , "fade" seem to play nice. (you can test with slide_from_bottom and talkback for example , with and without keyboard input)

Steps to reproduce

I already wrote in the description

Snack or a link to a repository

not needed

Screens version

4.13.1 and 4.14.0

React Native version

0.79 and 0.81

Platforms

Android

JavaScript runtime

Hermes

Workflow

Expo bare workflow

Architecture

None

Build type

None

Device

None

Device model

2 x Android 12 devices + emulators different versions

Acknowledgements

Yes

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reproducing the native-stack behavior on Android 12 with TalkBack and hardware-keyboard focus. Read the focus handling around clearFocus(), removeViewAt(), and removeScreenAt(), comparing it with the Android 13+ back dispatcher example. Done means focus remains stable during navigation, back navigation avoids focusing the disappearing screen, and modal focus stays within the modal.

Written by the indexing model from the issue text.

Assessment

Tech stack
android, kotlin, react-native
Domain
accessibility, mobile
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
32/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.