8

I'm using Scaffold for my main screen with a fixed bottomBar that is visible in every screen of the app, and I'm applying the innerPadding of the Scaffold to its content.

I want the keyboard to appear over the bottomBar, and for that I'm applying the imePadding() only to the Scaffold's content.

However, when the keyboard is opened, both the Scaffold's innerPading and imePadding() are applied to the contents padding.

I've tried to go through the Accompanist Insets migration, but no lucky.

Is there anyway that I can prevent it and apply only one or the other?

Here is a piece of my code:

Scaffold(
    topBar = { },
    bottomBar = { },
    modifier = Modifier
        .systemBarsPadding()
) { innerPadding ->
    Content(
        modifier = Modifier
            .padding(innerPadding)
            .imePadding()
    )
}

And this is the result:

enter image description here

With the now, deprecated, Accompanist Insets, I was using the following solution:

val isImeVisible = LocalWindowInsets.current.ime.isVisible
val contentPadding = remember(isImeVisible) {
    if (isImeVisible) PaddingValues(top = innerPadding.calculateTopPadding()) else innerPadding
}
rewgoes
  • 656
  • 2
  • 9
  • 23

3 Answers3

7

Since 1.4.0 you can use system WindowInsets.isImeVisible. For 1.3.0 see answer below:

According to Accompanist Insets migration, LocalWindowInsets.current.ime should be replaced with WindowInsets.ime.

It doesn't have isVisible for now, until this bug is fixed. Here's how I've re-created it for now:

val WindowInsets.Companion.isImeVisible: Boolean
    @Composable
    get() {
        val density = LocalDensity.current
        val ime = this.ime
        return remember {
            derivedStateOf {
                ime.getBottom(density) > 0
            }
        }.value
    }

Usage:

val isImeVisible = WindowInsets.isImeVisible

This should work with your old remember(isImeVisible) code.

Phil Dukhov
  • 67,741
  • 15
  • 184
  • 220
  • Can we listen to the keyboard open/close state changes using this approach? – SpiralDev Apr 12 '22 at 10:13
  • @SpiralDev in compose instead of listening for changes we're reacting on state changes, which trigger recomposition of the view which is using this particular state. E.g. you can write `if (WindowInsets.isImeVisible) Text("visible") else Text("hidden")`, if you need to launch some action, you can do this with `LaunchedEffect`: `if (WindowInsets.isImeVisible) LaunchedEffect(Unit) { doSomeStuff }`. – Phil Dukhov Apr 12 '22 at 10:46
  • How about [this](https://stackoverflow.com/a/69533584/5985958) then, It detects state changes? – SpiralDev Apr 12 '22 at 11:01
  • @SpiralDev I'm not saying that you can't, sometimes you have to, like in the linked case, when you need to deal with old Android API. – Phil Dukhov Apr 12 '22 at 11:37
  • So now that Compose 1.4 is released, which should contain the fix, do you know how to replace the custom method? – G00fY Mar 23 '23 at 08:53
  • 1
    @G00fY there's system `WindowInsets.isImeVisible` now – Phil Dukhov Mar 23 '23 at 09:57
1

Another solution would be to set BringIntoViewRequester to your content inside Scaffold. Then when textField is focused, you could call bringIntoViewRequester.bringIntoView(). This way you wouldn't need to set any paddings.

val bringIntoViewRequester = remember { BringIntoViewRequester() }

Column(
    modifier = Modifier.bringIntoViewRequester(bringIntoViewRequester)
) {
    TextField(
        value = "",
        onValueChange = {},
        modifier = Modifier
            .onFocusEvent {
                if (it.isFocused) {
                    coroutineScope.launch {
                        delay(350)
                        bringIntoViewRequester.bringIntoView()
                    }
                }
            }
    )
}
Martynas B
  • 2,843
  • 2
  • 12
  • 15
  • Bear in mind that onFocusEvent callback will be triggered after opening or closing the keyboard, and on each value change - that's a lot of callbacks. Also, adding delay() seems to solve the problem of consuming the callback on recomposition. – Pitos Nov 10 '22 at 22:48
0

Try using something like this (WARNING: consumedWindowInsets is Experimental, but it's working):

Scaffold(
    topBar = { },
    bottomBar = { },
    modifier = Modifier
        .systemBarsPadding()
) { innerPadding ->
    Content(
        modifier = Modifier
            .consumedWindowInsets(innerPadding)
            .padding(innerPadding)
            .imePadding()
    )
}
Ivan
  • 137
  • 1
  • 7