Learn practical Kotlin Coroutines and Flow patterns for Android and KMP.
Copy the install command and let the AI configure it · recommended for beginners
Please install the "kotlin-coroutines-flows" skill from askskill: 1. Download https://raw.githubusercontent.com/affaan-m/ECC/main/skills/kotlin-coroutines-flows/SKILL.md 2. Save it as ~/.claude/skills/kotlin-coroutines-flows/SKILL.md 3. Reload skills and tell me it's ready
Design a Kotlin UI state management approach for an Android ViewModel using StateFlow, including loading, success, and error states, and show how to collect it in a Fragment or Compose.
Provides ViewModel code, a sealed class or data model for UI state, and example code showing how to collect StateFlow in the UI layer.
I have a Kotlin Flow pipeline that needs search debouncing, deduplication, latest-request switching, and error handling. Give a complete example using debounce, distinctUntilChanged, flatMapLatest, and catch, and explain when to use them.
Returns a complete example with common Flow operators and explains how each operator works and how they combine in async data streams.
Help me write unit tests for Kotlin coroutines and Flow code using kotlinx-coroutines-test to verify suspend functions, StateFlow updates, and error handling, with runnable examples.
Provides unit test examples using test dispatchers, covering coroutine execution, Flow collection results, and exception assertions.
Patterns for structured concurrency, Flow-based reactive streams, and coroutine testing in Android and Kotlin Multiplatform projects.
Application
└── viewModelScope (ViewModel)
└── coroutineScope { } (structured child)
├── async { } (concurrent task)
└── async { } (concurrent task)
Always use structured concurrency — never GlobalScope:
// BAD
GlobalScope.launch { fetchData() }
// GOOD — scoped to ViewModel lifecycle
viewModelScope.launch { fetchData() }
// GOOD — scoped to composable lifecycle
LaunchedEffect(key) { fetchData() }
Use coroutineScope + async for parallel work:
suspend fun loadDashboard(): Dashboard = coroutineScope {
val items = async { itemRepository.getRecent() }
val stats = async { statsRepository.getToday() }
val profile = async { userRepository.getCurrent() }
Dashboard(
items = items.await(),
stats = stats.await(),
profile = profile.await()
)
}
Use supervisorScope when child failures should not cancel siblings:
suspend fun syncAll() = supervisorScope {
launch { syncItems() } // failure here won't cancel syncStats
launch { syncStats() }
launch { syncSettings() }
}
fun observeItems(): Flow<List<Item>> = flow {
// Re-emits whenever the database changes
itemDao.observeAll()
.map { entities -> entities.map { it.toDomain() } }
.collect { emit(it) }
}
class DashboardViewModel(
observeProgress: ObserveUserProgressUseCase
) : ViewModel() {
val progress: StateFlow<UserProgress> = observeProgress()
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5_000),
initialValue = UserProgress.EMPTY
)
}
WhileSubscribed(5_000) keeps the upstream active for 5 seconds after the last subscriber leaves — survives configuration changes without restarting.
val uiState: StateFlow<HomeState> = combine(
itemRepository.observeItems(),
settingsRepository.observeTheme(),
userRepository.observeProfile()
) { items, theme, profile ->
HomeState(items = items, theme = theme, profile = profile)
}.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), HomeState())
// Debounce search input
searchQuery
.debounce(300)
.distinctUntilChanged()
.flatMapLatest { query -> repository.search(query) }
.catch { emit(emptyList()) }
.collect { results -> _state.update { it.copy(results = results) } }
// Retry with exponential backoff
fun fetchWithRetry(): Flow<Data> = flow { emit(api.fetch()) }
.retryWhen { cause, attempt ->
if (cause is IOException && attempt < 3) {
delay(1000L * (1 shl attempt.toInt()))
true
} else {
false
}
}
class ItemListViewModel : ViewModel() {
private val _effects = MutableSharedFlow<Effect>()
val effects: SharedFlow<Effect> = _effects.asSharedFlow()
sealed interface Effect {
data class ShowSnackbar(val message: String) : Effect
data class NavigateTo(val route: String) : Effect
}
private fun deleteItem(id: String) {
viewModelScope.launch {
repository.delete(id)
_effects.emit(Effect.ShowSnackbar("Item deleted"))
}
}
}
…
Automatically format, lint, and fix code issues on every edit.
Learn robust error-handling patterns across TypeScript, Python, and Go applications.
Audit Claude skills and commands with quick scans or full stocktakes.
Plan demand forecasts, safety stock, and replenishment for multi-location retail inventory.
Create iOS liquid glass interfaces with dynamic visuals and interactive morphing.
Record polished web app UI demo videos for walkthroughs, tutorials, and showcases.
Apply idiomatic Kotlin patterns to build robust, efficient, maintainable applications.
Build high-quality Kotlin tests with Kotest, MockK, coroutines, and coverage.
Learn practical Kotlin Ktor server patterns for building and testing backend apps.
Apply Compose Multiplatform patterns for UI architecture, navigation, theming, and performance.
Learn Kotlin conventions, result handling, and session patterns for DAT SDK Android.
Run Flow type checks and fix React type errors quickly.