android-coroutines-flowlisted
Install: claude install-skill lenorebreakneck630/claude-zero-to-hero-android-KMP
# Android / KMP Coroutines and Flow
## Core Principles
- Prefer `suspend` for one-shot work and `Flow` for streams over time.
- `ViewModel` owns UI-facing coroutine scopes.
- Repositories expose `Flow` only when data can change over time; otherwise return `suspend` functions.
- Cancellation is expected behavior, not an error.
- Never block the main thread with disk, network, or heavy CPU work.
---
## Choosing the Right API
| Need | Preferred API |
|---|---|
| Single request/response | `suspend fun` |
| Ongoing database observation | `Flow<T>` |
| UI state stream | `StateFlow<T>` |
| One-time events | `Channel` + `receiveAsFlow()` |
| Fire-and-forget parallel child work | `coroutineScope { async { ... } }` |
Do not use `LiveData` in new code.
---
## Dispatcher Ownership
The class doing the blocking work is responsible for moving to the correct dispatcher.
```kotlin
class ImageCompressor(
private val ioDispatcher: CoroutineDispatcher = Dispatchers.IO
) {
suspend fun compress(bytes: ByteArray): ByteArray = withContext(ioDispatcher) {
// blocking compression
bytes
}
}
```
`ViewModel`s that only use `viewModelScope` should not inject dispatchers. See the **android-testing** skill for test setup with `Dispatchers.setMain(...)`.
---
## Repository Contracts
Use `Flow` only when the caller should keep receiving updates:
```kotlin
interface NoteRepository {
fun observeNotes(): Flow<List<Note>>
suspend fun refreshNotes(): EmptyResult<Da