I am developing on moving the list(item) in LazyColumn Composable using Jetpack Compose and Kotlin.
The app I am implementing is the TodoList app, and if I explain the function, the item includes text and checkbox composable, and the position of the list (item) changes depending on the value (true, false). If the value of the check box changes to false -> true, the list (item) moves to the bottom of the sticky header to which it belongs, and if the value of the check box changes to true -> false, the list (item) moves to the original position of the sticky header to which it belongs.
The problem with the code I implemented is that if the value of the check box changes to false -> true, the list (item) is normally implemented to move to the bottom of the sticky header to which it belongs, but if the value of the check box changes to true -> false, the list (item) is not to the original place of the sticky header to which it belongs.
If the value of the check box changes to true -> false, it is too difficult to implement the function of moving the list(item) to the original position of the sticky header to which it belongs, so I ask the developers.
How can I fix it?
- Checkbox value: false -> true =>
onCheckedUpdateTodo()
- Checkbox value: true -> false =>
onUnCheckedUpdateTodo()
UpdateTodo
fun updateTodo(
token: String,
year: String,
month: String,
day: String,
title: String,
done: Boolean,
description: String,
color: String,
time: String,
id: String,
response: (UpdateTodoResponse?) -> Unit,
) {
var updateTodoResponse: UpdateTodoResponse? = null
var retrofit = Retrofit.Builder().baseUrl("https://plotustodo-ctzhc.run.goorm.io/")
.addConverterFactory(GsonConverterFactory.create()).build()
var updateTodoRequest: UpdateTodoRequest = retrofit.create(UpdateTodoRequest::class.java)
updateTodoRequest.requestUpdateTodo(
token,
id,
UpdateTodo(year, month, day, title, done, description, color, time)
)
.enqueue(object : Callback<UpdateTodoResponse> {
// 실패 했을때
override fun onFailure(call: Call<UpdateTodoResponse>, t: Throwable) {
Log.e("updateTodo", t.message.toString())
}
// 성공 했을때
override fun onResponse(
call: Call<UpdateTodoResponse>,
response: Response<UpdateTodoResponse>,
) {
if (response.isSuccessful) {
updateTodoResponse = response.body()
response(updateTodoResponse)
Log.d("updateTodo", "token : " + MyApplication.prefs.getData("token", ""))
Log.d("updateTodo", "resultCode : " + updateTodoResponse?.resultCode)
} else {
Log.e("updateTodo", "resultCode : " + response.body())
Log.e("updateTodo", "code : " + response.code())
}
}
})
}
readTodo
fun readTodo(
token: String,
year: String,
month: String,
day: String,
response: (ReadTodoResponse?) -> Unit,
) {
var readTodoResponse: ReadTodoResponse? = null
var retrofit = Retrofit.Builder().baseUrl("https://plotustodo-ctzhc.run.goorm.io/")
.addConverterFactory(GsonConverterFactory.create()).build()
var readTodoRequest: ReadTodoRequest = retrofit.create(ReadTodoRequest::class.java)
readTodoRequest.requestReadTodo(token, year, month, day)
.enqueue(object : Callback<ReadTodoResponse> {
//실패할 경우
override fun onFailure(call: Call<ReadTodoResponse>, t: Throwable) {
Log.e("readTodo", t.message.toString())
}
//성공할 경우
override fun onResponse(
call: Call<ReadTodoResponse>,
response: Response<ReadTodoResponse>,
) {
readTodoResponse = response.body()
response(readTodoResponse)
Log.d("readTodo", "token : " + MyApplication.prefs.getData("token", ""))
Log.d("readTodo", "resultCode : " + readTodoResponse?.resultCode)
Log.d("readTodo", "data : " + readTodoResponse?.data)
}
})
}
RToDoResponse
data class RToDoResponse(
val id: String,
val writer: String,
val year: String,
val month: String,
val day: String,
val title: String,
val description: String,
val done: Boolean,
val time: String,
val color: Int
)
TodoItem(One Item) & TodoList(LazyColumn)
@Composable
fun TodoItem(
Todo: RToDoResponse,
onTodoItemClick: (RToDoResponse) -> Unit,
onCheckedUpdateTodo: () -> Unit,
onUnCheckedUpdateTodo: () -> Unit
) {
var checked by rememberSaveable { mutableStateOf(Todo.done) }
val token = "Token ${MyApplication.prefs.getData("token", "")}"
var done by remember { mutableStateOf(false) }
LaunchedEffect(
key1 = Unit,
block = {
done = Todo.done
if (checked) {
done = true
checked = true
updateTodo(token,
Todo.year,
Todo.month,
Todo.day,
Todo.title,
done,
Todo.description,
Todo.color.toString(),
Todo.time,
Todo.id,
response = {
onCheckedUpdateTodo()
})
}
}
)
Card(colors = CardDefaults.cardColors(Color.White),
shape = RoundedCornerShape(8.dp),
modifier = Modifier
.fillMaxWidth()
.height(50.dp)
.clickable {
onTodoItemClick(Todo)
}) {
Row(
modifier = Modifier.padding(start = 7.dp, top = 15.dp, bottom = 15.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Center
) {
Checkbox(
checked = checked,
onCheckedChange = {
checked = it
if (checked) {
done = true
checked = true
updateTodo(token,
Todo.year,
Todo.month,
Todo.day,
Todo.title,
done,
Todo.description,
Todo.color.toString(),
Todo.time,
Todo.id,
response = {
onCheckedUpdateTodo()
})
} else {
done = false
checked = false
updateTodo(token,
Todo.year,
Todo.month,
Todo.day,
Todo.title,
done,
Todo.description,
Todo.color.toString(),
Todo.time,
Todo.id,
response = {
onUnCheckedUpdateTodo()
})
}
}, colors = CheckboxDefaults.colors(
when (Todo.color) {
1 -> Color(0xffFFB4B4)
2 -> Color(0xffFFDCA8)
3 -> Color(0xffB1E0CF)
4 -> Color(0xffB7D7F5)
5 -> Color(0xffFFB8EB)
6 -> Color(0xffB6B1EC)
else -> Color.Black
}
)
)
Text(text = Todo.title, fontSize = 13.sp, fontStyle = FontStyle.Normal)
}
}
}
@Composable
fun TodoItemList(
Todo: List<RToDoResponse>,
todoList: MutableList<RToDoResponse>,
onTodoItemClick: (RToDoResponse) -> Unit,
) {
val token = "Token ${MyApplication.prefs.getData("token", "")}"
LazyColumn(verticalArrangement = Arrangement.spacedBy(6.dp)) {
val grouped = Todo.groupBy { it.color }
grouped.forEach { (header, items) ->
stickyHeader {
Row(
modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically
) {
Button(
modifier = Modifier.size(9.dp),
onClick = { /*TODO*/ },
enabled = false,
content = {},
colors = ButtonDefaults.buttonColors(
disabledContainerColor = when (header) {
1 -> Color(0xffFFB4B4)
2 -> Color(0xffFFDCA8)
3 -> Color(0xffB1E0CF)
4 -> Color(0xffB7D7F5)
5 -> Color(0xffFFB8EB)
6 -> Color(0xffB6B1EC)
else -> Color.Black
}
)
)
Spacer(modifier = Modifier.padding(horizontal = 5.dp))
Text(
text = "그룹 $header",
fontWeight = FontWeight.Bold,
fontSize = 13.sp,
lineHeight = 17.sp
)
}
}
items(items = items, key = { Todo -> Todo.id }) { item ->
val dismissState = androidx.compose.material.rememberDismissState()
val dismissDirection = dismissState.dismissDirection
val isDismissed = dismissState.isDismissed(DismissDirection.EndToStart)
if (isDismissed && dismissDirection == DismissDirection.EndToStart) {
deleteTodo(token, item.id, response = {
todoList.remove(item)
readTodo(token, year = item.year, month = item.month, day = item.day) {
todoList.clear()
for (i in it!!.data) {
todoList.add(i)
}
}
})
}
androidx.compose.material.SwipeToDismiss(state = dismissState,
background = { DeleteBackground() },
directions = setOf(DismissDirection.EndToStart),
dismissContent = {
TodoItem(Todo = item,
onTodoItemClick = { onTodoItemClick(it) },
onCheckedUpdateTodo = {
todoList.removeAll {
it.id == item.id
}
todoList.add(item)
}, onUnCheckedUpdateTodo = {
todoList.removeAll { it.id == item.id }
todoList.add(
todoList.indexOfFirst { !it.done || it.id == item.id },
item.copy(done = false))
}
})
},
dismissThresholds = {
androidx.compose.material.FractionalThreshold(fraction = 0.2f)
})
}
}
}
}