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

Issue

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 = LocalWindowInsets.current.ime.isVisible
        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
            }
        }
    }
}

p.s. You need to install accompanist insets dependency for LocalWindowInsets.current.ime

p.s.s. Since Compose 1.2.0-alpha03, Accompanist Insets was mostly moved into Compose Foundation, check out migration guide for more details. LocalWindowInsets.current.ime should be replaced with WindowInsets.ime.


Usage:

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

Answered By – Phil Dukhov

This Answer collected from stackoverflow, is licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply

(*) Required, Your email will not be published