Home > Mobile >  why can't I use scrollToEnd or similar functions in my Jetpack Compose project?
why can't I use scrollToEnd or similar functions in my Jetpack Compose project?

Time:01-13

I 'm trying to go lazycolumn last item because I have a lazycolumn and empty list. The application I made is a chatbot application, there are bot messages and user messages. When the bot gives a message, it is added to the list, at the same time, when the user enters a reply, it is added to the list and as it is added to this list, the empty list starts to fill up and lazycolumn is also displayed, but it is a separate thing that should be in a correspondence application. As messages are added, that is, when the end of the screen is reached, the screen needs to slide down. I used scrollToEnd or something similar to do this, but my project does not see scrollToEnd and gives the following error Unresolved reference: scrollToEnd or for example isScrolledToEnd() When I use this, I wonder if there is a separate item that I should add. Is there a dependency or are they obsolete? can you help me solve this issue


var index = 1

@SuppressLint("CoroutineCreationDuringComposition")
@Composable
fun FirstScreen() {

    val list = remember { mutableStateListOf<Message>() }
    val UserList = remember { mutableStateListOf<Message>() }
    val botList = listOf("Peter", "Francesca", "Luigi", "Igor")
    val random = (0..3).random()
    val botName: String = botList[random]
    val hashMap: HashMap<String, String> = HashMap<String, String>()
    val coroutineScope = rememberCoroutineScope()

    customBotMessage(message = Message1, list)

    Column(modifier = Modifier.fillMaxHeight(),
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.Top) {

        Image(
            painter = painterResource(id = R.drawable.diyetkolik_logo),
            contentDescription = "logo",
            modifier = Modifier.padding(10.dp)
        )

        Divider()
        val listState = rememberLazyListState()

        LazyColumn(modifier = Modifier.fillMaxSize(),
            state = listState) {
            println("size:" list.size)
            items(list.size) { i ->

                Row(
                    modifier = Modifier.fillMaxWidth(),
                    horizontalArrangement =
                    if (list[i].id == RECEIVE_ID)
                        Arrangement.Start
                    else
                        Arrangement.End
                ) {
                    if (list[i].id == RECEIVE_ID) {
                        Item(
                            message = list[i],
                            botName,
                            botcolor,
                            list,
                            simpleTextFlag = true,
                            hashMap,
                            modifier = Modifier
                                .padding(
                                    start = 32.dp,
                                    end = 4.dp,
                                    top = 4.dp
                                ),
                        )
                    } else {
                        Item(
                            message = list[i],
                            "user",
                            usercolor,
                            UserList,
                            simpleTextFlag = false,
                            hashMap,
                            Modifier.padding(
                                start = 4.dp,
                                end = 32.dp,
                                top = 4.dp
                            )
                        )
                    }
                }
            }
        }
        val endOfListReached = remember {
            derivedStateOf {
                listState.isScrolledToEnd() // error occurs Unresolved reference
            }
        }

        // act when end of list reached
        LaunchedEffect(endOfListReached) {
            // do your stuff
        }
    }
}

private fun customBotMessage(message: String, list: SnapshotStateList<Message>) {

    GlobalScope.launch {
        delay(1000)
        withContext(Dispatchers.Main) {
            list.add(
                Message(
                    message,
                    RECEIVE_ID,
                    Timestamp(System.currentTimeMillis()).toString()
                )
            )

        }
    }
}

@Composable
fun Item(
    message: Message,
    person: String,
    color: Color,
    list: SnapshotStateList<Message>,
    simpleTextFlag: Boolean,
    hashMap: HashMap<String, String>,
    modifier: Modifier
) {

    Column(verticalArrangement = Arrangement.Top) {
        Card(
            modifier = Modifier
                .padding(10.dp),
            backgroundColor = color,
            elevation = 10.dp
        ) {
            Row(
                verticalAlignment = Alignment.Top,
                modifier = Modifier.padding(3.dp)
            ) {
                Text(
                    buildAnnotatedString {
                        withStyle(
                            style = SpanStyle(
                                fontWeight = FontWeight.Medium,
                                color = Color.Black
                            )
                        ) {
                            append("$person: ")
                        }
                    },
                    modifier = Modifier
                        .padding(4.dp)
                )
                Text(
                    buildAnnotatedString {
                        withStyle(
                            style = SpanStyle(
                                fontWeight = FontWeight.W900,
                                color = Color.White//Color(/*0xFF4552B8*/)
                            )
                        )
                        {
                            append(message.message)
                        }
                    }
                )
            }
        }
        if (simpleTextFlag)
            SimpleText(list = list, hashMap)
    }
}

