Flow & Channels

A Flow is a cold asynchronous stream that emits values over time and only runs when collected, while a Channel is a hot queue passing values between coroutines.

Learn Flow & Channels in our free Kotlin course — a beginner-friendly interactive lesson with worked examples, a practice exercise and a quick reference.

Part of the free Kotlin course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.

You'll learn the flow {' '} builder, why flows are cold, collecting values, operators like map / filter , how flows differ from sequences, and the basics of Channel .

What You'll Learn in This Lesson

1️⃣ Cold Flows: flow {' '}

A Flow is an asynchronous stream. You build one with the flow {' '} builder and push values with emit() . It's cold : the builder doesn't run until a terminal operator like collect consumes it. Each collector restarts it from the beginning.

Notice "before collect" prints first — proof the flow body waited until collect pulled values through it.

2️⃣ Flow Operators: map & filter

Flows share the familiar operators — map , filter , take — but they run lazily and may suspend . Each returns a new flow; nothing happens until a terminal operator like collect runs. flowOf(...) is a quick way to make a flow from fixed values.

A single collector processes values in emission order, so 1, 9, 25 always print in exactly that sequence — the same mental model as a sequence, but able to suspend.

3️⃣ Channels: Hot Queues Between Coroutines

Where a flow is cold, a Channel is hot : it's a coroutine-safe queue. One coroutine send s values, another receive s them, and each value goes to exactly one consumer. Closing the channel lets a for loop drain it cleanly.

The consumer's for loop receives values in send order until the producer calls close() , so the output is fully ordered.

Your turn. Fill in the ___ , then run and compare.

Create a flow, filter and map it, then collect — all with deterministic, ordered output.

📋 Quick Reference — Flow & Channels

Practice quiz

What does it mean that a Flow is 'cold'?

  • It runs on a background thread only
  • The builder block does not run until a terminal operator collects it
  • It can never be collected twice
  • It blocks the main thread

Answer: The builder block does not run until a terminal operator collects it. A cold flow does not run until a terminal operator like collect starts consuming it.

Which builder pushes values into a flow?

  • send()
  • yield()
  • push()
  • emit()

Answer: emit(). Inside flow { }, you push values with emit().

Which is a terminal operator that starts a flow?

  • collect
  • map
  • filter
  • flowOf

Answer: collect. collect is the terminal operator that actually runs the flow and receives each value.

How do intermediate operators like map and filter behave on a flow?

  • They run eagerly and block
  • They run lazily and return a new flow; nothing happens until collected
  • They consume the flow immediately
  • They cannot suspend

Answer: They run lazily and return a new flow; nothing happens until collected. Intermediate operators are lazy, return a new flow, and may suspend; collect triggers them.

How does a Flow differ from a Sequence?

  • A Flow cannot have operators
  • A Sequence is asynchronous
  • A Flow is built for coroutines and can suspend; a Sequence is synchronous and cannot
  • They are identical

Answer: A Flow is built for coroutines and can suspend; a Sequence is synchronous and cannot. A Flow can suspend at every step; a Sequence is synchronous and cannot suspend.

Why is the output of a single collector deterministic in order?

  • Because flows sort their output
  • Because one collector consumes emissions sequentially in production order
  • Because collect runs in parallel
  • Because emit randomizes order

Answer: Because one collector consumes emissions sequentially in production order. A single collector receives emitted values one at a time in the order produced.

How is a Channel best described?

  • A cold, repeatable stream
  • A synchronous list
  • An extension property
  • A hot, coroutine-safe queue between coroutines

Answer: A hot, coroutine-safe queue between coroutines. A Channel is a hot stream: a coroutine-safe queue passing values between coroutines.

In a Channel, each sent value is received by...

  • Exactly one consumer
  • Every consumer simultaneously
  • No consumer
  • Only the producer

Answer: Exactly one consumer. Each Channel value is received by exactly one consumer.

Why must a producer call channel.close()?

  • To free CPU cores
  • So a for loop over the channel can end cleanly instead of hanging
  • To restart the channel
  • To make it cold

Answer: So a for loop over the channel can end cleanly instead of hanging. Closing lets the consumer's for loop drain remaining values and then finish.

Where must collect be called from?

  • From any ordinary function
  • From the top level only
  • From inside a coroutine, such as runBlocking
  • From a constructor

Answer: From inside a coroutine, such as runBlocking. collect is suspending, so it must be called inside a coroutine like runBlocking.