Skip to main content

Menu

System onlineAI Tutorials / 2026

AI Tutorials
Knowledge search

Recent · Popular · Tutorials

  • dart3 tutorials
  • java1 tutorial
  • Javascript3 tutorials
  • Node.js Introduction2 tutorials
  • dartdart · General · In depth
  • JavascriptJavascript · Advanced · In depth
  • JavascriptJavascript · Advanced · Mid
  • JavascriptJavascript · Advanced · Short

Find a tutorial

Tell the assistant what you want to learn. It suggests published chapters.

For example: “I want to learn Rust ownership from the start.” The conversation is kept only in this tab.

0 / 2,000 characters

Enter to send, Shift+Enter for a new line. Do not include personal information.

Chapter 17

Streams: Sequences of Asynchronous Events

In the last chapter you met the Future, Dart's way of saying "this value will arrive later." A Future delivers exactly one value. But many things in real software deliver many values over time: a sensor reporting temperature every second, a chat server sending messages as they arrive, a search box u
Level: GeneralLength: In depth4 min read

Read at another depth

Streams: Sequences of Asynchronous Events

In the last chapter you met the Future, Dart's way of saying "this value will arrive later." A Future delivers exactly one value. But many things in real software deliver many values over time: a sensor reporting temperature every second, a chat server sending messages as they arrive, a search box updating results with every keystroke.

A Stream is Dart's answer to that. A Stream is like a conveyor belt of events. You stand at the end of the belt, and values keep arriving one after another — now, in a second, in ten seconds, whenever they come. You do not ask for each value; you simply react as each one appears.

  • Future: one value, delivered once.
  • Stream: many values, delivered over time.

The chapter assumes you have read the previous one and are comfortable with async and await.

Creating and listening to a Stream

Dart can generate a Stream for you with Stream.periodic. It emits a value on a fixed schedule — here, every 100 milliseconds — and a function turns the count into a value.

Code example in dart
Future<void> main() async {
  final ticks = Stream.periodic(Duration(milliseconds: 100), (i) => i);
  await for (final i in ticks.take(3)) {
    print('tick $i');
  }
}

Run this and you will see:

Code example in text
tick 0
tick 1
tick 2

Two new ideas are hiding in those few lines, so let's slow down.

The for-await loop

await for (final i in ticks) is a loop that waits. Each time the Stream delivers a value, the body of the loop runs with that value. Then the loop pauses and waits for the next one. This is asynchronous iteration: iteration where you do not know when, or how many times, the next turn will come.

The word async on main is what makes this legal. A for-await loop can only appear inside an async function, because waiting for a Stream means the function itself must be able to pause and resume.

Stopping early with take

A Stream.periodic never ends on its own. It would tick forever. That is dangerous in a loop: your program would hang waiting for values that never stop coming.

The .take(3) call fixes this. It wraps the Stream in a new Stream that passes along at most three values and then closes. A Stream is closed when it says "no more values, ever." The for-await loop ends when the Stream closes.

You can also end the loop early by writing break inside it. Dart then quietly cancels your subscription to the Stream, meaning you stop receiving values. Cancelling matters: if nobody cancels a never-ending Stream, its work continues in the background, doing nothing useful.

The life of a Stream subscription

When you start listening to a Stream, you create a subscription — a personal connection between you and the events. The connection goes through a small life cycle.

A flow diagram. A Stream flows to a Subscription. The Subscription either flows through a Data event and back to itself in a loop, or flows to Done or Cancel, both of which lead to Closed.
100%
Life cycle of a Stream subscription
Show diagram source
stateDiagram-v2
    [*] --> Subscribed
    Subscribed --> Subscribed: data event
    Subscribed --> Done: stream closes
    Subscribed --> Cancelled: listener cancels
    Done --> Closed
    Cancelled --> Closed

Each arrow labelled "data event" is one turn of your for-await loop. The loop can leave that cycle in only two ways: the Stream closes on its own, or you stop listening. Either way, the connection is finished and the loop is over.

Streams you make yourself

You are not limited to built-in Streams. An async generator is a function marked async* that uses yield to push values into a Stream. This function produces a countdown, one value per second:

Code example in dart
Stream<int> countdown(int from) async* {
  for (var i = from; i > 0; i--) {
    yield i;
    await Future.delayed(Duration(seconds: 1));
  }
}

Future<void> main() async {
  await for (final n in countdown(3)) {
    print(n);
  }
  print('liftoff');
}

The yield statement hands one value to whoever is listening, then the function continues. When the function ends, the Stream closes automatically. This is the most common way to write your own Streams, because it reads almost like ordinary code.

Transforming Streams

Like Lists, Streams come with methods such as map, where, take, and skip. The difference is timing. With a List, all the values exist before you transform them. With a Stream, the transformation runs on each value the moment it arrives.

Code example in dart
Future<void> main() async {
  final ticks = Stream.periodic(Duration(milliseconds: 100), (i) => i);
  final names = ticks.map((i) => 'event $i');
  final shortOnes = names.where((n) => n.length < 9);

  await for (final n in shortOnes.take(2)) {
    print(n);
  }
}

Here map and where do not wait for anything. They set up a pipeline, and every event that flows through gets reshaped and filtered on the way. Streams also have asynchronous-only tools, such as whereType for filtering by type and distinct for suppressing repeated values — but map, where, and take cover most everyday needs.

Streams compared with what you already know

IdeaListStream
ValuesAll exist alreadyArrive over time
Looping`for (final x in list)``await for (final x in stream)`
Transforming`map`, `where``map`, `where` (as events arrive)
EndsAfter the last valueWhen closed, or cancelled
Needs `async`?NoYes, for `await for`

If a List is a photo album you can flip through at will, a Stream is a live broadcast: you watch as it happens, and it is gone once it has passed.

Where you will meet Streams

Streams show up throughout the Dart ecosystem. In Flutter, user input, page navigation, and state updates often arrive as Streams. In server code, an incoming request body is a Stream of chunks. Any library that talks to the outside world — network, files, sensors — tends to offer Streams, because the outside world does not deliver one tidy value and stop.

The rule to carry forward: one later value? Future. Many values over time? Stream. The next chapter completes the picture with isolates, Dart's way of running heavy work on separate threads.

Exercise

A Stream.periodic Stream emits every second and never closes. What happens if you run await for (final i in ticks) { print(i); } with no take and no break?

Show answer

The loop waits forever. The Stream never closes, so the for-await loop never finishes and the rest of the program (if any) never runs. You must limit the Stream with take, or break, or otherwise cancel the subscription.

Exercise

Rewrite Stream.periodic(Duration(milliseconds: 100), (i) => i) values so that only even counts are printed, stopping after the first three even counts.

Show answer

Use where to filter, then take to stop: await for (final i in ticks.where((i) => i.isEven).take(3)) { print(i); }

Spotted a mistake, or code that does not work?

Report an issue

Chapter: Streams: Sequences of Asynchronous Events

0 / 2,000 characters

Plain text, for example: the second code sample does not compile. Do not include personal information.

You can't do that