Show keyboard over Scaffold's bottomBar in Jetpack Compose and apply proper inset paddings

5.4k views Asked by At

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
}
4

There are 4 answers

6
Phil Dukhov On BEST ANSWER

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.

0
Riccardo Tesio On

This is the solution I've adopted in my app:

  1. In AndroidManifest.xml add android:windowSoftInputMode="adjustResize" to the activity
  2. In Activity::onCreate add WindowCompat.setDecorFitsSystemWindows(window, false)
  3. In the composable:
    Scaffold(
        topBar = { },
        bottomBar = { }
    ) { innerPadding ->
        val topPadding = innerPadding.calculateTopPadding()
        val bottomPadding = max(
            innerPadding.calculateBottomPadding(),
            WindowInsets.ime.asPaddingValues().calculateBottomPadding()
        )
        Box(
            modifier = Modifier
                .padding(PaddingValues(top = topPadding, bottom = bottomPadding))
        )
    }

This way the animation works well when the keyboard opens / closes.

0
Ivan On

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()
    )
}
1
Martynas B On

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()
                    }
                }
            }
    )
}