How to restore a previous UI state with StateFlow?


I have the following screen flow:

Initial → StateA → Loading → StateB

The ViewModel first fetches some initial data and then renders StateA. The user enters values manually and presses Next. I then show Loading, call another API, and display StateB. When the user presses Back from StateB, I want to restore StateA with the same manual input values.

sealed interface UiState {
    data object Initial : UiState
    data class StateA(
        val availableTimes: List<String>,
        val hour: String = "",
        val minute: String = "",
        val isPm: Boolean = false
    ) : UiState
    data object Loading : UiState
    data class StateB(
        val result: String
    ) : UiState
}

class MyViewModel : ViewModel() {

    private val _uiState = MutableStateFlow<UiState>(UiState.Initial)
    val uiState: StateFlow<UiState> = _uiState.asStateFlow()

    init {
        fetchInitialData()
    }

    private fun fetchInitialData() {
        viewModelScope.launch {
            delay(500)

            _uiState.value = UiState.StateA(
                availableTimes = listOf(
                    "10:00 AM",
                    "11:00 AM",
                    "12:00 PM"
                )
            )
        }
    }

    fun onInputChanged(
        hour: String,
        minute: String,
        isPm: Boolean
    ) {
        val currentState = _uiState.value as? UiState.StateA ?: return

        _uiState.value = currentState.copy(
            hour = hour,
            minute = minute,
            isPm = isPm
        )
    }

    fun onNextClicked() {
        val screenAState = _uiState.value as? UiState.StateA ?: return

        viewModelScope.launch {
            _uiState.value = UiState.Loading

            delay(1_000)

            _uiState.value = UiState.StateB(
                result = "${screenAState.hour}:${screenAState.minute}"
            )
        }
    }

    fun onBackClicked() {
        // How should ScreenA be restored with its previous input?
    }
}

Since the StateFlow holds only one sealed state at a time, the previous StateA state is no longer available after changing the state to Loading and then StateB.

My team follows this single-state pattern, so using one large state object containing data for every screen is not an option.

What is the recommended way to restore the previous StateA state when the user presses Back?

The state is inside a Dialog fragment with Compose ui and that's why I use a single viewmodel for the state.

3
Aug 2 at 9:38 AM
User AvatarCompose Learner
#best-practices#android#kotlin#android-viewmodel

Accepted Answer

Well, the data has to be stored somewhere.

You can have the different steps available as separate properties in one, monolithic state object, you can have separate StateFlows for each step or you can even introduce separate view models (each with their own StateFlow) for each step.

There is no clear superior way to model this. It depends on the overall complexity of the data and user flow and how flexbile this must be.

For example, having a lot of different steps with complex state holders and validation logic, I would recommend separating each step into a separate view model. In that case don't forget to persist that state somewhere (in a database or at least a SavedStateHandle) so the current state can be easily restored on process death. You way even want to extract the single source of truth of the entered data into a central repository in the data layer that all view models can access. This also helps when a later step depends on the data of a previous step.

On the other hand, if you really only have two separate steps and don't see this being expanded in the future, then I wouldn't bother with different view models and simply adjust the one UiModel you already have. Having separate StateFlows (in the same view model) may prove more easy for the validation logic, see what fits best.

User Avatartyg
Aug 3 at 6:25 PM
0