@OptIn(ExperimentalComposeUiApi::class, ExperimentalFoundationApi::class)
@Composable
fun SimpleText(list: SnapshotStateList<Message>, hashMap: HashMap<String, String>) {

    val coroutineScope = rememberCoroutineScope()
    val keyboardController = LocalSoftwareKeyboardController.current
    val bringIntoViewRequester = remember { BringIntoViewRequester() }
    var visible by remember { mutableStateOf(true) }

    var text by remember { mutableStateOf("") }

    AnimatedVisibility(
        visible = visible,
        enter = fadeIn()   slideInHorizontally(),
        exit = fadeOut()   slideOutHorizontally()
    ) {
        Row(
            verticalAlignment = Alignment.Bottom,
            horizontalArrangement = Arrangement.End,
            modifier = Modifier.bringIntoViewRequester(bringIntoViewRequester)
        ) {
            OutlinedTextField(
                modifier = Modifier
                    .padding(2.dp)
                    .onFocusEvent { focusState ->
                        if (focusState.isFocused) {
                            coroutineScope.launch {
                                bringIntoViewRequester.bringIntoView()
                            }
                        }
                    },
                keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
                keyboardActions = KeyboardActions(onDone = { keyboardController?.hide() }),
                value = text,
                onValueChange = { text = it },
                shape = RoundedCornerShape(12.dp),
                label = { Text("send message") })
            IconButton(onClick = {
                if (text.isNotEmpty()) {
                    //Adds it to our local list
                    list.add(
                        Message(
                            text,
                            Constants.SEND_ID,
                            Timestamp(System.currentTimeMillis()).toString()
                        )
                    )
                    hashMap.put(Messages.listOfMessageKeys[index - 1], text)
                    customBotMessage(listOfMessages[index], list)
                    index  = 1
                    visible = false
                }
                text = ""


            }) {
                Icon(
                    modifier = Modifier.padding(2.dp),
                    painter = painterResource(id = R.drawable.ic_baseline_send_24),
                    contentDescription = "send message img"
                )
            }
        }
    }
}

private fun customBotMessage(message: String, list: SnapshotStateList<Message>) {

    GlobalScope.launch {
        delay(1000)
        withContext(Dispatchers.Main) {
            list.add(
                Message(
                    message,
                    Constants.RECEIVE_ID,
                    Timestamp(System.currentTimeMillis()).toString()
                )
            )
        }
    }
}

I know it's badly written code and you might have trouble understanding it, so I'll explain how the code works for you. Briefly, the first screen that opens is FirstScreen and there is an empty list that I defined here, and by empty list I mean val list = remember { mutableStateListOf<Message>() } in FirstScreen. First, I call the customBotMessage function to interact with the user, and it adds the first message in the message list I prepared. When added to the list, it enters the lazycolumn and is displayed on the screen as an Item.

When going to Item, there is a message box in the Item and a textfield just below the message box where the user can enter an input, if the message incoming to Item is a user message, show this input textfield. I checked this with the id's in the if states in the lazycolumn in FirstScreen, you can understand if you look at the FirstScreen. And when the user enters a value in that input textfield, it is added to the same list and displayed on the screen, but as messages are added to the list, I want the screen to scroll down, as I said above, how can I do this in my code ?

CodePudding user response:

listState.isScrolledToEnd() // error occurs Unresolved reference

The function listState.isScrolledToEnd() currently doesn't exist in Compose.

Starting from 1.4.0-alpha03 you can use LazyListState#canScrollForward to check if you are at the end of the list.

val listState = rememberLazyListState()
val isAtBottom = listState.canScrollForward

Before 1.4.0-alpha03 you have to built a custom function like:

@Composable
private fun LazyListState.isAtBottom(): Boolean {

    return remember(this) {
        derivedStateOf {
            val visibleItemsInfo = layoutInfo.visibleItemsInfo
            if (layoutInfo.totalItemsCount == 0) {
                false
            } else {
                val lastVisibleItem = visibleItemsInfo.last()
                val viewportHeight = layoutInfo.viewportEndOffset   layoutInfo.viewportStartOffset

                (lastVisibleItem.index   1 == layoutInfo.totalItemsCount &&
                        lastVisibleItem.offset   lastVisibleItem.size <= viewportHeight)
            }
        }
    }.value
}

CodePudding user response:

Please set your LayColumn, You don't need to call scrollToEnd, you add to the list, the latest news will be displayed automatically:

LazyColumn(
     reverseLayout = true,
     state = listState,
     modifier = Modifier.fillMaxSize(),
     horizontalAlignment = Alignment.CenterHorizontally,
     verticalArrangement = Arrangement.Top) {
   // do something....
}
  • Related