Reputation: 358
So I have this BottomSheetScaffold layout with a CenterAlignedTopAppBar and a bottom sheet with some text fields.
Below are modifier that I presume might be useful:
// root composable, NavHost modifier
Modifier.fillMaxSize().imePadding()
// screen-level composable, BottomSheetScaffold
Modifier.fillMaxSize()
// modifier for a content column inside the BottomSheetScaffold
Modifier.padding(paddingValues).fillMaxSize()
So when I click on a text field inside the bottom sheet, the whole layout does an awful shift (I guess it tries to do some kind of animation) and then gets resized to appropriate dimensions. Here's a video demonstration
Is there a way this can be avoided?
I've tried setting android:windowSoftInputMode
to all different kinds of values, like adjustPan, adjustResize, adjustNothing, but this didn't work.
Also tried setting android:animateLayoutChanges
to false, but this didn't work either.
I did also try to apply imePadding
modifier to different parts of the layout, but unless it's on NavHost, the paddings are weird and inappropriate, so I assume that NavHost (or any other top-level composable in your activity) is indeed the right place to put this modifier.
The most basic code sample that resembles my problem as closely as possible:
@OptIn(ExperimentalMaterial3Api::class)
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {
var input by remember { mutableStateOf("") }
ImeLayoutJumpShiftTheme {
Box(modifier = Modifier.imePadding()) {
BottomSheetScaffold(
topBar = { TopAppBar(title = { Text("Example") }) },
sheetPeekHeight = 125.dp,
sheetContent = {
Column(modifier = Modifier.padding(16.dp)) {
TextField(value = input, onValueChange = { input = it })
}
}
) { paddingValues ->
LazyColumn(modifier = Modifier.padding(paddingValues)) {
items(12) {
ListItem(
headlineContent = { Text("Item $it") },
supportingContent = {
Text("This is item #$it. It increases the amount of space this list item takes up to recreate the usage of list items in a real project.")
}
)
}
}
}
}
}
}
}
}
Upvotes: 3
Views: 281
Reputation: 67218
When android:windowSoftInputMode="adjustUnspecified"
or by default is set keyboard animation is not synchronized with the maximum space it will reach at the end. Views or Composable go faster than keyboard.
https://developer.android.com/develop/ui/views/layout/sw-keyboard
In windowSoftInputMode is adjustNothing
or adjustPan
is set it pushes everything up to display TextField if it's below max height of keyboard and has this terrible animation, however when you use android:windowSoftInputMode="adjustResize"
animation is smoother but it doesn't push TextField
up. This is easy to overcome either with
Modifier.imePadding()
or setting an offset based on current height of sw keyboard.
The issue with Modifier.imePadding
is how it works. The misconception people have, including me before testing, with it is thinking it pushes your Composable above keyboard but it doesn't.
It adds padding to bottom of the Composable it's assigned to. And works best like in Composable if there is a space to shrink, for instance a Spacer(weight(1f)), so it can add padding to Composable at the bottom while shrinking space above it. In view if you check how adjustResize works with Views they use it inside a ConstraintsLayout EditText anchored to bottom.
However, if there are any Composables above it, it just adds padding bottom or might not do anything at all. You can consider this as adding padding to last item, or parent's bottom if it's assigned to parent.
With android:windowSoftInputMode="adjustResize"
Column(
modifier = Modifier
.systemBarsPadding()
.imePadding()
) {
var text by remember {
mutableStateOf("")
}
TopAppBar(
modifier = Modifier.fillMaxWidth(),
title = {
Text("TopAppbar")
}
)
// Comment repeat block to see difference
repeat(10) {
Text(
text = "Text: $it",
fontSize = 16.sp,
modifier = Modifier.padding(16.dp)
)
}
Column {
Spacer(Modifier.weight(1f))
TextField(text, onValueChange = { text = it })
Text("Bottom Text")
}
}
If you comment repeat block
This Modifier actually should have been Modifier.offset
to work properly with every layout since setting offset does not push siblings. And it's easy to make one but you need to set WindowCompat.setDecorFitsSystemWindows(window, false)
to be able to get current keyboard height.
val density = LocalDensity.current
val bottom: Int = WindowInsets.ime.getBottom(density)
val bottomDp: Dp = with(density) { bottom.toDp() }
Adding offset or padding below Composable works when you don't use BottomSheets, because it has peekHeight. In OP's case more precise solution requires reproducible example.
Upvotes: 0