How to move focus from Compose Button to Compose TextField by using the arrow key?

Solution 1:

After testing I see that onKeyEvent will fired 2 times after press a key on Button (1 for ACTION_DOWN and 1 for ACTION_UP). Maybe it cause the focus move twice when press a key.
Then I think you can handle focus on your Button like

val focusRequester = remember { FocusRequester() }
var buttonColor by remember { mutableStateOf(Black) }
OutlinedButton(
    onClick = { focusRequester.requestFocus() }, modifier = Modifier
        .focusRequester(focusRequester)
        .onFocusChanged { buttonColor = if (it.isFocused) Green else Black }
        .focusTarget()
        .onKeyEvent { keyEvent ->
            if (keyEvent.nativeKeyEvent.action == ACTION_UP) {
                when (keyEvent.nativeKeyEvent.keyCode) {
                    KEYCODE_DPAD_DOWN -> {
                        focusManager.moveFocus(FocusDirection.Down)
                        true
                    }
                    KEYCODE_DPAD_UP -> {
                        focusManager.moveFocus(FocusDirection.Up)
                        true
                    }
                    else -> {
                        false
                    }
                }
            } else {
                true
            }
        }
) {
    Text("Button", color = buttonColor)
}

Complete example

@Composable
fun Screen() {
    Column(
        modifier = Modifier.fillMaxSize(),
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        val focusManager = LocalFocusManager.current

        FocusMoveTextField(focusManager)
        FocusMoveTextField(focusManager)
        FocusMoveTextField(focusManager)

        val focusRequester = remember { FocusRequester() }
        var buttonColor by remember { mutableStateOf(Black) }
        OutlinedButton(
            onClick = { focusRequester.requestFocus() }, modifier = Modifier
                .focusRequester(focusRequester)
                .onFocusChanged { buttonColor = if (it.isFocused) Green else Black }
                .focusTarget()
                .onKeyEvent { keyEvent ->
                    if (keyEvent.nativeKeyEvent.action == ACTION_UP) {
                        when (keyEvent.nativeKeyEvent.keyCode) {
                            KEYCODE_DPAD_DOWN -> {
                                focusManager.moveFocus(FocusDirection.Down)
                                true
                            }
                            KEYCODE_DPAD_UP -> {
                                focusManager.moveFocus(FocusDirection.Up)
                                true
                            }
                            else -> {
                                false
                            }
                        }
                    } else {
                        true
                    }
                }
        ) {
            Text("Button", color = buttonColor)
        }
        FocusMoveTextField(focusManager)
        FocusMoveTextField(focusManager)
    }
}

@Composable
private fun FocusMoveTextField(
    focusManager: FocusManager
) {
    OutlinedTextField(
        modifier = Modifier
            .fillMaxWidth()
            .onKeyEvent { keyEvent ->
                when (keyEvent.nativeKeyEvent.keyCode) {
                    KEYCODE_DPAD_DOWN -> {
                        focusManager.moveFocus(FocusDirection.Down)
                        true
                    }
                    KEYCODE_DPAD_UP -> {
                        focusManager.moveFocus(FocusDirection.Up)
                        true
                    }
                    else -> {
                        false
                    }
                }
            },
        value = "",
        onValueChange = {},
        keyboardOptions = KeyboardOptions(
            keyboardType = KeyboardType.Number,
            imeAction = ImeAction.Done
        )
    )
}