androidandroid-jetpack-composeandroid-jetpack-compose-text

How to clear TextField focus when closing the keyboard and prevent two back presses needed to exit app in Jetpack Compose?


I'm using BasicTextField.

When I start editing, back button becomes hide keyboard button(arrow down).

First press on back button hides keyboard, but the focus is still on the text field. Both onFocusChanged and BackPressHandler handlers not getting called.

Second press on back button clears focus: onFocusChanged is called and BackPressHandler is not.

BackHandler {
    println("BackPressHandler")
}
val valueState = remember { mutableStateOf(TextFieldValue(text = "")) }
BasicTextField(
    value = valueState.value,
    onValueChange = {
        valueState.value = it
    },
    modifier = Modifier
        .fillMaxWidth()
        .onFocusChanged {
            println("isFocused ${it.isFocused}")
        }
)

Third time BackHandler works fine. Just used it for testing, I shouldn't be needed it here, it expected focus to get lost after first back button tap


Solution

  • There's a compose issue with focused text field prevents back button from dismissing the app when keyboard is hidden. It's marked as fixed, but will be included in some future release, not in 1.0

    But, as I understand, the fact that text field is not loosing focus after keyboard being dismissed, is intended behaviour on Android(because of possible connected keyboard? I didn't get the reason). And this is how it works in old android layout too

    It seems strange to me, so I came with the following modifier which resigns focus when keyboard disappears:

    fun Modifier.clearFocusOnKeyboardDismiss(): Modifier = composed {
        var isFocused by remember { mutableStateOf(false) }
        var keyboardAppearedSinceLastFocused by remember { mutableStateOf(false) }
        if (isFocused) {
            val imeIsVisible = WindowInsets.isImeVisible
            val focusManager = LocalFocusManager.current
            LaunchedEffect(imeIsVisible) {
                if (imeIsVisible) {
                    keyboardAppearedSinceLastFocused = true
                } else if (keyboardAppearedSinceLastFocused) {
                    focusManager.clearFocus()
                }
            }
        }
        onFocusEvent {
            if (isFocused != it.isFocused) {
                isFocused = it.isFocused
                if (isFocused) {
                    keyboardAppearedSinceLastFocused = false
                }
            }
        }
    }
    

    Usage:

    BasicTextField(
        value = valueState.value,
        onValueChange = {
            valueState.value = it
        },
        modifier = Modifier
            .clearFocusOnKeyboardDismiss()
    )