fix: prevent IME from hiding latest messages in chat

Changed chat layout from Box overlay to Column flow so imePadding()
applies to the whole container instead of just the input bar. Messages
area now shrinks with the keyboard, keeping latest messages visible.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-26 13:01:43 +08:00
parent 5dad0cd39b
commit 91231834dc
@@ -162,72 +162,67 @@ fun ChatScreen(
else -> CyreneStatus.OFFLINE else -> CyreneStatus.OFFLINE
} }
// Input area overlaid at bottom, with IME padding so only input moves up // Single column layout: everything flows together and IME shrinks the whole view
Box( Column(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
.statusBarsPadding(), .statusBarsPadding()
.imePadding(),
) { ) {
Column(modifier = Modifier.fillMaxSize()) { // Top status bar
// Top status bar Row(
Row( modifier = Modifier
modifier = Modifier .fillMaxWidth()
.fillMaxWidth() .padding(horizontal = 16.dp, vertical = 8.dp),
.padding(horizontal = 16.dp, vertical = 8.dp), verticalAlignment = Alignment.CenterVertically,
verticalAlignment = Alignment.CenterVertically, ) {
) { StatusIndicator(status = status)
StatusIndicator(status = status) }
}
// Messages area (fills space above input area) // Messages area (fills remaining space, shrinks with IME)
PullToRefreshBox( PullToRefreshBox(
isRefreshing = isRefreshing, isRefreshing = isRefreshing,
onRefresh = { viewModel.refreshMessages() }, onRefresh = { viewModel.refreshMessages() },
modifier = Modifier modifier = Modifier.weight(1f),
.weight(1f) ) {
.padding(bottom = 96.dp), // Reserve space for floating input bar if (messages.isEmpty() && !isStreaming) {
) { Box(
if (messages.isEmpty() && !isStreaming) { modifier = Modifier.fillMaxSize(),
Box( contentAlignment = Alignment.Center,
modifier = Modifier.fillMaxSize(), ) {
contentAlignment = Alignment.Center, Text(
) { text = "开始和昔涟对话吧",
Text( style = MaterialTheme.typography.bodyLarge,
text = "开始和昔涟对话吧", color = MaterialTheme.colorScheme.onSurfaceVariant,
style = MaterialTheme.typography.bodyLarge, )
color = MaterialTheme.colorScheme.onSurfaceVariant, }
} else {
LazyColumn(
modifier = Modifier.fillMaxSize(),
state = listState,
reverseLayout = true,
) {
itemsIndexed(messages, key = { _, msg -> msg.id }) { index, message ->
AnimatedChatBubble(
message = message,
animIndex = index.coerceAtMost(20),
) )
} }
} else { if (isStreaming) {
LazyColumn( item(key = "typing_indicator") {
modifier = Modifier.fillMaxSize(), TypingIndicator()
state = listState,
reverseLayout = true,
) {
itemsIndexed(messages, key = { _, msg -> msg.id }) { index, message ->
AnimatedChatBubble(
message = message,
animIndex = index.coerceAtMost(20),
)
}
if (isStreaming) {
item(key = "typing_indicator") {
TypingIndicator()
}
} }
} }
} }
} }
} }
// Input area at bottom, moved up by IME // Input area at bottom, in flow (not overlaid)
Column( Column(
modifier = Modifier modifier = Modifier
.align(Alignment.BottomCenter)
.fillMaxWidth() .fillMaxWidth()
.background(MaterialTheme.colorScheme.surface) .background(MaterialTheme.colorScheme.surface)
.navigationBarsPadding() .navigationBarsPadding(),
.imePadding(),
) { ) {
// "昔涟正在输入..." indicator // "昔涟正在输入..." indicator
if (isStreaming) { if (isStreaming) {