https://kotlinlang.org logo
#coroutines
Title
# coroutines
g

gts13

04/28/2021, 3:00 PM
this might be a noob question but today I realized that StateFlow doesn’t work very well (or better saying intentionally doesn’t work) with Lists. So if we have the below (very rough example)
Copy code
data class ViewItem(
  val id: String,
  val title: String,
  var description: String = "" // yes a var
)

private val _items: MutableStateFlow<List<ViewItem>> = MutableStateFlow(emptyList())
val metrics: StateFlow<List<ViewItem>> = _metrics

_metrics.value = _metrics.value.toMutableList().find { // some predicate }?.description = "a new title"
The
_metrics
won’t emit the new value (which is actually a change in the
ViewItem
object. Am I right or I am missing something here? And If I am right, in order to solve the above “issue” I need to use
Channel
or is there any other way with
StateFlow
?
c

Casey Brooks

04/28/2021, 3:07 PM
The last line of your snippet isn’t a valid Kotlin expression. Is this the logic you’re actually using? Or could you post the actual code that’s causing issues?
Copy code
val currentList = _metrics.value.toMutableList()
currentList.find { it.id == "1" }?.description = "a new title"
_metrics.value = currentList
f

Francesc

04/28/2021, 3:08 PM
MutableStateFlow
is not aware to changes to your list. To trigger a new emission you have to make a new copy of your state with the list updated instead.
g

gts13

04/28/2021, 3:10 PM
thanks for your quick answers! @Casey Brooks this could be more valid ?
Copy code
val tempList = _metrics.value.toMutableList()
tempList.find{ // }...
_metrics.value = tempList
@Francesc like this? Becaause doesnt work. Or could you please tell me how?
ah ok sorry now I read it again. A copy of the state. Ok didnt try this
Although I am not sure how to do this
c

Casey Brooks

04/28/2021, 3:14 PM
StateFlow emissions are conflated with
Any.equals
. If the result of your modifications changes the equality property of the list, then it should be emitted. A List is equal to another List when it contains the same contents in the same order. So to get a “different” list, you either need to add an item, remove an item, or cause one item to become not equal to a previous one. However, mutable properties are very problematic for things like this. When you modify a
ViewItem
, it gets the new value in both the new and old lists. Even though the values of the objects are now different than they were, the two lists are still equal because they share the same instance of
ViewItem
. This is likely your problem
To fix it, use immutable data objects and
.copy
it to receive a new instance that is not shared between the two lists
This example should help you understand what’s going on. Because of that mutable property, changes to the mutable list result in an unintentional change to the original list, where the two lists remain equal to each other https://pl.kotl.in/NOpp6t3qZ
❤️ 2
And this example shows how to fix it. Simply by copying each item in the first list to the second, we give each one their own instance to work with. https://pl.kotl.in/Hgatgt3e4 But with mutable properties you have to keep this fact in mind, because the issues it causes can be very subtle. This is why it’s generally better to prefer fully immutable data classes and using their
.copy
method to make changes, as it forces you to do this correctly
❤️ 1
g

gts13

04/28/2021, 3:23 PM
thanks a lot Casey for the detailed explanation and for the examples.
just out of curiosity the channel would also solve this issue? Channel with UNLIMITED buffer
c

Casey Brooks

04/28/2021, 3:30 PM
Yes, because Channels by default do not conflate emissions. But that’s probably not a good reason to drop down to the Channel API, which is intended for lower-level control and brings its own set of challenges, not to mention the fact that a Channel does not hold a value once it has been consumed. A
SharedFlow
also does not conflate emissions like the
StateFlow
does, but also like the Channel it does not really hold onto a value. Its “replayCache” kinda does, but it doesn’t hold the strong guarantee that a StateFlow does, so you’d have to assume the “current value” could be null.
💯 1
5 Views