$npx -y skills add hanamizuki/solopreneur --skill viewmodelBest practices for implementing Android ViewModels, specifically focused on StateFlow for UI state and SharedFlow for one-off events.
| 1 | # Android ViewModel & State Management |
| 2 | |
| 3 | ## Instructions |
| 4 | |
| 5 | Use `ViewModel` to hold state and business logic. It must outlive configuration changes. |
| 6 | |
| 7 | ### 1. UI State (StateFlow) |
| 8 | * **What**: Represents the persistent state of the UI (e.g., `Loading`, `Success(data)`, `Error`). |
| 9 | * **Type**: `StateFlow<UiState>`. |
| 10 | * **Initialization**: Must have an initial value. |
| 11 | * **Exposure**: Expose as a read-only `StateFlow` backing a private `MutableStateFlow`. |
| 12 | ```kotlin |
| 13 | private val _uiState = MutableStateFlow<UiState>(UiState.Loading) |
| 14 | val uiState: StateFlow<UiState> = _uiState.asStateFlow() |
| 15 | ``` |
| 16 | * **Updates**: Update state using `.update { oldState -> ... }` for thread safety. |
| 17 | |
| 18 | ### 2. One-Off Events (SharedFlow) |
| 19 | * **What**: Transient events like "Show Toast", "Navigate to Screen", "Show Snackbar". |
| 20 | * **Type**: `SharedFlow<UiEvent>`. |
| 21 | * **Configuration**: Must use `replay = 0` to prevent events from re-triggering on screen rotation. |
| 22 | ```kotlin |
| 23 | private val _uiEvent = MutableSharedFlow<UiEvent>(replay = 0) |
| 24 | val uiEvent: SharedFlow<UiEvent> = _uiEvent.asSharedFlow() |
| 25 | ``` |
| 26 | * **Sending**: Use `.emit(event)` (suspend) or `.tryEmit(event)`. |
| 27 | |
| 28 | ### 3. Collecting in UI |
| 29 | * **Compose**: Use `collectAsStateWithLifecycle()` for `StateFlow`. |
| 30 | ```kotlin |
| 31 | val state by viewModel.uiState.collectAsStateWithLifecycle() |
| 32 | ``` |
| 33 | For `SharedFlow`, use `LaunchedEffect` with `LocalLifecycleOwner`. |
| 34 | * **Views (XML)**: Use `repeatOnLifecycle(Lifecycle.State.STARTED)` within a coroutine. |
| 35 | |
| 36 | ### 4. Scope |
| 37 | * Use `viewModelScope` for all coroutines started by the ViewModel. |
| 38 | * Ideally, specific operations should be delegated to UseCases or Repositories. |