Skip to main content
A for loop in Dart is a control flow statement used to execute a block of code repeatedly based on a boolean condition, or to iterate over the elements of an Iterable or Stream. Dart provides three primary syntactical constructs for iteration: the traditional C-style for loop, the for-in loop for synchronous collections, and the asynchronous await for loop for streams.

Standard for Loop

The standard for loop relies on an explicit loop counter and consists of three optional expressions: initialization, condition, and update.
for (var i = 0; i < 5; i++) {
  print(i);
}
  • Initialization: Executed exactly once before the loop begins. Variables declared here are lexically scoped to the loop block.
  • Condition: Evaluated before each iteration. If it resolves to true, the execution block runs. If false, the loop terminates.
  • Update: Executed at the end of each iteration, typically used to mutate the loop counter.
Closure Capture Mechanics: Dart captures the state of the loop variable inside closures correctly. Each iteration of the loop creates a new lexical scope for the loop variable, meaning asynchronous callbacks or closures defined within the loop will capture the exact value of the variable for that specific iteration, rather than a reference to the final mutated variable.
var callbacks = <Function>[];
for (var i = 0; i < 3; i++) {
  callbacks.add(() => print(i)); // Captures 0, 1, 2 respectively
}

for-in Loop

The for-in loop is a declarative construct used to traverse objects that implement the Iterable interface (such as List or Set). It abstracts away the underlying Iterator mechanics (moveNext() and current).
var numbers = [1, 2, 3];
for (var number in numbers) {
  print(number);
}
Pattern Matching (Dart 3.0+): The for-in loop supports Dart’s pattern matching and destructuring. If the Iterable contains structured data like Records or instances of a class, they can be destructured directly in the loop declaration.
var coordinates = [(1, 2), (3, 4), (5, 6)];

for (var (x, y) in coordinates) {
  print('X: $x, Y: $y');
}

Asynchronous await for Loop

The await for loop is an asynchronous control flow construct used to consume elements from a Stream. It suspends execution of the surrounding async function until the stream emits a new event, executing the loop body for each data event and terminating when the stream is closed.
Future<void> processStream(Stream<int> numberStream) async {
  await for (var number in numberStream) {
    print(number);
  }
}

Control Transfer Statements

Dart provides keywords to alter the standard execution flow within any for loop:
  • break: Immediately terminates the innermost enclosing loop.
  • continue: Bypasses the remaining statements in the current execution block. In a standard for loop, it jumps directly to the update expression. In a for-in or await for loop, it advances to the next element.
Labeled Statements: For nested loops, Dart supports labels to direct break or continue statements to a specific loop level.
void processMatrix() {
  outerLoop:
  for (var i = 0; i < 5; i++) {
    for (var j = 0; j < 5; j++) {
      if (i == 2 && j == 2) {
        break outerLoop; // Terminates both loops
      }
    }
  }
}

The forEach Method

While technically a higher-order method rather than a loop statement, forEach is a functional alternative available on all Iterable objects. It accepts a callback function and applies it to every element.
var items = ['A', 'B', 'C'];
items.forEach((element) {
  print(element);
});
Note: Control transfer statements (break and continue) cannot be used inside the forEach callback, as the block is a separate function scope, not a loop body. To terminate iteration early, a standard for or for-in loop must be used.
Tired of Poor Dart Skills? Fix That With Deep Grasping!Learn More