jetpack compose LazyColumn scroll not work

Viewed 924

I am developing an android chatting app using jetpack compose.

The chat messages are shown using the LazyColumn. And the messages are coming from the WebSocket.

What I want to develop is:

  • If the user is seeing the latest message when new messages come, the LazyColumn should be scroll to the latest message.
  • If the user is seeing the previous message (scrolled to upper side) when new messages come, the scroll position should not be changed.

To do these, I found listState.layoutInfo.visibleItemsInfo. If the listState.layoutInfo.visibleItemsInfo.lastOrNull()?.index == chatMessages.size - 1 means that the user is seeing the latest message. (positioned bottom)

LazyColumn(
    state = listState
) {
    vm.scrollToBottom.value = listState.layoutInfo.visibleItemsInfo.lastOrNull()?.index == chatMessages.size - 1
    Log.d("TEST", "[test] scrollToBottom: ${vm.scrollToBottom.value}")
    if (vm.scrollToBottom.value) {
        coroutineScope.launch {
            Log.d("TEST", "[test] scroll!")
            listState.animateScrollToItem(chatMessages.size)
        }
    }
    itemsIndexed(chatMessages) { _, chat ->
        when (chat.command) {
            Command.MESSAGE -> when (chat.userId) {
                myId -> MyChat(chat = chat)
                else -> OtherUserChat(chatMsg = chat)
            }
            Command.JOIN -> JoinLeaveMessage(
                chat = chat,
                message = stringResource(R.string.user_joined_group)
            )
            Command.LEAVE -> JoinLeaveMessage(
                chat = chat,
                message = stringResource(R.string.user_left_group)
            )
        }
    }
}

But upper code doesn't work.. And I check the logcat it is showing...:

2022-03-04 12:17:40.534 19854-19854 D/TEST: [test] scrollToBottom: true
2022-03-04 12:17:40.549 19854-19854 D/TEST: [test] scroll!
2022-03-04 12:17:40.551 19854-19854 D/TEST: [test] scrollToBottom: true
2022-03-04 12:17:40.566 19854-19854 D/TEST: [test] scroll!
...
2022-03-04 12:17:40.635 19854-19854 D/TEST: [test] scrollToBottom: true
2022-03-04 12:17:40.649 19854-19854 D/TEST: [test] scroll!
2022-03-04 12:17:40.651 19854-19854 D/TEST: [test] scrollToBottom: true
2022-03-04 12:17:40.665 19854-19854 D/TEST: [test] scroll!
1 Answers

I think you are trying to scroll while the LazyColumn is being rendered.

I also think you need to subtract one more since the index starts at 0?

You can try doing it in a LaunchedEffect like this:

val composableScope = rememberCoroutineScope()
val scrollToBottom = listState.layoutInfo.visibleItemsInfo.lastOrNull()?.index == chatMessages.size - 2
val lastItemIndex = chatMessages.size - 1

LaunchedEffect(scrollToBottom, lastItemIndex) {
    if (scrollToBottom) {
        composableScope.launch {
            listState.animateScrollToItem(lastItemIndex)
        }
    }
}
Related