Update
With the new WindowInsets
API, it gets easier
First, to return the correct values, you need to set:
WindowCompat.setDecorFitsSystemWindows(window, false)
Then to use Keyboard as a state:
@Composable
fun keyboardAsState(): State<Boolean> {
val isImeVisible = WindowInsets.ime.getBottom(LocalDensity.current) > 0
return rememberUpdatedState(isImeVisible)
}
use example:
val isKeyboardOpen by keyboardAsState() // true or false
ps: I've tried to use WindowInsets.isImeVisible, but it returns true in the first call.
Without an experimental API
if you want with the statement, I found this solution:
enum class Keyboard {
Opened, Closed
}
@Composable
fun keyboardAsState(): State<Keyboard> {
val keyboardState = remember { mutableStateOf(Keyboard.Closed) }
val view = LocalView.current
DisposableEffect(view) {
val onGlobalListener = ViewTreeObserver.OnGlobalLayoutListener {
val rect = Rect()
view.getWindowVisibleDisplayFrame(rect)
val screenHeight = view.rootView.height
val keypadHeight = screenHeight - rect.bottom
keyboardState.value = if (keypadHeight > screenHeight * 0.15) {
Keyboard.Opened
} else {
Keyboard.Closed
}
}
view.viewTreeObserver.addOnGlobalLayoutListener(onGlobalListener)
onDispose {
view.viewTreeObserver.removeOnGlobalLayoutListener(onGlobalListener)
}
}
return keyboardState
}
and to detect/check the value you'll only need this:
val isKeyboardOpen by keyboardAsState() // Keyboard.Opened or Keyboard.Closed
WindowInsets.isImeVisible
– Dalessandro