Real World Example

Jetpack Compose Screen

This is how a production Android screen (e.g. a product list in a shopping app) consumes a sealed UI state. The ViewModel emits states; the composable draws each one.

sealed interface ProductsUiState {
    data object Loading : ProductsUiState
    data class Success(val products: List<Product>) : ProductsUiState
    data class Error(val message: String) : ProductsUiState
}

@Composable
fun ProductsScreen(viewModel: ProductsViewModel = viewModel()) {
    val state by viewModel.uiState.collectAsStateWithLifecycle()

    when (val s = state) {
        ProductsUiState.Loading -> CircularProgressIndicator()
        is ProductsUiState.Success -> LazyColumn {
            items(s.products) { product -> ProductRow(product) }
        }
        is ProductsUiState.Error -> ErrorView(s.message, onRetry = viewModel::refresh)
    }
}

Mapping Network Results to UI State

Example

A simplified repository + ViewModel flow: network outcomes are sealed too, and a pure function turns them into UI state.

sealed interface ApiResult<out T> {
    data class Ok<T>(val data: T) : ApiResult<T>
    data class HttpError(val code: Int) : ApiResult<Nothing>
    data object NoInternet : ApiResult<Nothing>
}

sealed interface UiState {
    data object Loading : UiState
    data class Content(val items: List<String>) : UiState
    data class Error(val message: String, val canRetry: Boolean) : UiState
}

fun toUiState(result: ApiResult<List<String>>): UiState = when (result) {
    is ApiResult.Ok -> UiState.Content(result.data)
    is ApiResult.HttpError -> when (result.code) {
        401 -> UiState.Error("Session expired. Please log in again.", canRetry = false)
        in 500..599 -> UiState.Error("Server is busy. Try again.", canRetry = true)
        else -> UiState.Error("Something went wrong (${result.code})", canRetry = true)
    }
    ApiResult.NoInternet -> UiState.Error("You are offline", canRetry = true)
}

fun main() {
    val responses = listOf(
        ApiResult.Ok(listOf("iPhone 16", "Galaxy S25")),
        ApiResult.HttpError(401),
        ApiResult.HttpError(503),
        ApiResult.NoInternet,
    )
    responses.forEach { println(toUiState(it)) }
}

Output

Content(items=[iPhone 16, Galaxy S25])
Error(message=Session expired. Please log in again., canRetry=false)
Error(message=Server is busy. Try again., canRetry=true)
Error(message=You are offline, canRetry=true)