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....
}