MutableStateFlow not working with MutableList
Asked Answered
R

3

6

Here is my MutableStateFlow value I try to work with:

val songList: MutableStateFlow<MutableList<Song>> = MutableStateFlow(arrayListOf())

I need to observe changes (after methods like add, removeAt etc.) on my MutableList above but can't achieve that. My guess is since only the elements of the list change instead of the list itself, collect method doesn't get fired.

How can I achieve that using StateFlow if possible? If not, what is the correct way to do it?

Note that I need initial value of arrayListOf(), so LiveData probably won't be enough for me.

Robichaud answered 29/1, 2022 at 12:41 Comment(0)
C
2

Solution:

  1. Create helper extension function:

    fun <T> List<T>.mapButReplace(targetItem: T, newItem: T) = map {
        if (it == targetItem) {
            newItem
        } else {
            it
        }
    }
    
  2. React on action (ex: click event):

     val newStudent = student.copy(isSelected = !student.isSelected)
    
     viewModel.updateStudents(student, newStudent)
    
  3. Handle in ViewModel:

     fun updateStudents(currentStudent: Student, newStudent: Student) {
         val newList = _students.value.mapButReplace(currentStudent, newStudent)
    
         _students.value = newList
     }
    
Clow answered 29/1, 2022 at 18:40 Comment(2)
Thank you for the answer. So, it turns out for me that there is no direct way when observing list items.Robichaud
@Robichaud Compose has fixed this problem: https://mcmap.net/q/367601/-jetpack-compose-update-composable-when-list-changes.Clow
D
3

In your view model:

val songList: MutableStateFlow<MutableList<Song>> = MutableStateFlow(emptyList()) // modified

fun add(song: Song) {
    songList.update {
        songList.value.toMutableList().apply { this.add(song) }
    }
}

in a composable:

val songs = yourViewModel.songList.collectAsStateWithLifecycle()
Duodiode answered 18/11, 2022 at 16:47 Comment(1)
It would be good to declare the type as read-only List to avoid the possibility of accidentally mutating the list instead of copying it. val songList: MutableStateFlow<List<Song>> = ...Chewning
C
2

Solution:

  1. Create helper extension function:

    fun <T> List<T>.mapButReplace(targetItem: T, newItem: T) = map {
        if (it == targetItem) {
            newItem
        } else {
            it
        }
    }
    
  2. React on action (ex: click event):

     val newStudent = student.copy(isSelected = !student.isSelected)
    
     viewModel.updateStudents(student, newStudent)
    
  3. Handle in ViewModel:

     fun updateStudents(currentStudent: Student, newStudent: Student) {
         val newList = _students.value.mapButReplace(currentStudent, newStudent)
    
         _students.value = newList
     }
    
Clow answered 29/1, 2022 at 18:40 Comment(2)
Thank you for the answer. So, it turns out for me that there is no direct way when observing list items.Robichaud
@Robichaud Compose has fixed this problem: https://mcmap.net/q/367601/-jetpack-compose-update-composable-when-list-changes.Clow
R
1

For updating the MutableList inside the Stateflow, you have to map the item based on conditions like below, which I have accomplished, and it's performing nicely.

 _userList.value = _userList.value.map {
                if (it?.userName == updatedUser.userName) updatedUser
                else it
            }

Here _userList is a MutableStateFlow @Sam Sen's explanation assisted me a lot with my workaround solution.

Roomette answered 13/9, 2022 at 16:54 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.