Skip to main content
The for-in loop in Dart is a control flow statement designed for sequential iteration over objects that implement the Iterable interface. It abstracts the underlying Iterator mechanics, providing a concise syntax to access each element in a collection sequentially without manual index management.

Syntax

for (<declaration|identifier> in iterable) {
  // Statement(s) to execute
}
  • declaration|identifier: Either a new variable declaration (scoped to the loop body) or an existing identifier (externally scoped) that receives the current element in the iteration.
  • iterable: An expression that evaluates to a Dart Iterable.

Underlying Mechanics

When a for-in loop executes, Dart performs the following operations under the hood:
  1. Evaluates the iterable expression.
  2. Calls the .iterator getter on the resulting Iterable to obtain an Iterator object.
  3. Enters a loop that calls moveNext() on the Iterator.
  4. If moveNext() returns true, it assigns the value of Iterator.current to the loop variable and executes the loop body.
  5. If moveNext() returns false, the loop terminates.

Variable Binding and Scoping

The loop variable can be newly declared within the loop construct or it can reference an existing, externally scoped variable.
Iterable<String> tokens = ['alpha', 'beta', 'gamma'];

// Explicit typing (scoped to the loop body)
for (String token in tokens) {
  print(token);
}

// Implicit typing (type inferred as String)
for (var token in tokens) {
  print(token);
}

// Immutable binding (prevents reassignment within the loop body)
for (final token in tokens) {
  print(token);
}

// Assignment to an existing, externally scoped variable
String currentToken = '';
for (currentToken in tokens) {
  print(currentToken);
}
// currentToken retains the final iteration value ('gamma') after termination

Pattern Matching Integration

As of Dart 3.0, the for-in loop supports destructuring patterns directly within the declaration clause. This allows for unpacking complex data structures, such as Records or nested collections, during iteration.
Iterable<(int, String)> records = [(1, 'A'), (2, 'B')];

// Destructuring a Record into separate variables
for (final (id, name) in records) {
  print('$id: $name');
}

Technical Constraints

  • No Index Access: The for-in loop does not expose an iteration counter or index. If an index is required, developers must use a standard for loop or the .indexed getter (e.g., for (final (index, item) in items.indexed)).
  • Concurrent Modification: The for-in language construct itself does not enforce structural immutability; it merely invokes moveNext(). However, the specific Iterator implementations of standard library collections (such as List and Set) are designed to be fail-fast. Attempting to modify the length or structure of these standard collections during iteration will cause their iterators to throw a ConcurrentModificationError. Custom Iterable implementations dictate their own modification rules and may allow mutations without throwing an error.
  • Synchronous Only: The standard for-in loop operates strictly on synchronous Iterable objects. To iterate over asynchronous data sequences (Stream), Dart provides the await for variant.
Tired of Poor Dart Skills? Fix That With Deep Grasping!Learn More