0

I have a StateFlow from which my List composable collects any changes as a State.

private val _people = MutableStateFlow(personDataList())
val people = _people.asStateFlow()

And inside my viewModel, I perform modifications on _people and I verify that people as a read-only StateFlow is also getting updated. I also have to make a copy of the original _people as an ordinary kotlin map to use for some verifications use-cases.

val copyAsMap : StateFlow<MutableMap<Int, Person>> = people.map {
    it.associateBy( { it.id }, { it } )
        .toMutableMap()
}.stateIn(viewModelScope, SharingStarted.Eagerly, mutableMapOf())

however, with my attempt above, it (the copyAsMap) doesn't get updated when I try to modify the list (e.g delete) an item from the _people StateFlow

Any ideas..? Thanks!

Edit:

Nothing is collecting from the copyAsMap, I just display the values everytime an object is removed from _person state flow

delete function (triggered by an action somewhere)

private fun delete(personModel: Person) {
    _person.update { list ->
        list.toMutableStateList().apply {
            removeIf { it.id == personModel.id }
        }
    }

    copyAsMap.values.forEach {
        Log.e("MapCopy", "$it")
    }
}
z.g.y
  • 5,512
  • 4
  • 10
  • 36

1 Answers1

1

So based on your comment how you delete the item, that's the problem:

_people.update { list ->
    list.removeIf { it.id == person.id }
    list
}

You get an instance of MutableList here, do the modification and you "update" the flow with the same instance. And, as StateFlow documentation says:

Values in state flow are conflated using Any.equals comparison in a similar way to distinctUntilChanged operator. It is used to conflate incoming updates to value in MutableStateFlow and to suppress emission of the values to collectors when new value is equal to the previously emitted one.

Which means that your updated list is actually never emitted, because it is equal to the previous value.
You have to do something like this:

_people.update { list ->
    list.toMutableList().apply { removeIf { ... } }
}

Also, you should define your state as val _people: MutableStateFlow<List<T>> = .... This would prevent some mistakes you can make.

Jan Bína
  • 3,447
  • 14
  • 16
  • Thank you @Jan, I tried your suggestion, unfortunately, the `copyAsMap ` still dont get updated when I delete something from the `_people` state flow, apologies I didn't mention, nothing is collecting from `copyMap`, I'm just looping through its values everytime `_people` gets modified (remove an item in my case), maybe this is an intended behavior of StateFlow, and my use-case may not be something that this approach can solve, Ill edit my original post with additional functions – z.g.y Sep 23 '22 at 08:12
  • 1
    The copyAsMap should get updated, maybe you just have the logging wrong, MutableStateFlow.update is async, so if you call copyAsMap.values right after update, it might not be updated yet. You can try to do the logging like this: val copyAsMap = people.map{}.onEach{ log }.stateIn() – Jan Bína Sep 23 '22 at 09:40
  • Ohh there you go!, thank you @Jan, inside `onEach` I get the most recent update from the `_person`, I verify the size changes as I remove items from it, looks like I have to look further about `StateFlows`, I haven't been able to re-produce/re-code this issue on a smaller implementation, maybe there's something I don't notice yet, anyway Ill get back to this if I still encounter same issue with smaller scope of codes, thank you very much @Jan for the time, appreciate it! – z.g.y Sep 23 '22 at 09:50