Use Kotlin Coroutines and Flow for reactive async streams
✓Works with OpenClaudeYou 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
- Mark suspend functions: suspend fun loadUser(): User
- Use coroutineScope { } or supervisorScope { } for structured concurrency
- For streams of values over time, use Flow<T>
- Collect flows with .collect { } inside a coroutine
- Use stateIn() for Flow that needs a current value (StateFlow)
- 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()
Related Kotlin / Android Skills
Other Claude Code skills in the same category — free to download.
Jetpack Compose
Build Jetpack Compose UIs with state and navigation
Room Database
Set up Room database with DAOs, entities, and migrations
Retrofit
Configure Retrofit for API calls with coroutines
Hilt DI
Set up Hilt dependency injection in Android
Kotlin Coroutines
Write coroutines with Flow, StateFlow, and error handling
Kotlin Testing
Write Android tests with JUnit, Mockk, and Espresso
Android Room Database
Set up Room (SQLite) for local data persistence in Android Kotlin apps
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.