Home > Enterprise >  Recommposition is not happening when i update a MutableStateFlow<List<AttendanceState>>
Recommposition is not happening when i update a MutableStateFlow<List<AttendanceState>>

Time:10-25

There is a viewmodel VM. There is one variable v of type MutableStateFlow<List>.

var attendanceStates = MutableStateFlow<List<AttendanceState>>(arrayListOf())
    private set

Model : AttendanceState

data class AttendanceState (
    var memberId: Int,
    var memberName: String = "",
    var isPresent: Boolean = false,
    var leaveApplied: Boolean = false
)

The variable is mapped to a LazyColumn

The Lazy column contains Checkboxes. If i update the checkbox, the event is propagated to VM and from there I'm changing the value of v

attendanceStates.value[event.index].copy(leaveApplied = event.hasApplied)
attendanceStates.value = attendanceStates.value.toList()

But this is not updating the LazyColumn.

In the Composable Function,

val attendances by viewModel.attendanceStates.collectAsState()

LazyColumn(modifier = Modifier.fillMaxWidth().padding(top = 24.dp)) {
        Log.e("Attendance","Lazy Column Recomposition")
        items(attendances.size) { index ->
            AttendanceCheckBox(attendanceState = attendances[index], modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp), onAttendanceStatusChangeListener = { viewModel.onEvent(AttendanceEvent.IsPresentStatusChanged(index, it)) }, onLeaveAppliedStatusChangeListener = { viewModel.onEvent(AttendanceEvent.IsLeaveAppliedStatusChanged(index, it)) })
        }
    }

But recomposition is not happening.

CodePudding user response:

Try this:

viewModelScope.launch {
    val helper = ArrayList(attendanceStates.value)
    helper[event.index] = helper[event.index].copy(leaveApplied = event.hasApplied)
    attendanceStates.emit(helper)
}

Changing an item's properties will not trigger a StateFlow, you have to replace the whole item with the changed item and emit a new list.

CodePudding user response:

I would recommend SnapshotStateList instead of a standard List, this will guarantee an update without having to create a new instance of it like what you would do in an ordinary List, assuming you call AttendanceState instance copy() and updating at least one of its properties with a different value.

var attendanceStates = MutableStateFlow<SnapshotStateList>(mutableStateListOf())
    private set

I would also recommend changing the way you use your LazyColumn where items are mapped by their keys not just by their index position,

LazyColumn(modifier = Modifier.fillMaxWidth().padding(top = 24.dp)) {
        items(attendances, key = {it.memberId}) {
               AttendanceCheckBox(...)
        }
}

and if you still need the index position.

LazyColumn(modifier = Modifier.fillMaxWidth().padding(top = 24.dp)) {
       itemsIndexed(attendances, key = { index, item ->   
              item.memberId
       }) { item, index ->
              AttendanceCheckBox(...)
       }
}

You should also use update when updating your StateFlow instead of modifying its value directly to make it concurrently safe.

attendanceStates.update { list ->
      val idx = event.idx
      list[idx] = list[idx].copy(leaveApplied = event.hasApplied)
      list
}
  • Related