State Management — KMP
State as a Data Class
In the KMP / Decompose path, state is a plain data class — no marker interface required.
data class HomeState(
val title: String = "",
val isLoading: Boolean = false,
)
Holding and Updating State
BaseComponent provides componentState: MutableStateFlow<VS> to hold the component's state.
Update state using the built-in MutableStateFlow.update extension:
componentState.update { copy(title = "new") }
Or use the equivalent Arkitekt helper from app.futured.arkitekt.decompose.ext:
update(componentState) { copy(title = "new") }
Exposing State
Define an interface that exposes only what the UI needs, then implement it in your component:
interface HomeScreen {
val state: StateFlow<HomeState>
}
class HomeComponent(
componentContext: AppComponentContext,
) : HomeScreen, BaseComponent<HomeState, HomeUiEvent>(componentContext, HomeState()) {
override val state: StateFlow<HomeState> = componentState
}
This keeps the UI decoupled from the concrete component class, which makes Compose previews and tests straightforward — just provide a fake HomeScreen implementation.
Observing State in Compose
Pass the interface type to your composable, not the concrete component:
@Composable
fun HomeScreen(component: HomeScreen) {
val state by component.state.collectAsState()
if (state.isLoading) {
CircularProgressIndicator()
} else {
Text(state.title)
}
}
Value / Flow Conversions
Arkitekt provides utility extensions for bridging Decompose Value and Kotlin Coroutines StateFlow / Flow APIs.
Value<T>.asStateFlow()
Converts a Decompose Value to a Kotlin StateFlow:
val state: StateFlow<HomeState> = decomposeValue.asStateFlow()
Flow<T>.collectAsValue(initial, coroutineScope)
Converts a Kotlin Flow to a Decompose Value:
val decomposeValue: Value<HomeState> = stateFlow.collectAsValue(
initial = HomeState(),
coroutineScope = lifecycleScope,
)