How to correctly model UI states with sealed classes in Compose?

Viewed 803

Say you have a simple screen that displays a list of items. I could define the 3 possible states of the screen like this:

 sealed class State() {
        data class Success(val items: List<Item> = listOf()): State()
        object Loading: State()
        data class Error(val error: String? = null): State()
    }

In my Compose UI, I treat the states accordingly:

when(state) {
    is State.Success -> {
        LazyColumn(...) {...}
    }
    is State.Error -> {
        Text(state.error ?: "")
    }
    is State.Loading -> {
        LoadingBar()
    }
}

The first state will be State.Loading() as I will initiate a network request to get the items. When the items arrive, the state becomes State.Success().

But what if, afterwards, the user presses on a button that should trigger another network request? On top of the existing content, I need to push the State.Loading class again to show the loading indicator while the new network request is finished.

Yet this state has no knowledge of the previous items content, so the UI will only show the loading indicator, and it will not render the content from the previous State.Success().

I could fix this by introducing a new State called: State.LoadingActionWhileSuccess yet this is not scalable.

How would you treat such situations? Adding other State classes could work, but it would become un-manageable after some point.

My biggest worry for this approach is when the screen complexity rises, the combination of possible states gets out of hand.

I could hold the state in a regular data class and mutate it, but I found that this approach can introduce illegal states for concurrent mutations.

1 Answers

Maybe don't allow the user to start another network call while one is underway by disabling buttons, etc.. Most of the apps I've seen do this. When you do a pull-to-refresh, the refresh icon always shows until the call has ended. This prevents you from making additional calls.

Then again, without knowing your use-case, I'm only making a general suggestion.

Related