1

I have a lazyColumn of todo item

data class TodoItem(val id: Int, val title: String, var urgent: Boolean = false)


class AppViewModel : ViewModel() {
    private var todoList = mutableListOf(
        TodoItem(0, "My First Task"),
        TodoItem(1, "My Second Task", true),
        TodoItem(2, "My Third Task")
    )
    private val _todoListFlow = MutableStateFlow(todoList)

    val todoListFlow: StateFlow<List<TodoItem>> get() = _todoListFlow

    fun setUrgent(index: Int, value: Boolean) {
        val modified = todoList.toMutableList()
        modified[index] = modified[index].copy(urgent = value)
        todoList = modified
        _todoListFlow.value = modified
    }
}

And my lazyColumn as below where I can update the urgent value accordingly

@Composable
fun Greeting(name: String) {
    val todoListState = viewModel.todoListFlow.collectAsState()

    LazyColumn(modifier = Modifier.fillMaxHeight()) {
        items(items = todoListState.value, itemContent = { item ->
            Row(
                modifier = Modifier.fillMaxWidth(),
                verticalAlignment = Alignment.CenterVertically
            ) {
                Text(
                    modifier = Modifier
                        .weight(1f)
                        .padding(8.dp),
                    text = item.title
                )
                Checkbox(
                    checked = item.urgent,
                    onCheckedChange = {
                        val index = todoListState.value.indexOf(item)
                        viewModel.setUrgent(index, it)
                    }
                )
            }
         })
    }
}

All these works fine.

But I feel the function below is strange, where I have to make a new list to update the StateFlow

    fun setUrgent(index: Int, value: Boolean) {
        val modified = todoList.toMutableList()
        modified[index] = modified[index].copy(urgent = value)
        todoList = modified
        _todoListFlow.value = modified
    }

I cannot just update the todoList as below, as it will not work

    fun setUrgent(index: Int, value: Boolean) {
        todoList[index] = todoList[index].copy(urgent = value)
        _todoListFlow.value = todoList
    }

Is there a way to not needing to manually generate a modified todoList to update it?

Elye
  • 53,639
  • 54
  • 212
  • 474

1 Answers1

2

I can get it to work by converting my todoList in my ViewModel from a List to a mutableStateListOf

   private var todoList = mutableStateListOf(
        TodoItem(0, "My First Task"),

Then I can change

    fun setUrgent(index: Int, value: Boolean) {
        todoList[index] = todoList[index].copy(urgent = value)
    }

Also in my compose function, I no longer need collectAsState()

val todoListState = viewModel.todoListFlow

Though this work, I feel it's odd for me to have MutableStateList in the ViewModel, and to pass through the StateFlow.

Elye
  • 53,639
  • 54
  • 212
  • 474
  • compose cannot tell if you modified your list, so mutableStateList is the only direct solution. – AagitoEx Jan 04 '23 at 11:31
  • And the original link https://stackoverflow.com/questions/74699081/jetpack-compose-lazy-column-all-items-recomposes-when-a-single-item-update/74700668#74700668 – Thracian Jan 07 '23 at 17:56
  • Thanks @Thracian for reaching out. Your original answer is not compilable. After make some change, I got something compilable, hence post the answer there. Now I have updated put some explanation there. https://stackoverflow.com/a/75003134/3286489 – Elye Jan 08 '23 at 06:54
  • This question in this stackoverflow is different from https://stackoverflow.com/q/74967044/3286489. This is actually asking is it okay to put mutableStateList in ViewModel. Each question is different [1] https://stackoverflow.com/questions/74960999/how-to-update-an-item-in-array-with-a-lazy-column-item-change this one I learned about mutableStateOf [2] https://stackoverflow.com/questions/74967044/how-to-update-the-original-list-when-snapshotstatelist-is-updated this one I lean how to update a list [3] This StackOverflow - this one is asking is mutableStateOf should live in ViewmModel? – Elye Jan 08 '23 at 06:57
  • @Elye what i'm saying is i already posted everything that follows in original comment in this link. How to use SnapStateStateList with a list, how to use SnapShotStateList instead of duplicate list and how to use it with ViewModel here. https://stackoverflow.com/questions/74699081/jetpack-compose-lazy-column-all-items-recomposes-when-a-single-item-update/74700668#74700668 in your first question. And, also my mistake i put second answer without testing, SnapshotStateList has addAll but not a single item add but you should have asked about it instead of ignoring – Thracian Jan 08 '23 at 07:15
  • Also i explained why your answer works showing source code in this answer. https://stackoverflow.com/a/74968199/5457853 – Thracian Jan 08 '23 at 07:15
  • Thanks. If you can help edit the answer there with the correct syntax etc, I will prefer your answer over mine, as your has more elaboration. – Elye Jan 08 '23 at 07:24
  • Even for this StackOverflow question, if you can also provide the answer if it is okay for MutableStateList in ViewModel, I would also think your answers if the make sense to the question. Thanks! – Elye Jan 08 '23 at 07:25
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/250935/discussion-between-elye-and-thracian). – Elye Jan 08 '23 at 07:25