如何让rememberSaveable在movableContentOf中正常生效?
问题根因
movableContentOf 会在结构变化(比如父容器从Row切换为Column、配置变更触发重组)时,将内部的Composable节点从旧位置直接移动到新位置,避免节点重新创建。但它默认不会为内部内容绑定稳定的状态保存键:
- 普通
remember声明的状态直接绑定在movableContent持有的节点实例上,节点移动过程中没有被销毁,所以状态能正常保留,就是示例里第一个Checkbox状态不丢的原因。 rememberSaveable的状态是通过视图树中的位置键做索引,存在SaveableStateRegistry里的。当movableContent在不同父容器间移动时,系统会判定旧位置的节点已被移除,直接清除对应位置键绑定的保存状态,旋转后自然读不到之前存的值。
修复方案
二选一即可,都能解决状态丢失问题:
- 方案1:为movableContent绑定固定唯一键
在两个调用movableContent的位置,用相同的固定key包裹内容,保证不管外层容器怎么切换,状态保存用的索引键始终不变,SaveableStateRegistry就能正确关联到之前存储的状态:Column( verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally, ) { val content = remember { movableContentOf { var a by remember { mutableStateOf(false) } Checkbox(a, { a = it }) var b by rememberSaveable { mutableStateOf(false) } Checkbox(b, { b = it }) } } var switch by rememberSaveable { mutableStateOf(false) } Checkbox(switch, { switch = it }) // 两个分支使用完全相同的key,保证状态索引稳定 if (switch) Row { key("fixed_nav_content_key") { content() } } else Column { key("fixed_nav_content_key") { content() } } } - 方案2:将需要跨配置保存的状态提升到movableContent外层
把所有用rememberSaveable声明的状态提到movableContent的外部持有,通过参数传给内部组件,状态的保存逻辑完全不受节点移动、容器切换的影响,稳定性更高:Column( verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally, ) { // 状态提升到外层,依托外层稳定的位置完成保存 var b by rememberSaveable { mutableStateOf(false) } val content = remember { movableContentOf { var a by remember { mutableStateOf(false) } Checkbox(a, { a = it }) Checkbox(b, { b = it }) } } var switch by rememberSaveable { mutableStateOf(false) } Checkbox(switch, { switch = it }) if (switch) Row { content() } else Column { content() } }
如果是开发适配不同屏幕尺寸的导航类组件,优先选方案2,状态统一在上层管理,后续新增其他尺寸的布局适配时,也不会因为movableContent的位置变化触发状态丢失。
内容的提问来源于stack exchange,提问作者Vitalis11
相关产品推荐
相关产品推荐

