Custom Iterables in Dart: Build Your Own Iterable

Custom iterables in Dart take two forms: a sync* generator for lazy sequences, or a hand-written Iterator with moveNext()/current. Here's the 12-line version first, then when to pick which.

*Last updated: October 8, 2026* Custom iterables in Dart let any class work in a for-in loop: add an iterator getter returning an Iterator . The fastest path is a sync* generator — Dart builds the iterator for you, lazily. Hand-write moveNext() and current only when you need explicit control over iteration state. *Tested on Dart 3.13 (Flutter 3.47 stable, October 2026). All snippets run with dart run .* Minimal working example of custom iterables in dart A countdown sequence as a lazy iterable — twelve lines, no iterator class: // countdown.dart — run with: dart run countdown.dart /// A lazy countdown: values are produced one at a time, on demand. Iterable<int> countdown(int from) sync* { for (var i = from; i >= 0; i--) { yield i; // pauses here; resumes when the next value is requested } } void main() { for (final n in countdown(3)) { print(n); } // Output: // 3 // 2 // 1 // 0 // Laziness in action: take(2) only runs the loop twice. print(countdown(1000000).take(2).toList()); // [1000000, 999999] } The sync* marks this a *synchronous generator function*. Calling countdown(3) runs zero loop iterations — it returns an Iterable immediately. The loop body executes lazily, one yield at a time, as the for-in loop (or take(2) ) pulls values. A million-element countdown costs nothing until you consume it. If you&#x27;ve used async* for streams, this is the synchronous twin — and our streams guide covers when to pick which. Variations A reusable class, not just a function. Wrap the generator in a class with an iterator getter and the class itself becomes for-in-able: class Countdown extends Iterable<int> { Countdown(this.from); final int from; @override Iterator<int> get iterator sync* { for (var i = from; i >= 0; i--) { yield i; } } } void main() { for (final n in…

Explore