如何在Jetpack Compose中快照不受后续影响的MutableStateFlow
解决撤销/重做栈中状态被原状态联动修改的问题
你的问题核心是引用传递导致的浅拷贝问题:
Data是可变类,成员变量number用var修饰,修改时直接改变原对象的状态UiState作为data class,调用copy()时只会对dataList做浅拷贝——栈里保存的UiState和当前_uiState里的dataList指向同一个列表实例,列表里的Data对象也是同一个引用。所以修改原状态的Data时,栈里的状态也会跟着变。
具体解决方案
方案1:将Data改为不可变数据类
把Data改成不可变的data class,强制每次修改都生成新对象,从根源避免引用共享:
data class Data( val number: Int = 0 ) { fun foo() {} // 保留原有方法 }
修改状态时,通过创建新的Data对象来更新值,而不是修改原对象:
fun makeChangeToState() { saveStateToUndoStack(_uiState.value) Log.i("Test", undoStack.last().dataList[0].number) // 0 // 创建新的列表,替换指定位置的Data对象 val updatedList = _uiState.value.dataList.mapIndexed { index, data -> if (index == 0) data.copy(number = 1) else data }.toMutableStateList() _uiState.update { currentState -> currentState.copy(dataList = updatedList) } Log.i("Test", undoStack.last().dataList[0].number) // 0,栈内状态不受影响 }
方案2:实现状态的深拷贝(保留Data可变的情况)
如果无法将Data改为不可变,需要在保存状态到撤销栈时,对UiState做深拷贝,复制整个列表及其中的每个Data对象:
- 给
Data添加深拷贝方法:
class Data() { var number: Int = 0 fun foo() {} // 实现深拷贝,复制所有成员变量 fun copy(): Data { val newData = Data() newData.number = this.number // 若有其他成员变量,需逐一复制 return newData } }
- 修改
saveStateToUndoStack方法,完成深拷贝:
private fun saveStateToUndoStack(state: UiState) { // 复制列表中的每个Data对象,生成新列表 val copiedDataList = state.dataList.map { it.copy() }.toMutableList() val copiedState = state.copy(dataList = copiedDataList) undoStack.add(copiedState) }
- 调整状态更新逻辑,确保修改的是拷贝后的对象:
fun makeChangeToState() { saveStateToUndoStack(_uiState.value) Log.i("Test", undoStack.last().dataList[0].number) // 0 // 先复制原列表中的Data对象,再修改新对象的值 val updatedList = _uiState.value.dataList.map { it.copy() }.toMutableStateList() updatedList[0].number = 1 _uiState.update { currentState -> currentState.copy(dataList = updatedList) } Log.i("Test", undoStack.last().dataList[0].number) // 0,栈内状态不受影响 }
为什么之前的方案无效?
- 直接使用
_uiState.value.copy:仅做浅拷贝,dataList的引用未改变,内部Data对象还是同一实例,修改原状态时栈内状态会同步变化。 - 在Composable中传入
collectAsState()的值:collectAsState()返回的状态快照里,dataList依然是原引用,本质还是浅拷贝问题。
内容的提问来源于stack exchange,提问作者Rex
相关产品推荐
相关产品推荐

