From cbcbd4f76b3bd787ef3249e7ddbe9358773b675b Mon Sep 17 00:00:00 2001 From: Bruno Deanoz Date: Thu, 18 Jun 2026 16:33:21 +0200 Subject: [PATCH] fix(ui): resolve double-resizing overlap bug with dynamic keyboard insets padding --- .../dev/kaizen/app/chat/ChatComponents.kt | 12 +++++++++++- .../java/dev/kaizen/app/chat/ChatScreen.kt | 19 ++++++++++++------- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/dev/kaizen/app/chat/ChatComponents.kt b/app/src/main/java/dev/kaizen/app/chat/ChatComponents.kt index cf198b3..189f890 100644 --- a/app/src/main/java/dev/kaizen/app/chat/ChatComponents.kt +++ b/app/src/main/java/dev/kaizen/app/chat/ChatComponents.kt @@ -21,10 +21,13 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.FlowRow import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.WindowInsets +import androidx.compose.foundation.layout.asPaddingValues import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.heightIn +import androidx.compose.foundation.layout.ime import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width @@ -128,6 +131,13 @@ fun EmptyHero(userName: String, onPick: (String) -> Unit, modifier: Modifier = M val dateLine = remember { now.format(DateTimeFormatter.ofPattern("EEE, d. MMMM ยท HH:mm", Locale.GERMAN)) } + + // Query soft-keyboard height dynamically to dynamically expand the bottom spacer. + // This allows suggestion pills to push upwards cleanly above the keyboard & floating input, + // completely preventing any overlaps without squeezing the greeting, date, or Kaizen orb. + val imeBottom = WindowInsets.ime.asPaddingValues().calculateBottomPadding() + val bottomSpacerHeight = remember(imeBottom) { 142.dp + imeBottom } + Column( modifier = modifier .fillMaxSize() @@ -145,7 +155,7 @@ fun EmptyHero(userName: String, onPick: (String) -> Unit, modifier: Modifier = M Text(dateLine, color = cs.onSurfaceVariant.copy(alpha = 0.7f), fontSize = 13.sp) Spacer(Modifier.height(32.dp)) SuggestionPills(onPick) - Spacer(Modifier.height(142.dp)) // Clear the floating bottom dock + Spacer(Modifier.height(bottomSpacerHeight)) // Dynamically expands to push layout out of the floating bottom dock } } diff --git a/app/src/main/java/dev/kaizen/app/chat/ChatScreen.kt b/app/src/main/java/dev/kaizen/app/chat/ChatScreen.kt index 33d28ca..f8b8819 100644 --- a/app/src/main/java/dev/kaizen/app/chat/ChatScreen.kt +++ b/app/src/main/java/dev/kaizen/app/chat/ChatScreen.kt @@ -4,12 +4,15 @@ import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.WindowInsets +import androidx.compose.foundation.layout.asPaddingValues import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.ime import androidx.compose.foundation.layout.imePadding import androidx.compose.foundation.layout.navigationBarsPadding -import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.statusBarsPadding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items @@ -80,23 +83,25 @@ fun ChatScreen(userName: String = "Bruno") { MeshBackground { Box(Modifier.fillMaxSize()) { + val imeBottom = WindowInsets.ime.asPaddingValues().calculateBottomPadding() + // 1. Scrollable Chat Content // Stretches to the full screen, allowing bubbles to scroll cleanly behind the floating glass panels. + // No root imePadding() here to prevent double-resizing/pushing bugs on standard Android windows. if (messages.isEmpty()) { EmptyHero( userName = userName, onPick = { haptics.tick(); send(it) }, - modifier = Modifier.imePadding() // Viewport shrinks so the scrollable Column adapts without warping + modifier = Modifier.fillMaxSize() ) } else { + val bottomPadding = remember(imeBottom) { 152.dp + imeBottom } LazyColumn( state = listState, - modifier = Modifier - .fillMaxSize() - .imePadding(), // Adjusts viewport height so scrollable content stops exactly above the keyboard + modifier = Modifier.fillMaxSize(), contentPadding = PaddingValues( top = 92.dp, // Leaves luxurious room for the floating KaizenHeader + status bars - bottom = 152.dp, // Leaves luxurious room for the floating ModePills + ChatInput + bottom margins + bottom = bottomPadding, // Dynamically expands as the keyboard rises to keep messages clear of the floating dock start = 16.dp, end = 16.dp ), @@ -118,7 +123,7 @@ fun ChatScreen(userName: String = "Bruno") { modifier = Modifier .align(Alignment.BottomCenter) .navigationBarsPadding() - .imePadding() + .imePadding() // Only the bottom controls float up, perfectly placing them above the keyboard ) { ModePillsRow(selected = selectedMode, onSelect = { selectedMode = it; haptics.tick() }) Spacer(Modifier.height(10.dp))