You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何借助SavedStateHandle为多个Chip创建单个ViewModel

解决多个Chip独立状态并通过SavedStateHandle持久化的方案

问题根源

当前ViewModel仅用单个CHECKED_STATE_KEY存储布尔值,所有Chip绑定同一状态,导致点击任意Chip时全量同步变化。要实现独立状态,需为每个Chip分配唯一状态键,通过SavedStateHandle存储多组键值对。

步骤1:修改ViewModel

将单个布尔状态改为Chip标识与选中状态的映射集合,同时提供单Chip状态的获取与更新方法:

class SavedStateViewModel(private val state: SavedStateHandle) : ViewModel() {
    // 用Map存储所有Chip的选中状态,初始为空即所有Chip默认未选中
    private val chipStates = state.getStateFlow(
        key = CHIP_STATES_KEY,
        initialValue = emptyMap<String, Boolean>()
    )

    // 获取指定Chip的选中状态,默认返回未选中
    fun getChipState(chipKey: String): Boolean {
        return chipStates.value[chipKey] ?: false
    }

    // 更新指定Chip的选中状态
    fun updateChipState(chipKey: String, isSelected: Boolean) {
        val currentStates = chipStates.value.toMutableMap()
        currentStates[chipKey] = isSelected
        state.set(CHIP_STATES_KEY, currentStates)
    }

    companion object {
        private const val CHIP_STATES_KEY = "chip_states"
    }
}

步骤2:调整Chip调用逻辑

在AllAnimals组件中,每个Chip使用自身文本作为唯一标识键,绑定对应状态,并在点击时触发对应键的状态更新:

@SuppressLint("UnusedMaterialScaffoldPaddingParameter", "UnusedMaterial3ScaffoldPaddingParameter")
@Composable
fun AllAnimals(
    navController: NavController,
    viewModel: SavedStateViewModel = viewModel(),
) {
    // 收集Chip状态流,转为Composable可观测的状态
    val chipStates by viewModel.chipStates.collectAsState()

    Scaffold(
        topBar = {
            TopAppBarAnimalsActivities(
                title = "Animals",
                buttonIcon = Icons.Filled.ArrowBack,
                onButtonClicked = { navController.popBackStack() }
            )
        }
    ) {
        Column(
            verticalArrangement = Arrangement.Center,
            horizontalAlignment = Alignment.CenterHorizontally
        ) {
            Column(
                modifier = Modifier.fillMaxSize(),
                verticalArrangement = Arrangement.Center,
                horizontalAlignment = Alignment.CenterHorizontally
            ) {
                Text(
                    text = "What is your favourite animal?",
                    fontSize = 25.sp,
                    fontWeight = FontWeight.Bold,
                    fontFamily = FontFamily.Default,
                    color = DarkBlue
                )

                Box(
                    modifier = Modifier
                        .fillMaxWidth(0.8F)
                        .fillMaxHeight(0.93F),
                ) {
                    Column(
                        modifier = Modifier.fillMaxSize(),
                        verticalArrangement = Arrangement.Top,
                        horizontalAlignment = Alignment.Start
                    ) {
                        Spacer(modifier = Modifier.height(20.dp))
                        Row {
                            Spacer(modifier = Modifier.width(5.dp))
                            SimpleChip(
                                text = "Dog",
                                isSelected = chipStates["Dog"] ?: false,
                                onChecked = { isSelected ->
                                    viewModel.updateChipState("Dog", isSelected)
                                },
                            )

                            Spacer(modifier = Modifier.width(5.dp))
                            SimpleChip(
                                text = "Horse",
                                isSelected = chipStates["Horse"] ?: false,
                                onChecked = { isSelected ->
                                    viewModel.updateChipState("Horse", isSelected)
                                },
                            )

                            Spacer(modifier = Modifier.width(5.dp))
                            SimpleChip(
                                text = "Guinea Pig",
                                isSelected = chipStates["GuineaPig"] ?: false,
                                onChecked = { isSelected ->
                                    viewModel.updateChipState("GuineaPig", isSelected)
                                },
                            )
                        }

                        Spacer(modifier = Modifier.height(20.dp))
                        Row {
                            Spacer(modifier = Modifier.width(5.dp))
                            SimpleChip(
                                text = "Rabbit",
                                isSelected = chipStates["Rabbit"] ?: false,
                                onChecked = { isSelected ->
                                    viewModel.updateChipState("Rabbit", isSelected)
                                },
                            )

                            Spacer(modifier = Modifier.width(5.dp))
                            SimpleChip(
                                text = "Reptiles",
                                isSelected = chipStates["Reptiles"] ?: false,
                                onChecked = { isSelected ->
                                    viewModel.updateChipState("Reptiles", isSelected)
                                },
                            )

                            Spacer(modifier = Modifier.width(5.dp))
                            SimpleChip(
                                text = "Cats",
                                isSelected = chipStates["Cats"] ?: false,
                                onChecked = { isSelected ->
                                    viewModel.updateChipState("Cats", isSelected)
                                },
                            )
                        }

                        Spacer(modifier = Modifier.height(20.dp))
                        Row {
                            Spacer(modifier = Modifier.width(5.dp))
                            SimpleChip(
                                text = "Other",
                                isSelected = chipStates["Other"] ?: false,
                                onChecked = { isSelected ->
                                    viewModel.updateChipState("Other", isSelected)
                                },
                            )
                        }
                    }
                }
            }
        }
    }
}

步骤3:保留原SimpleChip组件

你的SimpleChip组件无需修改,它已正确接收isSelected状态和点击回调,只需确保每个实例绑定不同的状态标识键即可:

@Composable
fun SimpleChip(
    text: String,
    isSelected: Boolean,
    selectedColor: Color = Color.DarkGray,
    onChecked: (Boolean) -> Unit,
) {
    Surface(
        onClick = { onChecked(!isSelected) },
        modifier = Modifier.padding(4.dp)
            .wrapContentSize()
            .border(
                width = 1.dp,
                color = if (isSelected) selectedColor else Color.LightGray,
                shape = RoundedCornerShape(20.dp)
            ),
        shape = RoundedCornerShape(16.dp),
        color = if (isSelected) LightGreen else Color.White,
    ) {
        Row(
            modifier = Modifier.padding(8.dp),
            horizontalArrangement = Arrangement.spacedBy(4.dp),
            verticalAlignment = Alignment.CenterVertically,
        ) {
            Text(
                text = text,
                fontSize = 18.sp,
                fontWeight = FontWeight.Bold,
                color = if (isSelected) Color.White else Color.Black,
            )
        }
    }
}

核心逻辑说明

  • 用Map<String, Boolean>存储所有Chip状态,每个Chip的唯一标识(如文本)作为键,确保状态独立隔离。
  • SavedStateHandle自动持久化该Map,配置变更(如屏幕旋转)后状态不会丢失。
  • 每个Chip点击时仅更新自身对应的键值对,不会影响其他Chip的选中状态。

内容的提问来源于stack exchange,提问作者Josef M

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 17:37:09