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

如何在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对象:

  1. 给Data添加深拷贝方法:
class Data() {
    var number: Int = 0
    fun foo() {}

    // 实现深拷贝,复制所有成员变量
    fun copy(): Data {
        val newData = Data()
        newData.number = this.number
        // 若有其他成员变量,需逐一复制
        return newData
    }
}
  1. 修改saveStateToUndoStack方法,完成深拷贝:
private fun saveStateToUndoStack(state: UiState) {
    // 复制列表中的每个Data对象,生成新列表
    val copiedDataList = state.dataList.map { it.copy() }.toMutableList()
    val copiedState = state.copy(dataList = copiedDataList)
    undoStack.add(copiedState)
}
  1. 调整状态更新逻辑,确保修改的是拷贝后的对象:
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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 02:25:15