Hello guys, I have an issue with imePadding() not working with ModalBottomSheet. I have some TextFie...
s
Hello guys, I have an issue with imePadding() not working with ModalBottomSheet. I have some TextField in the BottomSheet's content and I want that when I open the keybaord, it adds some padding under the BottomSheet and not cover the content. it seems that a lot people have experienced this same issue as well especially with older devices. I am on Android 10 (Api level 29), and a team member/boss who has a higher android version, I think he uses a Google pixel 7 pro had the BottomSheet logic working well for him(even without the imePadding), he was basically able to drag his when the keyboard opened. So I'm not sure why this is happening. I've done some research and found some github issues on this, but they were either closed or left unanswered. I just want to know if there is a fix or workaround for this especially the fact that we targeting devices API level 27 and above(minSdk 27).
Here is my code
Copy code
@Composable
fun Tray(
    onDismissRequest: OptionalButtonCallback,
    isSwipeDismissible: Boolean = true,
    skipPartiallyExpanded: Boolean = false,
    stickyBottom: (@Composable BoxScope.() -> Unit)? = null,
    content: @Composable BoxScope.() -> Unit,
) {
    val sheetState = rememberModalBottomSheetState(confirmValueChange = { isSwipeDismissible }, skipPartiallyExpanded = skipPartiallyExpanded)
    val bottomPadding = WindowInsets.navigationBars.asPaddingValues().calculateBottomPadding()

    BaseModelBottomSheet(
        onDismissRequest = {
            onDismissRequest?.invoke()
        },
        sheetState = sheetState,
    ) {
        Box(
            modifier = Modifier
                .imePadding()  // does not work. The keyboard covers the content of the BottomSheet
                .padding(bottom = bottomPadding),
            contentAlignment = Alignment.BottomEnd
        ) {
            content()
            stickyBottom?.let { stickyBottom ->
                Box(modifier = Modifier
                    .wrapContentWidth()
                    .padding(Theme.spacing.x5)
                    .offset {
                        IntOffset(x = 0, y = -sheetState.requireOffset().toInt())
                    }) {
                    stickyBottom()
                }
            }
        }

    }
}
s
What I typically do for such cases is make sure my sheet's content is scrollable, so that if the keyboard comes up and there's little room left, you can still scroll to see everything. And you can add a spacer at the bottom of your content with the keyboard's height to add enough space at the bottom so that the content does scroll up above the keyboard. Something like
Copy code
Sheet() {
 Column(verticalScroll) {
  NormalSheetContent()
  Spacer(Modifier.windowInsetsBottomHeight(WindowInsets.ime))
 }
}
but you may also need to be careful not to double-apply the insets, depending on what insets you pass to your Sheet itself. Play around with this idea to see if it helps you or not. Oh and if you do not have
android:windowSoftInputMode="adjustResize"
then things may be very different overall, but I don't know how that works, I only really know how insets work with adjustResize.
s
Okay thanks Stylianos, I will try this. I will also try it to see what it will looks like on other/newer devices. But please what do you mean double applying the insets? Is because of the BottomPadding insets that I set?
s
If you got some insets on the sheet itself. And then you got
Spacer(Modifier.windowInsetsBottomHeight(WindowInsets.ime))
, you may end up applying the ime insets both on the sheet and on this spacer too. So I'd check what insets you pass to the sheet itself too. Even if you pass none, it has some default ones iirc
s
Okay by "default", if you are referring to the windowInsets parameter in the actual ModalBottomSheet composable I actually set it to zero so that I can handle the insets manually. It is something like this. windowInsets = WindowInsets(0), The only other insets that I am actually applying is the set the navigationBars as the bottomPadding like this :
Copy code
val bottomPadding = WindowInsets.navigationBars.asPaddingValues().calculateBottomPadding()

// and then in the composable, the direct sheet's Box content
 Box(
            modifier = Modifier
                .imePadding()  // does not work. The keyboard covers the content of the BottomSheet
                .padding(bottom = bottomPadding), // bottom padding applied here
            contentAlignment = Alignment.BottomEnd
        )
👍 1
But yeah let me try your solution and see the results
Hello Stylianos, I tried your workaround and it does not seem to work. I have screens where I pass the BottomSheet content(Text fields and buttons) to the Tray's content parameter based on my previous code, I applied the spacer just like you suggested, its still not working. Let me briefly share the code :
Copy code
Tray(
        onDismissRequest = { viewModel.emit(Intent.Navigation.Back) }
    ) {
        Column(
            modifier = Modifier
                .verticalScroll(rememberScrollState())
                .padding(horizontal = Theme.spacing.x4),
            horizontalAlignment = Alignment.CenterHorizontally
        ) {
            // Some screen content including the TextFields 
            Spacer(
                modifier = Modifier.windowInsetsBottomHeight(WindowInsets.ime)  // keyboard still covers the content
            )
        }
    }
