Search code examples
androidkotlinandroid-jetpack-composelazycolumn

Lazy Column's ScrollToItem Does Not Work As Expected


I am trying to scroll my LazyColumn to the last item automatically when the keyboard appears. However, the code below that is suggested in the other StackOverflow discussions works inconsistently as shown in the attached image, and rarely brings the last item into view.

@OptIn(ExperimentalLayoutApi::class)
@Preview(showBackground = true)
@Composable
fun ChatScreen() {

    val viewModel: ChatScreenViewModel = viewModel()
    val textFieldContent = viewModel.textFieldState
    val messages =  viewModel.messageListState
    val listState = viewModel.messageListScrollState
    val keyboardVisibility = WindowInsets.isImeVisible

    LaunchedEffect(key1 = keyboardVisibility) {

        listState.animateScrollToItem(listState.layoutInfo.totalItemsCount)
    }

    ChatbotAppTheme {

        Column(
            modifier = Modifier
                .fillMaxSize()
                .systemBarsPadding()
                .imePadding()
        ) {
            // LazyColumn to hold messages
            LazyColumn(
                modifier = Modifier
                    .weight(1f),
                state = listState

            ) {
                items(messages) {
                    MessageBubble(
                        text = it.body,
                        role = it.role
                    )
                }
            }

            Row(
                modifier = Modifier
                    .fillMaxWidth()
                    .background(Color.Transparent),
                verticalAlignment = Alignment.CenterVertically,
                horizontalArrangement = Arrangement.SpaceAround
            ) {
                CustomTextField(
                    modifier = Modifier.weight(1f),
                    text = textFieldContent.value,
                    onValueChangeFunc = {viewModel.onTextFieldContentChanged(it)},
                    trailingIconFunc = {viewModel.clearTextField()},
                )
                CustomButton (enabled = textFieldContent.value.isNotEmpty()) {
                    viewModel.onSendButtonPressed()

                }
            }
        }
    }
}

I have set the required android:windowSoftInputMode="adjustResize" in the manifest and set enableEdgeToEdge() in the MainActivity to ensure that isImeVisible is working correctly. However, I am unable to get the expected result. I would really appreciate any help or insight.

enter image description here


Solution

  • I managed to fix the problem by adding a fairly long delay before executing the scroll. Note that a short delay (e.g., 50ms) does not fix the issue.

    @Composable
    fun ChatScreen() {
    
    //...
            val keyboardVisibility = WindowInsets.isImeVisible
    
            LaunchedEffect(key1 = keyboardVisibility) {
        
                if (keyboardVisibility == true){
                    delay(300)
                    viewModel.scrollToLastItem()
                }
            }
    //...
    
    }
    
    
    class ChatScreenViewModel(): ViewModel() {
    
    //...
        val messageListScrollState = LazyListState()
    
        fun scrollToLastItem(){
            viewModelScope.launch {
                messageListScrollState.scrollToItem(index = messageListScrollState.layoutInfo.totalItemsCount)
            }
        }
    //...
    
    }