I am implementing a chat app using a LazyColumn
. I've created this with reverseLayout = true
because I want the LazyColumn to automatically scroll to the last message when the view and keyboard first appear.
I want the TextField
to stay at the bottom of the screen so I've implemented a stickyHeader
. When my upperLimit
is set to a high number where the view needs to scroll, the TextField
stays at the bottom of the screen. However when upperLimit
is set to a much smaller number like 3, the TextField
no longer stays at the bottom of the screen.
What do I need to edit to fix this?
@OptIn(ExperimentalFoundationApi::class, ExperimentalMaterial3Api::class)
@Composable
fun ChatView() {
val lazyColumnState = rememberLazyListState()
var textFieldText by remember { mutableStateOf("") }
val upperLimit = 30
LazyColumn(
state = lazyColumnState,
contentPadding = PaddingValues(40.dp),
reverseLayout = true
) {
stickyHeader {
TextField(
value = textFieldText,
onValueChange = { inputTextField ->
textFieldText = inputTextField
}
)
}
item {
for (i in upperLimit downTo 0) {
Text(
text = "This is message no. $i",
modifier = Modifier.padding(10.dp)
)
}
}
}
}
Please try to add the fillMaxHeight()
Modifier to the LazyColumn
:
LazyColumn(
modifier = Modifier.fillMaxHeight(),
state = lazyColumnState,
contentPadding = PaddingValues(40.dp),
reverseLayout = true
) {
//...
}
However, then the first few messages will be attached to the bottom of the screen. If you don't want this, you can try to use reverseLayout = false
and programmatically handle scrolling using lazyColumnState.scrollToItem()
like shown at this sample repository from @Thracian, it gives a great example how to implement a chat app in Jetpack Compose.
@Composable
fun ChatView() {
val lazyColumnState = rememberLazyListState()
var textFieldText by remember { mutableStateOf("") }
val chatList = remember {
mutableStateListOf(
"Hello, how are you?",
"I am fine, thanks.",
"I am learning Jetpack Compose, and you?",
"Bro why do you do this, it's so boring!",
"The only thing that's boring is having a conversation with you...",
"Bro that's so mean!",
"I did not mean it in a mean way :)",
"Aw you're such a nice guy... NOT",
"I appreciate you seeing my whole potential",
"Well, that's what friends are for.\nYou should feel honored for having me as your friend!",
"Be assured that my gratitude is on the same level as your arrogance."
)
}
LaunchedEffect(chatList.size) {
lazyColumnState.animateScrollToItem(chatList.size - 1)
}
Column(
modifier = Modifier.fillMaxSize()
) {
LazyColumn(
modifier = Modifier
.fillMaxWidth()
.weight(1f),
state = lazyColumnState,
contentPadding = PaddingValues(16.dp),
) {
itemsIndexed(
items = chatList
) { chatIndex, chatMessage, ->
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = if (chatIndex % 2 == 0) Arrangement.Start else Arrangement.End // simulate chat conversation
) {
Text(
text = chatMessage,
textAlign = if (chatIndex % 2 == 0) TextAlign.Start else TextAlign.End,
modifier = Modifier.padding(10.dp)
)
}
}
}
Row {
TextField(
modifier = Modifier.weight(1f),
value = textFieldText,
onValueChange = { inputTextField ->
textFieldText = inputTextField
}
)
IconButton(
onClick = {
chatList.add(textFieldText)
textFieldText = ""
}
) {
Icon(Icons.AutoMirrored.Filled.Send, "")
}
}
}
}
Also note that you are actually creating one giant item in your LazyColumn
, which kills any benefit of a lazy layout. You need to change your code like this:
for (i in upperLimit downTo 0) {
item {
Text(
text = "This is message no. $i",
modifier = Modifier.padding(10.dp)
)
}
}