s
With this, can you not scroll the content all the way up so that they're visible? Does the spacer not take up as much height as the keyboard is big?
s
No it does not, the bottomSheet only goes up as much as it needs to show the content when I initially open it, and when I open the keyboard, it still covers the contents
s
If your spacer is not taking up the size of the keyboard by applying the keyboard insets, I'd check if you got adjustResize in your manifest. Also add a log on an onPlaced perhaps on the spacer to print out the height of the spacer, see if it shows that it's expanding there. It must be following the insets of the ime.
s
I just confirmed in the Manifest file that the adjustResize is there, let me try to log the spacer
I don't know if this makes sense, but I did it like this. I am about to run the build now.
Copy code
Spacer(
                modifier = Modifier
                    .windowInsetsBottomHeight(WindowInsets.ime)
                    .onPlaced {
                        Log.i("CreateChildScreen", "The height of the spacer is ${it.size.height}")
                    }
            )
👍 1
The log message isn't showing up for some reason, tried searching it. I also tried looking for it through the Tag too like this, tag~:CreateChildScreen
s
Give it at least 1.dp to make sure it renders something if all the window insets are already considered consumed for some reason.
s
Alright then
s
What does Tray do btw. Perhaps you're consuming the insets somewhere higher up already
s
Let me show you the Tray code and the code before it too. They're very short
This is the Tray code :
Copy code
@Composable
fun Tray(
    onDismissRequest: OptionalButtonCallback,
    isSwipeDismissible: Boolean = true,
    skipPartiallyExpanded: Boolean = false,
    stickyBottom: (@Composable BoxScope.() -> Unit)? = null,
    content: @Composable BoxScope.() -> Unit,
) {
    val sheetState = rememberModalBottomSheetState(confirmValueChange = { isSwipeDismissible }, skipPartiallyExpanded = skipPartiallyExpanded)
    val bottomPadding = WindowInsets.navigationBars.asPaddingValues().calculateBottomPadding()

    BaseModelBottomSheet(
        onDismissRequest = {
            onDismissRequest?.invoke()
        },
        sheetState = sheetState,
    ) {
        Box(
            modifier = Modifier
                .padding(bottom = bottomPadding),
            contentAlignment = Alignment.BottomEnd
        ) {
            content()
            stickyBottom?.let { stickyBottom ->
                Box(modifier = Modifier
                    .wrapContentWidth()
                    .padding(horizontal = Theme.spacing.x5)
                    .offset {
                        IntOffset(x = 0, y = -sheetState.requireOffset().toInt())
                    }) {
                    stickyBottom()
                }
            }

        }

    }
}
The only insets I use here is the WindowInsets.navigationBars.asPaddingValues().calculateBottomPadding()
Then the code that Tray calls is here :
Copy code
@Composable
internal fun BaseModelBottomSheet(
    modifier: Modifier = Modifier,
    sheetState: SheetState,
    onDismissRequest: OptionalButtonCallback,
    invertedColors: Boolean = isSystemInDarkTheme(),
    content: @Composable ColumnScope.() -> Unit,
) {

    ModalBottomSheet(
        containerColor = componentColors(invertedColors = invertedColors),
        onDismissRequest = {
            onDismissRequest?.invoke()
        },
        sheetState = sheetState,
        modifier = modifier,
        windowInsets = WindowInsets(0.dp)
    ) {
        content()
    }

}
s
Can't see a reason why the ime insets wouldn't be applied. If you do the exact same thing but outside of a bottom sheet, just on the root activity you got, before you add any wrappers anywhere, do you get the insets properly?
s
Let me try that, there are screens where I just added the imePadding(), but I did not put them inside any BottomSheet, just normal columns in a scaffold, I will remove the imePadding modifier and try it with this Spacer method to see it
Stylianos, I just added it to another screen and it works fine. Even the logs are showing up.
The screen is exactly as I described before this image
But in the Tray screens, it does not seem to work
s
Sounds like something wrong with either the way you call the bottom sheet, or the bottom sheet itself or something above your bottom sheet that you're forgetting. I don't think I can help with that any more than I have then 🤷‍♂️
s
Alright then thanks.
But please do you have any small sample code where you recreated your solution with same ModalBottomSheet so that I can study it and compare with my code to see if I'm doing anything wrong
Hello Stylianos, apparently I have just found an issue on issue tracker that specifically addresses this problem. I am looking into it now. If I am not mistaken, the last comment that made this marked as fixed suggests that we do some migration from the current BottomModalSheet right? How will I do that, I think I also have a hard time understanding the link. 😅 This is the issue btw, https://issuetracker.google.com/issues/290893168
But I want to first try out the other temporary solutions before this one.
1633 Views