Menu
Coddy logo textTech

Iterable & Iterator

Part of the Object Oriented Programming section of Coddy's Dart journey — lesson 68 of 110.

Every time you use a for-in loop on a List, Set, or other collection, you're working with Iterable and Iterator under the hood. Understanding these concepts lets you create your own classes that can be looped over.

An Iterable is any object that can produce a sequence of values. It has one key requirement: an iterator getter that returns an Iterator. The Iterator is what actually moves through the elements one by one:

var numbers = [1, 2, 3];
var iterator = numbers.iterator;

while (iterator.moveNext()) {
  print(iterator.current);  // 1, then 2, then 3
}

The moveNext() method advances to the next element and returns true if there is one, or false when finished. The current property gives you the current element.

To make your own class iterable, implement the Iterable interface:

class Countdown extends Iterable<int> {
  final int start;
  Countdown(this.start);
  
  @override
  Iterator<int> get iterator => CountdownIterator(start);
}

class CountdownIterator implements Iterator<int> {
  int _current;
  
  CountdownIterator(int start) : _current = start + 1;
  
  @override
  int get current => _current;
  
  @override
  bool moveNext() {
    if (_current > 0) {
      _current--;
      return true;
    }
    return false;
  }
}

void main() {
  for (var n in Countdown(3)) {
    print(n);  // 3, 2, 1, 0
  }
}

Now Countdown works with for-in loops and all Iterable methods like map, where, and fold. This pattern is essential when building custom collections that integrate seamlessly with Dart's collection ecosystem.

challenge icon

Challenge

Easy

Let's build a custom range generator that can be iterated over using a for-in loop! You'll create your own Iterable and Iterator classes to generate a sequence of numbers within a specified range.

You'll organize your code into two files:

  • range.dart: Create two classes that work together to produce a sequence of numbers:
    • A RangeIterator class that implements Iterator<int>. It should track the current position and know when to stop. The iterator starts before the first element, so moveNext() must be called before accessing current.
    • A Range class that extends Iterable<int>. It takes a start and end value in its constructor and provides an iterator getter that returns a RangeIterator.
    The range should be inclusive of both start and end. For example, Range(1, 5) should produce: 1, 2, 3, 4, 5.
  • main.dart: Import your range file and demonstrate your custom iterable:
    • Create a Range(1, 5) and use a for-in loop to print each number on its own line
    • Print an empty line
    • Create a Range(10, 13) and use the map method to double each value, then print the resulting list

Because your Range class extends Iterable, it automatically gains access to all the powerful collection methods like map, where, and fold - that's the beauty of implementing the Iterable interface!

Expected output:

1
2
3
4
5

[20, 22, 24, 26]

Cheat sheet

An Iterable is any object that can produce a sequence of values. It requires an iterator getter that returns an Iterator.

An Iterator moves through elements one by one using:

  • moveNext() - advances to the next element and returns true if there is one, or false when finished
  • current - property that gives you the current element

Using an iterator manually:

var numbers = [1, 2, 3];
var iterator = numbers.iterator;

while (iterator.moveNext()) {
  print(iterator.current);  // 1, then 2, then 3
}

To make a custom class iterable, implement the Iterable interface and create a corresponding Iterator class:

class Countdown extends Iterable<int> {
  final int start;
  Countdown(this.start);
  
  @override
  Iterator<int> get iterator => CountdownIterator(start);
}

class CountdownIterator implements Iterator<int> {
  int _current;
  
  CountdownIterator(int start) : _current = start + 1;
  
  @override
  int get current => _current;
  
  @override
  bool moveNext() {
    if (_current > 0) {
      _current--;
      return true;
    }
    return false;
  }
}

void main() {
  for (var n in Countdown(3)) {
    print(n);  // 3, 2, 1, 0
  }
}

Classes that extend Iterable work with for-in loops and automatically gain access to all Iterable methods like map, where, and fold.

Try it yourself

import 'range.dart';

void main() {
  // TODO: Create a Range(1, 5) and use a for-in loop to print each number
  
  
  // TODO: Print an empty line
  
  
  // TODO: Create a Range(10, 13), use map to double each value, and print the list
  
}
quiz iconTest yourself

This lesson includes a short quiz. Start the lesson to answer it and track your progress.

All lessons in Object Oriented Programming