I am making a chat bot application. I've searched in many places to solve this problem but couldn't find anything clear. I have an empty list. Bot messages and user messages are added to this list, but it is empty at first. I show this list with the help of lazycolumn. Since it is empty at first, no message is displayed on the screen, but at first I showed a bot message on the screen so that the user can interact with the user, and in response, the user enters a message, if the user enters a message, I show a bot message again and in the same way, the user responds to this bot message. enters a message. In short, there is a loop like this, but I want the screen to scroll when the messages reach the end of the screen. I couldn't find how to do this because I don't have a specific size list at first it is filled with empty user responses and bot messages. In the second question in my mind, the screen size of each phone is different, so I understand that the questions come to the end of the screen and overflow, how do I scroll, because as I said, this will vary according to the screen of the phone. I will share my codes and screenshot, you will better understand what I mean.
@SuppressLint("CoroutineCreationDuringComposition")
@Composable
fun FirstScreen(
viewModel: ChatBotViewModel = hiltViewModel()
) {
val list = 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()
viewModel.customBotMessage(message = Message1, list)
Column(modifier = Modifier.fillMaxHeight(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Top) {
Divider()
LazyColumn(
modifier = Modifier.fillMaxSize(),
){
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,
list,
simpleTextFlag = false,
hashMap,
Modifier.padding(
start = 4.dp,
end = 32.dp,
top = 4.dp
)
)
}
}
}
}
}
}
ITEM
@Composable
fun Item(
message: Message,
person: String,
color: Color,
list: SnapshotStateList<Message>,
simpleTextFlag: Boolean,
hashMap: HashMap<String, String>,
modifier: Modifier,
viewModel: ChatBotViewModel = hiltViewModel()
) {
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 && list.size != 13) {
SimpleText(list = list, hashMap, viewModel)
}
}
}
@OptIn(ExperimentalComposeUiApi::class, ExperimentalFoundationApi::class)
@Composable
fun SimpleText(
list: SnapshotStateList<Message>,
hashMap: HashMap<String, String>,
viewModel: ChatBotViewModel,
) {
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()
) {
Column {
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[Messages.listOfMessageKeys[viewModel.index.value - 1]] = text
viewModel.customBotMessage(listOfMessages[viewModel.index.value], list)
viewModel.index.value += 1
visible = false
}
text = ""
}) {
Icon(
modifier = Modifier.padding(2.dp),
painter = painterResource(id = R.drawable.ic_baseline_send_24),
contentDescription = "send message img"
)
}
}
if (list.size == 3 || list.size == 5 || list.size == 7) {
Button()
if(viewModel.isClickedBtn.value){
visible = false
viewModel.isClickedBtn.value = false
list.add(
Message(
text,
Constants.SEND_ID,
Timestamp(System.currentTimeMillis()).toString()
)
)
hashMap[Messages.listOfMessageKeys[viewModel.index.value - 1]] = text
viewModel.customBotMessage(listOfMessages[viewModel.index.value], list)
viewModel.index.value += 1
}
}
}
}
}
@Composable
fun Button(
viewModel: ChatBotViewModel = hiltViewModel()
) {
Button(
onClick = { viewModel.isClickedBtn.value = true },
) {
Text("skip")
}
}
ChatBotViewModel
class ChatBotViewModel @Inject constructor() : ViewModel() {
var index = mutableStateOf(value = 1)
val isClickedBtn = mutableStateOf(false)
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()
)
)
}
}
}
}
There is 1 more message(user:dsdssd)
after the last user message, it should automatically slide there. If this happens, the user has to scroll manually, which is a very bad user experience.