I have been wrestling with IME padding/adjustment for my app. I have written a little toy app to try and focus on just the problem. The app presents a scrollable list of cards with info on them, the name is a simple BasicTextField. The code is at the bottom past my question(s).
With no imePadding()
modifiers, when I focus into a named field at the bottom for the list, the caret shows up in the middle of the keyboard, the card is below the keyboard:
If I add imePadding()
as a modifier to either the parent Column or my LazyColumn, the content is adjusted to move the text field up, but the footer (or a gap that is suspiciously the same-ish height) is between the keyboard and field:
None of these are desirable. What I want is for the list to scroll (if necessary) and expose the whole card, not just the text field, so that the context of what is being edited remains visible.
Bonus points would be to scroll back to original position on keyboard dismiss. Is this at all doable? All these solutions that I'm finding seem to involve changing the heights, either directly through imePadding(), or setting height(...WindowInsets.ime...) or similar for a padding modifier. I don't actually want to change the size of anything. That just seems like a round-about methodology. What I want to do is adjust the column scroll with an offset that is enough to keep the whole card visible while the keyboard is present.
Code for the example:
@Composable
fun KeyboardExperiment2Screen() {
val names =
"Thorin,Balin,Dwalin,Fíli,Kíli,Dori,Nori,Ori,Óin,Glóin,Bifur,Bofur,Bombur".split(',')
Column(
modifier = Modifier
.systemBarsPadding()
.fillMaxSize()
.background(color = Color.White)
) {
Text(
text = "HEADER",
modifier = Modifier
.fillMaxWidth()
.height(60.dp)
.background(color = Color.Cyan),
textAlign = TextAlign.Center
)
LazyColumn(
modifier = Modifier
.weight(1f)
.fillMaxWidth()
) {
itemsIndexed(names) { index, name ->
MyCard(
name = name,
age = Random.nextInt(50, 150),
favColor = Color.hsv(
Random.nextFloat() * 360,
Random.nextFloat() * 0.5f + 0.5f,
Random.nextFloat() * 0.5f + 0.5f
),
height = Random.nextInt(80, 120),
weight = Random.nextDouble(50.0, 95.0),
background = Color.hsv(
((index * 3) % names.size).toFloat() * 360 / names.size.toFloat(), 0.5f, 1f
)
)
}
}
Text(
text = "Footer",
modifier = Modifier
.fillMaxWidth()
.height(60.dp)
.background(color = Color.Gray),
textAlign = TextAlign.Center
)
}
}
@Composable
private fun MyCard(
name: String, age: Int, favColor: Color, height: Int, weight: Double, background: Color
) {
Card(
modifier = Modifier
.fillMaxWidth()
.padding(12.dp),
colors = CardDefaults.cardColors(containerColor = background)
) {
Column(modifier = Modifier.padding(12.dp)) {
Row(
modifier = Modifier
.fillMaxWidth()
.background(color = Color.LightGray, shape = RoundedCornerShape(percent = 25))
.padding(vertical = 6.dp, horizontal = 6.dp),
verticalAlignment = Alignment.CenterVertically
) {
BasicTextField(
value = name,
modifier = Modifier.weight(1f),
onValueChange = { update -> println(update) },
textStyle = MaterialTheme.typography.bodyLarge,
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
keyboardActions = KeyboardActions.Default
)
Spacer(modifier = Modifier.width(12.dp))
Box(
modifier = Modifier
.size(28.dp)
.background(color = favColor, shape = RoundedCornerShape(6.dp))
)
}
Spacer(modifier = Modifier.height(8.dp))
Row(
modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween
) {
val style = MaterialTheme.typography.labelSmall
Text(text = "Age: $age yr", style = style)
Text(text = "Height: $height cm", style = style)
Text(text = "Weight: ${String.format("%.1f", weight)} kg", style = style)
}
}
}
}
Of note, my manifest includes android:windowSoftInputMode="adjustResize"
, and my Activity does WindowCompat.setDecorFitsSystemWindows(window, false)
before setContent {...}.