Jetpack Compose 复选框未显示已更改状态

Jetpack Compose Checkbox Not Showing Changed State

我有一个专业列表,用户将从中检查。在用户对他们的选择感到满意后,我将收集所选复选框的信息并将这些专业保存在用户的个人资料中。

但是,当我点击任何复选框时,视图模型中的状态会发生变化,但复选框仍未选中。

专业列表

@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)
                    )
                }
            }
        }
    }
}

专业视图模型

@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)
    )
}

专业模型

data class Specialty(
    val name: String,
    var isSelected: Boolean
)

这显然不是 Compose 的工作方式。在文档中阅读更多内容,但目前,修复方法是用此

替换现有的 VM 实现
@HiltViewModel
class SpecialtyListViewModel @Inject constructor() : ViewModel() {

    // HARD-CODED PROPERTIES
    val specialtyList = mutableStateListOf(
            Specialty(name = "Emergency Medicine", isSelected = false),
            Specialty(name = "Forensic Medicine", isSelected = false),
            Specialty(name = "General Practitioner", isSelected = false)
    )
}

您不能在对 mutableStateOf 的调用中包装任何内容并期望它触发重组。看到这个问题:Create Custom MutableState<T> Holders

编辑:根据下面@Philip 对您的 post 的评论,我注意到您也错误地实施了 LazyColumn。您必须将每个元素放在单独的 item 中,以便列有效地缓存和丢弃每个需要的元素。

@Composable
fun SpecialtyList(
    viewModel: SpecialtyListViewModel = hiltViewModel(),
    navController: NavController
) {
    LazyColumn(modifier = Modifier.fillMaxSize()) {    
         viewModel.specialtyList.value.forEach { specialty ->
             item{
                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)
                    )
                }
            }
        }
    }
}

这是一个小的交换。您还可以利用其他方式来实现这一点,例如,使用 items 块。

为了mutableStateOf触发重组,应该更新容器值,例如specialtyList = newValue.

它无法知道您更改了其中一个内部对象。

一般来说,data class 是一个很好的工具,可以在函数式编程中不可变地使用。如果你不会在其中使用 var,你将排除很多错误。

如果您的情况看起来更干净,请使用 mutableStateListOf。在您的视图模型中:

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)
}

像这样从您的可组合项中使用它:

viewModel.specialtyList.value.forEachIndexed { i, specialty ->
    // ...
    onCheckedChange = {
        viewModel.setSpecialtySelectedAtIndex(i, it)
    },
}

您可以在 documentation, including this youtube video 中找到有关 Compose 状态的更多信息,其中解释了基本原则。