skip to content

When would you reach for the flow { } builder instead of flowOf(...) or a collection's .asFlow(), and what can flow { } do that they cannot?

level: middleimportance: must knowfreq 55%

answer

  1. flowOf/asFlow = static, already-known values
  2. flow { } = suspend block, loops, dynamic emission
  3. All three cold + lazy
  4. Paging/polling/network => flow { }
  5. Concurrent/callback => channelFlow/callbackFlow

basics

~10 s

Use flow { } when you need to compute or fetch values lazily, call suspend functions, or emit based on logic/loops. flowOf and asFlow just wrap values you already have.

solid answer

~40 s

flowOf(a, b, c) and someList.asFlow() are convenience builders for values you already hold in memory — they just emit a fixed, known sequence. The flow { } builder is the general-purpose one: its block is a suspend FlowCollector, so inside it you can call suspend functions (network/db/delay), run loops and conditionals, maintain producer-local state, and decide dynamically what and when to emit. All three are cold and lazy. Reach for flow { } whenever emission requires computation, suspension, side effects, or unknown/unbounded length (e.g. polling, paging). Use flowOf/asFlow for simple, static data to keep code concise. For concurrent or callback-driven emission, neither fits — use channelFlow/callbackFlow.

go deeper

for a junior

Can pick flowOf for a fixed list and flow { } when 'doing work', even if fuzzy on why.

for a middle

Clearly maps each builder to its use case and knows flow { }'s suspend superpower.

for a senior

Discusses cold semantics across all three and when to escalate to channelFlow/callbackFlow.

for a principal

Considers API ergonomics/readability trade-offs and laziness implications when designing flow-returning functions for a codebase.

## The three cold builders - **`flowOf(vararg values)`** — emits a fixed, comma-separated list: `flowOf(1, 2, 3)`. - **`Iterable/Sequence/Array.asFlow()`** — turns an existing collection into a flow: `listOf(1,2,3).asFlow()`. - **`flow { }`** — the general builder; its block is `suspend FlowCollector<T>.() -> Unit`, so you can do arbitrary suspending logic and call `emit`. All three are **cold** (re-run per collector) and **lazy** (nothing until a terminal operator). ## What only flow { } can do Because the `flow { }` block is `suspend`, it can: - Call other `suspend` functions between emissions (`delay`, HTTP, DB). - Use loops, `if`, `while(true)`, recursion to decide what to emit. - Keep mutable producer-local state across emissions. - Produce an unknown or unbounded number of values (polling, paging, streaming). ```kotlin // Paging: only flow { } fits — it suspends and loops dynamically fun pagedUsers(): Flow<User> = flow { var page = 0 while (true) { val batch = api.fetchPage(page) // suspend if (batch.isEmpty()) break batch.forEach { emit(it) } page++ } } ``` `flowOf`/`asFlow` cannot call suspend functions to *produce* values; they only stream what is already materialized. ## When to pick which - Static, in-memory values → `flowOf` / `asFlow` (concise, clear intent). - Computed/suspended/looped/unbounded → `flow { }`. - Concurrent or callback-based emission → `channelFlow` / `callbackFlow` (not covered by these cold builders). ## Subtlety `emptyFlow()` is a special cold flow that completes immediately with no values — handy as a default instead of `flow { }` that emits nothing.

  • Can you call a suspend function inside flowOf(...) to compute one of its arguments?
    Only if the surrounding code is already in a suspend context, and even then the value is computed once before flowOf runs — not lazily per collector. To compute lazily per collection you need flow { } so the suspend call happens inside the producer block.

flowOf is reading a fixed shopping list aloud; flow { } is deciding each item on the spot while you walk the aisles.

saying these in an interview costs you the question

  • Thinks flowOf is lazy about computing its arguments
  • Uses flow { } for trivial static lists, adding noise
  • Believes asFlow can call suspend functions to generate items
  • Doesn't know channelFlow/callbackFlow exist for callbacks

context