Search code examples
androidandroid-jetpack-composeandroid-tvandroid-jetpack-compose-canvasandroid-jetpack-compose-tv

Unable to Focus Anything other than TextField


I have already viewed other posts on the site. They suggest using the focusRequestor modifier and I've tried that.

val scope = rememberCoroutineScope()
val focusRequester = remember { FocusRequester() }
Text(
    modifier = Modifier
        .focusable()
        .focusTarget() //Tried Adding/Removing
        .onFocusChanged {
            if (it.hasFocus || it.isFocused) {
                Log.i("Test", "Text Focused")
            }
        } // Tried Adding Visual Elements to delete it as well.
        .focusRequester(focusRequester),
    text = "MARSK"
)

LaunchedEffect(scope) {
    focusRequester.requestFocus()
}

I'm taking Text in this example, but actually I need to implement this on Canvas. This approach is not working on Box too. I'm afraid the same is the case for other containers as well.

For background, this is being built for my TV, so I wish to make this element clickable by the OK button on the remote's D-Pad. My approach is to make it focused first, then assuming that once it is focused, it will automatically detect the press of the OK button as the 'click'. If there is something wrong with my approach, feedback for improvements is also welcome.

PS: The solution by Abhimanyu works like a charm on Mobile Devices. However, since I mentioned a TV above, a TV is the device in consideration. On the TV, I have to press a button (Any button on the DPAD works, even the OK button, strangely) to get it focused up. Any idea how to get round this issue?

Thanks


Solution

  • Issue
    The onFocusChanged should be added BEFORE the focusTarget or focusable that is being observed.

    Source

    Changes

    1. Remove focusTarget and move onFocusChanged before focusable.
    2. Also, note that focusRequester must be before focusable.

    This would hopefully work for all comopsables. I have tested using Text and the example from Docs was using Box.

    Extra details.

    1. Prefer focusable over focusTarget.

      From focusTarget docs,

    Note: This is a low level modifier. Before using this consider using Modifier.focusable(). It uses a focusTarget in its implementation. Modifier.focusable() adds semantics that are needed for accessibility.

    1. Order of Modifiers matter
      Layouts in Jetpack Compose Codelab
      Check Order Matters section

    order matters when chaining modifiers as they're applied to the composable they modify from earlier to later,

    Sample code

    @Composable
    fun FocusableText() {
        val scope = rememberCoroutineScope()
        val focusRequester = remember { FocusRequester() }
        var color by remember { mutableStateOf(White) }
        LaunchedEffect(scope) {
            focusRequester.requestFocus()
        }
        Text(
            modifier = Modifier
                .background(color)
                .onFocusChanged {
                    color = if (it.hasFocus || it.isFocused) {
                        LightGray
                    } else {
                        White
                    }
                }
                .focusRequester(focusRequester)
                .focusable(),
            text = "Test Text"
        )
    }
    

    The Text background is LightGray which indicates the text is focused.

    Screenshot