← ClaudeAtlas

wnb-viewmodel-udflisted

Use this skill when writing, reviewing, or refactoring an Android ViewModel that follows unidirectional data flow (UDF). Enforces a single StateFlow<XxxState>, a sealed XxxAction, one onAction() entry point, injected DispatcherProvider for IO, MutableSharedFlow only for one-shot navigation/toast events, Result.onSuccess/onFailure from repositories, Timber for logging. Applies to Kotlin + Jetpack Compose projects using MVVM + UDF. Triggers on "new viewmodel", "add viewmodel", "refactor viewmodel", "UDF", "UiState", "XxxState", "onAction", "sealed action", "StateFlow", "MutableStateFlow", "unidirectional data flow", "MVI-lite", "one-shot event", "SharedFlow event".
wenubey/claude-android-skills · ★ 0 · AI & Automation · score 72
Install: claude install-skill wenubey/claude-android-skills
# Android ViewModel — UDF contract This skill encodes a single ViewModel shape used across a Kotlin + Jetpack Compose codebase. Do not invent variants. If a rule blocks the task, stop and surface the conflict — don't silently break the contract. ## Non-negotiables 1. **One state class per ViewModel.** Named `XxxState`, immutable `data class`, mutated via `copy()`. Everything the UI renders is a field on this class — loading, error, list contents, dialog visibility, form input. 2. **One `StateFlow<XxxState>`.** Backing `MutableStateFlow` is private (`_state`); public is `val state: StateFlow<XxxState> = _state.asStateFlow()`. 3. **One `onAction(action: XxxAction)` entry point.** All UI events flow through it. `XxxAction` is a sealed interface (or sealed class) declared in the same file or feature package. No public `onXxx()` per-event methods. 4. **No `mutableStateOf` inside a ViewModel.** Compose-side only. 5. **No string keys for navigation.** Use type-safe route classes. 6. **IO always goes through an injected `DispatcherProvider`.** Never `Dispatchers.IO` directly — it can't be swapped in tests. 7. **Repositories return `Result<T>` or `Flow<T>`.** - `Result<T>` → `.onSuccess { _state.update { ... } }.onFailure { Timber.e(it, "..."); _state.update { it.copy(error = ...) } }` - `Flow<T>` → `.catch { Timber.e(it, "..."); _state.update { ... } }.collect { ... }` 8. **One-shot events** (navigation, snackbar, toast) go through `MutableSharedFlow<T>(extraBufferCapacity =