home / skills / hoangnguyen0403 / agent-skills-standard / state
This skill guides Android state management using ViewModel, StateFlow, and LCE patterns to ensure robust, testable UI state handling.
npx playbooks add skill hoangnguyen0403/agent-skills-standard --skill stateReview the files below or copy the command above to add this skill to your agents.
---
name: Android State Management
description: Standards for ViewModel, StateFlow, and UI State Patterns
metadata:
labels: [android, state, viewmodel, flow]
triggers:
files: ['**/*ViewModel.kt', '**/*UiState.kt']
keywords: [viewmodel, stateflow, livedata, uistate]
---
# Android State Management
## **Priority: P0**
## Implementation Guidelines
### ViewModel Pattern
- **Exposure**: Expose ONE `StateFlow<UiState>` via `.asStateFlow()`.
- **Scope**: Use `viewModelScope` for all coroutines.
- **Initialization**: Trigger initial load in `init` or `LaunchedEffect` (once).
### UI State (LCE)
- **Type**: sealed interface `UiState` (Loading, Content, Error).
- **Immutability**: Data classes inside should be `@Immutable`.
### Flow Lifecycle
- **Collection**: Use `collectAsStateWithLifecycle()` in Compose.
- **Hot Flows**: Use `SharingStarted.WhileSubscribed(5000)` for shared resources.
## Anti-Patterns
- **LiveData**: `**No LiveData**: Use StateFlow.`
- **Mutable State**: `**No Mutable Public**: Expose read-only Flow.`
- **Context**: `**No Context in VM**: Memory Leak Risk.`
## References
- [Templates](references/implementation.md)
This skill documents practical standards for Android state management using ViewModel, StateFlow, and UI state patterns. It defines patterns to keep UI state predictable, lifecycle-safe, and testable across Compose and traditional UI layers. The guidance focuses on a single read-only StateFlow, sealed UI state types, and coroutine scope discipline.
The skill prescribes exposing one StateFlow<UiState> from each ViewModel, backed by a private MutableStateFlow and published with asStateFlow() to prevent external mutation. It mandates using viewModelScope for all coroutines and initializing load logic once (init or a Compose LaunchedEffect). In Compose, it recommends collectAsStateWithLifecycle() and using SharingStarted.WhileSubscribed(5000) for shared hot flows to balance resource use and responsiveness.
Why one StateFlow instead of multiple flows?
A single UiState flow centralizes UI representation, reduces race conditions, and simplifies Compose collection and testing.
Can I use LiveData instead?
LiveData is discouraged here; StateFlow provides a consistent Kotlin coroutine API and better interoperability with Compose and structured concurrency.