I have a list of specialties from which the user will check. After the user is happy with their selection, I will gather the information for selected checkboxes and save those specialties in the user's profile.
However, when I tap any of the checkboxes, the state in the view model changes but checkbox is still unchecked.
Specialty List
@Composable
fun SpecialtyList(
viewModel: SpecialtyListViewModel = hiltViewModel(),
navController: NavController
) {
LazyColumn(modifier = Modifier.fillMaxSize()) {
item {
viewModel.specialtyList.value.forEach { specialty ->
Row(
modifier = Modifier
.fillMaxWidth()
.padding(10.dp),
verticalAlignment = Alignment.CenterVertically
) {
Checkbox(
checked = specialty.isSelected,
onCheckedChange = {
specialty.isSelected = it
},
colors = CheckboxDefaults.colors(MaterialTheme.colors.primary)
)
Text(
text = specialty.name,
modifier = Modifier
.padding(horizontal = 10.dp)
)
}
}
}
}
}
Specialty View Model
@HiltViewModel
class SpecialtyListViewModel @Inject constructor() : ViewModel() {
// HARD-CODED PROPERTIES
val specialtyList = mutableStateOf(
mutableListOf(
Specialty(name = "Emergency Medicine", isSelected = false),
Specialty(name = "Forensic Medicine", isSelected = false),
Specialty(name = "General Practitioner", isSelected = false)
)
}
Specialty Model
data class Specialty(
val name: String,
var isSelected: Boolean
)
In order for mutableStateOf
to trigger recomposition, the container value should be updated, e.g. specialtyList = newValue
.
There's no way how it can know you've changed one of inner objects.
Generally speaking, data class
is a great tool to be used immutable in functional programming. If you won't use var
inside them, you'll exclude many mistakes.
Using mutableStateListOf
if your case seems much cleaner. In your view model:
private val _specialtyList = mutableStateListOf(
Specialty(name = "Emergency Medicine", isSelected = false),
Specialty(name = "Forensic Medicine", isSelected = false),
Specialty(name = "General Practitioner", isSelected = false)
)
val specialtyList: List<Specialty> = _specialtyList
fun setSpecialtySelectedAtIndex(index: Int, isSelected: Boolean) {
_specialtyList[index] = _specialtyList[index].copy(isSelected = isSelected)
}
Use it from your composable like this:
viewModel.specialtyList.value.forEachIndexed { i, specialty ->
// ...
onCheckedChange = {
viewModel.setSpecialtySelectedAtIndex(i, it)
},
}
You can find more info about state in Compose in documentation, including this youtube video which explains the basic principles.