1

In Jetpack/Desktop Compose I want a coroutine to run in response to changes to a SnapshotStateList.

In this example:

import androidx.compose.foundation.layout.Column
import androidx.compose.material.Button
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.remember

@Composable
fun TestMutableList() {
    val list = remember { mutableStateListOf(1, 2, 3) }

    LaunchedEffect(list) {
        println("List was changed.")
    }

    Column {
        Button(onClick = { list[0] = 0 }) {
            Text("Change List")
        }
        list.forEach { Text(it.toString()) }
    }
}

the LaunchedEffect was run on the first composition. And the Composable recomposes when I click the button, so it knows that the SnapshotStateList<Int> changed. However, it was not run when clicking the button. I understand that this is because the key is the reference to the SnapshotStateList<Int> and that did not change.

How can I have the LaunchedEffect run every time that the list is modified?

David Hadley
  • 184
  • 2
  • 8
  • You want to look at snapshotFlow, but again pay attention to the fact that the list object itself never changes, so don't return that from the snapshotFlow directly: https://stackoverflow.com/questions/70404434 – Uli Sep 29 '22 at 21:33

3 Answers3

3

With convert SnapshotStateList to ImmutableList, you can achieve to aim.

@Composable
fun TestMutableList() {
    val list = remember { mutableStateListOf(1, 2, 3) }

    LaunchedEffect(list.toList()) {
        println("List was changed.")
    }

    Column {
        Button(onClick = { list[0] = 0 }) {
            Text("Change List")
        }
        list.forEach { Text(it.toString()) }
    }
}
Erfan Sn
  • 89
  • 1
  • 6
  • 1
    i don't know where you got this stuff but works man, thank you, can you share the reference link where got to know __.toList()__ works – Mohd Qasim Apr 25 '23 at 13:30
2

I had the same problem and got it working using the list size instead of the list itself.

Like this:

val list = remember { mutableStateListOf(1, 2, 3) }

LaunchedEffect(list.size) {
    println("List was changed.")
}
nhcodes
  • 1,206
  • 8
  • 20
1

You can update an integer for anytime you change list so it will trigger when that value is changed

val list = remember { mutableStateListOf(1, 2, 3) }

var changeIndex by remember {
    mutableStateOf(0)
}

LaunchedEffect(list.size, changeIndex) {
    // add an if here if you don't want to trigger when changeIndex is 0
    println("List was changed.")
}

Column {
    Button(onClick = { list[0] = 0 }) {
        changeIndex ++
        Text("Change List")
    }
    list.forEach { Text(it.toString()) }
}
Thracian
  • 43,021
  • 16
  • 133
  • 222