0

I'm trying to make an editor app that allows you to undo/redo changes. I want to achieve that by storing the ui state in stacks (ArrayDeque) and pop them back once the user hits undo/redo. But every time I stored the state in stack, after I made change to the state, the value in the stack is changed as well.

Is there a way to snapshot a state that won't be affected by future changes in the state flow?

My code looks something like this:

Data Class

class Data () {
    var number: Int = 0
    fun foo()
}

State Class

data class UiState(
    val dataList: MutableList<Data> = mutableListOf(),
)

ViewModel

class UiViewModel : ViewModel() {
    private val _uiState = MutableStateFlow(UiState())
    private val undoStack: ArrayDeque<UiState> = ArrayDeque()

    fun makeChangeToState() {
        saveStateToUndoStack(_uiState.value)

        Log.i("Test", undoStack.last().dataList[0].number) // 0

        val dataList= _uiState.value.dataList.toMutableStateList()
        dataList[0].number = 1
        _uiState.update { currentState ->
            currentState.copy(
                dataList = dataList,
            )
        }
        Log.i("Test", undoStack.last().dataList[0].number) // 1 because the _uiState changed
    }

    fun undo() {
        val lastState = undoStack.last()
        
        // Won't work because the data in lastState has already been updated with _uiState
        _uiState.update { lastState }
    }
}

Things I've tried:

  • Use _uiState.value.copy
  • Call saveStateToUndoStack(uiState: UiState) from Composable functions and pass in the viewModel.uiState.collectAsState()

Both doesn't seem to work, I play around for a few hours but don't have a clue.

Rex
  • 1
  • 1

1 Answers1

0

The reason why the old value got updated is because all the objects in the list are references, not related to MutableStateFlow. I just need a deep copy of the list, see this post here: https://www.baeldung.com/kotlin/deep-copy-data-class

Another thread worth reading: Deep copy of list with objects in Kotlin

Rex
  • 1
  • 1