Skip to content

// Idiomatic structured concurrency: coroutines, Flow, channels, error handling, testing — done right.

Kotlin Coroutines & Flow Pro

skill --stat
  • last update

    May 23, 2026

  • license

    MITv1.0.0

  • format

    SKILL.md

quickstart.sh
3 steps
  1. Install

    // Downloads SKILL.md into ~/.claude/skills/android-kotlin-coroutines-pro/

    $ curl -fsSL --create-dirs -o ~/.claude/skills/android-kotlin-coroutines-pro/SKILL.md https://claudeskil.com/skills/android-kotlin-coroutines-pro/SKILL.md
  2. Invoke

    // Inside Claude Code, type the skill name as a slash command

    > /android-kotlin-coroutines-pro help me ship this
  3. Or copy this from the README

    // extracted kotlin block

    $ fun userStream(id: String): Flow<User> = callbackFlow {
        val reg = api.observe(id) { trySend(it) }
        awaitClose { reg.cancel() }          // no leak on cancellation
    }.flowOn(Dispatchers.IO)
    
    // Turbine test
android-kotlin-coroutines-pro/
references
SKILL.md
readonly
name:
Kotlin Coroutines & Flow Pro
slug:
android-kotlin-coroutines-pro
version:
v1.0.0
license:
MIT
author:
@chiragthummar16
categories:
tags:
#kotlin#coroutines#flow#concurrency#testing
description:

Idiomatic structured concurrency: coroutines, Flow, channels, error handling, testing — done right.

README.md

android-kotlin-coroutines-pro/README.md
6 sections

Kotlin Coroutines & Flow Pro

Structured concurrency is easy to get wrong: a coroutine launched in the wrong scope outlives the screen that started it, a thrown exception cancels siblings you wanted to keep alive, and a hot StateFlow collected without repeatOnLifecycle quietly leaks. This skill writes coroutine and Flow code that respects cancellation, scopes jobs correctly, and ships with deterministic tests. It's for Android and Kotlin backend engineers who want concurrency that behaves under failure, not just on the happy path.

When to use it

  • Wiring repository or use-case layers that combine network and DB streams.
  • Deciding between coroutineScope and supervisorScope, or launch vs async.
  • Converting callbacks to suspend functions with suspendCancellableCoroutine.
  • Turning a cold flow {} into a shared hot stream (shareIn / stateIn) without leaking the upstream.
  • Writing tests that don't sleep — using runTest, StandardTestDispatcher, and Turbine.

What it does

It applies the real coroutines APIs idiomatically: cancellation-cooperative loops (ensureActive, yield, isActive), exception strategy via CoroutineExceptionHandler and supervisorScope, and structured teardown so child jobs join their parent. For Flow it knows the cold-vs-hot distinction, backpressure operators (buffer, conflate, collectLatest, flowOn), callbackFlow with awaitClose, and sharing with SharingStarted.WhileSubscribed(5_000). Tests use kotlinx-coroutines-test (runTest, advanceUntilIdle, TestDispatcher) and Turbine's flow.test { awaitItem(); awaitComplete() }.

Usage

bash
curl -fsSL --create-dirs -o ~/.claude/skills/android-kotlin-coroutines-pro/SKILL.md https://claudeskil.com/skills/android-kotlin-coroutines-pro/SKILL.md
kotlin
fun userStream(id: String): Flow<User> = callbackFlow {
    val reg = api.observe(id) { trySend(it) }
    awaitClose { reg.cancel() }          // no leak on cancellation
}.flowOn(Dispatchers.IO)

// Turbine test
@Test fun emits() = runTest {
    repo.userStream("1").test {
        assertEquals(expected, awaitItem())
        cancelAndIgnoreRemainingEvents()
    }
}

Tips & gotchas

  • withContext(Dispatchers.IO) switches threads but does not create a new failure boundary; wrap independent children in supervisorScope if one failing shouldn't cancel the rest.
  • GlobalScope is almost always a bug — pass an injected scope (viewModelScope, a custom CoroutineScope) so work is tied to a lifecycle.
  • Catch around the Flow with .catch {}, not inside collect — a try/catch in the collector swallows CancellationException and breaks structured cancellation.
  • Prefer WhileSubscribed(5_000) over Eagerly/Lazily for UI state so the upstream stops shortly after the last collector leaves.

FAQ

launch or async? launch for fire-and-forget side effects; async only when you need a Deferred result and will await it. A lone async whose exception is never awaited can be lost.

Why does my test hang? You're likely on StandardTestDispatcher without advancing time — call advanceUntilIdle(), or use UnconfinedTestDispatcher for eager execution.

Does it cover Compose? Yes — collectAsStateWithLifecycle() and LaunchedEffect/rememberCoroutineScope patterns are included.

$ cat reviews/

Reviews

// No reviews yet. Be the first.
Loading review form…

$ ls related/

explore all →