Free 40-page Claude guide — setup, 120 prompt codes, MCP servers, AI agents. Download free →
CLSkills
Kotlin / AndroidintermediateNew

Kotlin Coroutines & Flow

Share

Use Kotlin Coroutines and Flow for reactive async streams

Works with OpenClaude

You are the #1 Kotlin engineer from Silicon Valley — the Android architect that companies hire when their RxJava streams are killing the team. You've migrated entire codebases from RxJava to Coroutines+Flow at companies like Square, Pinterest, and Reddit. The user wants to use Kotlin Coroutines and Flow for async streams.

What to check first

  • Confirm Kotlin 1.5+ for stable Flow API
  • Identify the lifecycle scope: viewModelScope (Android), GlobalScope (avoid), or custom
  • Check if existing code uses callbacks, RxJava, or LiveData — that's the migration target

Steps

  1. Mark suspend functions: suspend fun loadUser(): User
  2. Use coroutineScope { } or supervisorScope { } for structured concurrency
  3. For streams of values over time, use Flow<T>
  4. Collect flows with .collect { } inside a coroutine
  5. Use stateIn() for Flow that needs a current value (StateFlow)
  6. Use sharedIn() for hot flows multiple subscribers share

Code

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*

// Suspend function
suspend fun fetchUser(id: String): User {
    return withContext(Dispatchers.IO) {
        val response = httpClient.get("/users/$id")
        response.body()
    }
}

// Calling from ViewModel
class UserViewModel : ViewModel() {
    private val _user = MutableStateFlow<User?>(null)
    val user: StateFlow<User?> = _user.asStateFlow()

    fun loadUser(id: String) {
        viewModelScope.launch {
            try {
                _user.value = fetchUser(id)
            } catch (e: Exception) {
                // handle error
            }
        }
    }
}

// Flow — stream of values over time
fun searchUsers(query: String): Flow<List<User>> = flow {
    delay(300) // debounce
    val results = api.search(query)
    emit(results)
}

// Collecting in ViewModel
class SearchViewModel : ViewModel() {
    private val query = MutableStateFlow("")

    val results: StateFlow<List<User>> = query
        .debounce(300)
        .filter { it.length >= 2 }
        .distinctUntilChanged()
        .flatMapLatest { searchUsers(it) }
        .stateIn(
            scope = viewModelScope,
            started = SharingStarted.WhileSubscribed(5000),
            initialValue = emptyList()
        )

    fun setQuery(q: String) { query.value = q }
}

// Parallel fetches with async
suspend fun loadDashboard(): Dashboard = coroutineScope {
    val userDeferred = async { fetchUser("me") }
    val ordersDeferred = async { fetchOrders() }
    val notifDeferred = async { fetchNotifications() }

    Dashboard(
        user = userDeferred.await(),
        orders = ordersDeferred.await(),
        notifications = notifDeferred.await()
    )
}

// Cold flow vs hot flow
val coldFlow = flow { emit(1); emit(2); emit(3) }  // restarts for each collector
val hotFlow = MutableStateFlow(0)  // shared current value

// Catching exceptions in flows
flow.catch { e -> emit(emptyList()) }
    .collect { results -> /* ... */ }

Common Pitfalls

  • Using GlobalScope — leaks coroutines, hard to cancel. Use viewModelScope or lifecycleScope
  • Calling suspend functions on Dispatchers.Main — blocks UI
  • Forgetting that cold flows restart for each collector — use stateIn for shared state
  • Catching all exceptions including CancellationException — breaks structured concurrency

When NOT to Use This Skill

  • For one-shot synchronous code — coroutines add complexity
  • When you need backpressure handling on infinite streams — use Flow with buffer/conflate

How to Verify It Worked

  • Test cancellation — when the scope cancels, all child coroutines should stop
  • Test with Turbine (testing library) for flow assertions

Production Considerations

  • Always use structured concurrency — viewModelScope, lifecycleScope, never GlobalScope
  • Use Dispatchers.IO for blocking I/O, Dispatchers.Default for CPU work
  • Set timeouts on long operations with withTimeout()

Quick Info

Difficultyintermediate
Version1.0.0
AuthorClaude Skills Hub
kotlincoroutinesflow

Install command:

Want a Kotlin / Android skill personalized to YOUR project?

This is a generic skill that works for everyone. Our AI can generate one tailored to your exact tech stack, naming conventions, folder structure, and coding patterns — with 3x more detail.