So I'm updating my RecylerView
with StateFlow<List>
like following:
My data class:
data class Student(val name: String, var isSelected: Boolean)
My ViewModel logic:
fun updateStudentsOnSelectionChanged(targetStudent: Student) {
val targetIndex = _students.value.indexOf(targetStudent)
val isSelected = !targetStudent.isSelected
_students.value[targetIndex].isSelected = isSelected //<- doesn't work
}
Problem: The UI is not changed, but the isSelected
inside _student
is changed, what's going on? (same to LiveData
)
CodePudding user response:
I assume _students
is a StateFlow<List>
. Changing the isSelected
property of the Student
model doesn't trigger the StateFlow
. The workaround would be to make the isSelected
property of the Student
data class immutable to get it compared when new state is set, create a MutableList
out of the current list and copy the existing Student
object with the new value for isSelected
property:
data class Student(val name: String, val isSelected: Boolean)
val students = _students.value.toMutableList()
students[targetIndex] = students[targetIndex].copy(isSelected = isSelected)
_students.value = students
CodePudding user response:
Ok, thanks to @Tenfour04 and @Sergey, I finally found out that StateFlow/LiveData
cannot detect the internal changes, that's because they are both actually comparing the Reference of the .value
.
That means, If I want to force the StateFow<List>
to update, the only way is to assign a new List
to it, therefore I created the following helper extension function:
fun <T> List<T>.mapButReplace(targetItem: T, newItem: T) = map {
if (it == targetItem) {
newItem
} else {
it
}
}
//this function returns a new List with the content I wanted
In Fragment
:
val itemCheckAction: (Student) -> Unit = { student ->
val newStudent = student.copy(isSelected = !student.isSelected) //prepare a new Student
viewModel.updateStudentsOnSelectionChanged(student, newStudent) //update the StateFlow
}
In ViewModel
:
fun updateStudentsOnSelectionChanged(currentStudent: Student, newStudent: Student) {
val newList = _students.value.mapButReplace(currentStudent, newStudent)
_students.value = newList //assign a new List with different reference
}