kotlin-flows

Guides correct use of Flow, StateFlow, SharedFlow, and Channel in Kotlin Android and KMP projects.

Updated Jul 1, 2026
One-click install
npx skills add https://github.com/w0lzard/Wolzard-s-Marketplace --skill kotlin-flows-w0lzard
Or copy as Structured Prompt for Agent
Please help me install this Agent Skill.
Skill: kotlin-flows
Source: https://github.com/w0lzard/Wolzard-s-Marketplace/tree/main/plugins/personal-skills/skills/kotlin-flows
Command: npx skills add https://github.com/w0lzard/Wolzard-s-Marketplace --skill kotlin-flows-w0lzard

SYSTEM DOCUMENTATION & REQUIREMENTS

What problem does it solve? Kotlin coroutines offer four stream types (Flow, StateFlow, SharedFlow, Channel) with subtle semantic differences, and choosing the wrong one causes dropped events, leaked callbacks, race conditions, and re-consumed one-shot UI events. This Skill audits existing flow code and enforces correct patterns for stream selection, collection, and error handling. ## Core Features & Use Cases - Stream Type Selection: Decision tables for cold vs hot streams, including when Channel(BUFFERED).receiveAsFlow() beats SharedFlow for single-consumer fire-once events like navigation and snackbars. - Pattern Enforcement: Rules for callback bridging (callbackFlow with awaitClose), lifecycle-safe collection (collectAsStateWithLifecycle, repeatOnLifecycle), StateFlow encapsulation, and operator selection (flatMapLatest, combine, stateIn). - Pitfall Detection: Flags common bugs such as swallowed CancellationException, side effects inside combine/map transforms, sentinel initial values, stateIn called inside functions, and manual Job cancellation that flatMapLatest should replace. - Use Case: While refactoring an Android ViewModel, the Skill detects a BroadcastChannel usage, migrates it to SharedFlow, converts a manual searchJob cancellation pattern to flatMapLatest with debounce, and wraps collection in repeatOnLifecycle. ## Quick Start Review my ViewModel's StateFlow and SharedFlow usage and fix any incorrect event handling or collection patterns.

Frequently Asked Questions about kotlin-flows

High-intent search queries and answers about installing and using this skill.

FAQPage Schema
How do I choose between StateFlow, SharedFlow, and Channel in Kotlin?

Use StateFlow for UI state that new collectors need immediately, Channel(BUFFERED).receiveAsFlow() for single-consumer fire-once events like navigation and snackbars, and SharedFlow only for multi-collector broadcasts where missed events are acceptable. Plain Flow suits one-off cold data streams.

How do I convert a callback API to a Kotlin Flow?

Use callbackFlow with trySend for emissions and always include awaitClose to unregister the listener, otherwise the callback leaks. For single-value callbacks, prefer suspendCancellableCoroutine so cancellation propagates to the underlying call.

Should I use Channel or SharedFlow for one-shot UI events?

Channel(Channel.BUFFERED).receiveAsFlow() is the default for single-consumer events because send suspends until consumed, so events are never dropped. SharedFlow with replay = 0 silently drops emissions when no collector is active, such as during configuration changes.

Why does my Kotlin Flow keep running after the coroutine is cancelled?

A broad catch (e: Exception) inside collect or a suspend function swallows CancellationException, breaking structured concurrency. Catch only specific exception types, rethrow CancellationException explicitly, or use the Flow catch operator which skips it automatically.

Can I expose MutableStateFlow publicly from a ViewModel?

No. Exposing MutableStateFlow lets external callers mutate state and bypass ViewModel logic. Keep the mutable instance private and expose an immutable StateFlow, updating it with the atomic update { } function rather than non-atomic value reads and writes.

Does Kotlin Flow work in Kotlin Multiplatform with iOS?

Yes. Expose Flow from shared KMP code and collect it on iOS using SKIE or manual collection wrapped in a CoroutineScope. Avoid accessing StateFlow.value directly from non-coroutine iOS contexts; use collection wrappers instead.