LATEST UPDATE
This issue has been fixed in the latest versions of Jetpack Compose. So no need to handle it manually.
Old Answer
Thanks for all the answers here. After taking reference from the answers here, here's a solution without using any library
1. Create an extension on View to determine if the keyboard is open or not
fun View.isKeyboardOpen(): Boolean {
val rect = Rect()
getWindowVisibleDisplayFrame(rect);
val screenHeight = rootView.height
val keypadHeight = screenHeight - rect.bottom;
return keypadHeight > screenHeight * 0.15
}
2. Create an observable state for determining if a keyboard is open or not
This will listen to Global layout updates on LocalView in which on every event, we check for keyboard open/close status.
@Composable
fun rememberIsKeyboardOpen(): State<Boolean> {
val view = LocalView.current
return produceState(initialValue = view.isKeyboardOpen()) {
val viewTreeObserver = view.viewTreeObserver
val listener = OnGlobalLayoutListener { value = view.isKeyboardOpen() }
viewTreeObserver.addOnGlobalLayoutListener(listener)
awaitDispose { viewTreeObserver.removeOnGlobalLayoutListener(listener) }
}
}
3. Create modifier
This modifier will take care of clearing focus on keyboard visible/invisible events.
fun Modifier.clearFocusOnKeyboardDismiss(): Modifier = composed {
var isFocused by remember { mutableStateOf(false) }
var keyboardAppearedSinceLastFocused by remember { mutableStateOf(false) }
if (isFocused) {
val isKeyboardOpen by rememberIsKeyboardOpen()
val focusManager = LocalFocusManager.current
LaunchedEffect(isKeyboardOpen) {
if (isKeyboardOpen) {
keyboardAppearedSinceLastFocused = true
} else if (keyboardAppearedSinceLastFocused) {
focusManager.clearFocus()
}
}
}
onFocusEvent {
if (isFocused != it.isFocused) {
isFocused = it.isFocused
if (isFocused) {
keyboardAppearedSinceLastFocused = false
}
}
}
}
4. Use it
Finally, use it with TextField
composable
BasicTextField(Modifier.clearFocusOnKeyboardDismiss())