Compose State Management Patterns: Unidirectional Data Flow Explained

By Xiao Wang · Solutions Engineer • Published: 2025-01-28 • Updated: 2025-09-0510 min read

State ManagementArchitecture PatternsBest Practices

The declarative nature of Compose requires us to rethink data flow. Whether using ViewModel+StateFlow or frameworks like Orbit and Mavericks, the core goal is to keep state changes within predictable bounds.

Comparing Three Common Patterns

  • ViewModel + StateFlow: Official recommended approach, suitable for most medium-sized projects;
  • Unidirectional Data Flow (UDF): Emphasizes one-way input/output, easy to use with test snapshots;
  • Redux/MVI: Event-driven, suitable for complex interactions or scenarios requiring time-travel debugging.
Note: Always avoid holding mutable state directly inside Composables, otherwise it increases the risk of recomposition inconsistencies.

Example: Filter Panel

The following code demonstrates how to centralize intent handling in the ViewModel and push results to the UI layer through immutable State:

@Stable
data class FilterState(
    val categories: List<String> = emptyList(),
    val selected: Set<String> = emptySet()
)

sealed interface FilterIntent {
    data class Toggle(val category: String) : FilterIntent
    data object Reset : FilterIntent
}

class FilterViewModel : ViewModel() {
    private val _state = MutableStateFlow(FilterState())
    val state: StateFlow<FilterState> = _state.asStateFlow()

    fun dispatch(intent: FilterIntent) {
        _state.update { current ->
            when (intent) {
                is FilterIntent.Toggle -> current.copy(
                    selected = current.selected.toggle(intent.category)
                )
                FilterIntent.Reset -> current.copy(selected = emptySet())
            }
        }
    }
}
← Back to Blog