https://kotlinlang.org logo
#compose
Title
# compose
c

Colton Idle

08/14/2022, 5:08 PM
I'm creating a flow out of snapshot state.
Copy code
val listFlow = snapshotFlow { appState.myList }
viewModelScope.launch {
  listFlow.collectLatest {
Log...
but even when I clear the list and addAll to it, my flow doesn't get fired again (I don't see my log). Am I misunderstanding snapshotFlow, or should this work?
s

Stylianos Gakis

08/14/2022, 7:55 PM
What's the type of
myList
?
c

Colton Idle

08/14/2022, 7:56 PM
mutableStateListOf<String>
s

Stylianos Gakis

08/14/2022, 9:18 PM
Right, when we use
stateFlow
, we have to use it with
T
where
T
has to be backed by a
MutableState
so that its observers get notified (this case the snapshotFlow has subscribed to see its changes and is observing that value), instead of passing a
State<T>
in the
stateFlow
lambda. That’s my understanding of it. So this does not work for `snapshotFlow`:
Copy code
val state: State<String> = produceState("") {
  while(isActive) {
    value = "one"
    delay(1.seconds)
    value = "two"
    delay(1.seconds)
  }
}
LaunchedEffect(Unit) {
  snapshotFlow { state }.collectLatest {
    println("state:$state")
  }
}
But this does:
Copy code
val state: String by produceState("") {
  while(isActive) {
    value = "one"
    delay(1.seconds)
    value = "two"
    delay(1.seconds)
  }
}
LaunchedEffect(Unit) {
  snapshotFlow { state }.collectLatest {
    println("state:$state")
  }
}
In your case you got the type
SnapshotStateList<T>
but you want to use
snapshotFlow
on your
<T>
instead. This does not work
Copy code
val state: SnapshotStateList<String> = mutableStateListOf("One", "Two")
LaunchedEffect(Unit) {
  while (isActive) {
    delay(1.seconds)
    state.add("one")
    delay(1.seconds)
    state.add("two")
    delay(1.seconds)
    state.clear()
  }
}
LaunchedEffect(Unit) {
  snapshotFlow { state }.collectLatest {
    println("state:$state")
  }
}
But this does
Copy code
This does not work
val state: SnapshotStateList<String> = mutableStateListOf("One", "Two")
LaunchedEffect(Unit) {
  while (isActive) {
    delay(1.seconds)
    state.add("one")
    delay(1.seconds)
    state.add("two")
    delay(1.seconds)
    state.clear()
  }
}
LaunchedEffect(Unit) {
  snapshotFlow { state.toList() }.collectLatest {
    println("state:$state")
  }
}
a

Albert Chang

08/15/2022, 4:15 AM
See this thread. Read the values of the list in the lambda of
snapshotFlow
.
s

Stylianos Gakis

08/15/2022, 7:26 AM
Can’t believe I didn’t think of just
.toList()
😅 So for your original post do instead
Copy code
val listFlow = snapshotFlow { appState.myList.toList() }
viewModelScope.launch {
  listFlow.collectLatest {
Log...
c

Colton Idle

08/15/2022, 1:34 PM
No way. All I had to do was
.toList()
Oh man. so much time wasted debugging this yesterday. lmaooo
s

Stylianos Gakis

08/15/2022, 1:44 PM
I was looking into the
SnapshotStateList
class to see if something that can extract the values exists and there wasn’t anything in there aside from
get(index)
. But my brain completely forgot to make the connection that
SnapshotStateList
is in fact a
MutableList
which is a
List
which is a
Collection
which is a
Iterable
which you can call the
public fun <T> Iterable<T>.toList(): List<T>
function on it. Felt like I was back in the CS class on this one, learning Java inheritance and interfaces 😅
c

Colton Idle

08/15/2022, 2:41 PM
I don't really know if this is what happens but I would think .toList() is "wasteful" because... It's already a list so why waste the time converting it to a list again?
s

Stylianos Gakis

08/15/2022, 2:43 PM
I don’t think this converts anything in a wasteful manner. It simply gets the underlying list that is saved inside the
SnapshotStateList
and since you’re doing it inside the
snapshowFlow
it also subscribes to any changes it may get. Could be wrong, but I wouldn’t even think about it.
c

Chrimaeon

08/15/2022, 2:45 PM
Memory-wise it is wastfull.
toList()
will create a new list with the same content.
s

Stylianos Gakis

08/15/2022, 3:03 PM
Yeah but what other option does one have in this case?
c

Chrimaeon

08/15/2022, 3:08 PM
I’d probably change my architechture. Not sure about your use case. But sounds kind of hacky what you are doing. Why do you meet the state then in the first place if you use it as a flow. There is
channelFlow
builder that you can use to emit your “state”.
z

Zach Klippenstein (he/him) [MOD]

08/15/2022, 4:04 PM
You can use
snapshotFlow
as the full restart scope. Eg you could do something like this:
Copy code
viewModelScope.launch {
  snapshotFlow {
    appState.myList.forEach // or w/e
    // put all your list processing here
    // return type doesn't matter
  }.collect()
}
c

Colton Idle

08/15/2022, 4:44 PM
Interesting. I just want to know when the list is cleared or items are added. So maybe a for each will get the job done.
45 Views