Dart Programming Fundamentals

Loops: for, while, do-while & for-in

40 min Lesson 7 of 13

Why Do We Need Loops?

Imagine you need to print "Hello" 100 times, or process every item in a list of 1,000 products. Writing each line manually would be absurd. Loops let you repeat a block of code as many times as needed -- either a fixed number of times, or until a condition becomes false. Loops are one of the most fundamental tools in every programming language.

The for Loop

The for loop is the most common loop in Dart. It has three parts: initialization (runs once), a condition (checked before each iteration), and an update (runs after each iteration).

Basic for Loop

void main() {
  // Print numbers 1 through 5
  for (int i = 1; i <= 5; i++) {
    print(i);
  }
  // Output: 1  2  3  4  5

  // Count by twos
  for (int i = 0; i <= 10; i += 2) {
    print(i);
  }
  // Output: 0  2  4  6  8  10

  // Count backwards
  for (int i = 5; i >= 1; i--) {
    print('Countdown: $i');
  }
  // Output: Countdown: 5  Countdown: 4  ... Countdown: 1
}
Note: The loop variable i is scoped to the for loop. It does not exist outside the loop body. This prevents accidental reuse and keeps your code clean.

Looping Through Strings

You can use a for loop to iterate over each character of a string by using the string’s length property and index notation:

String Iteration

void main() {
  String word = 'Dart';

  for (int i = 0; i < word.length; i++) {
    print('Character $i: ${word[i]}');
  }
  // Output:
  // Character 0: D
  // Character 1: a
  // Character 2: r
  // Character 3: t
}

The while Loop

The while loop repeats a block of code as long as a condition is true. Use it when you don’t know in advance how many iterations you need.

Basic while Loop

void main() {
  int count = 1;

  while (count <= 5) {
    print('Count: $count');
    count++;  // Don't forget this! Without it, the loop runs forever.
  }
  // Output: Count: 1  Count: 2  Count: 3  Count: 4  Count: 5

  // Practical example: divide until less than 1
  double value = 100.0;
  int divisions = 0;

  while (value >= 1) {
    value /= 2;
    divisions++;
  }
  print('Divided $divisions times. Final value: ${value.toStringAsFixed(4)}');
  // Output: Divided 7 times. Final value: 0.7813
}
Warning: If the condition in a while loop never becomes false, you get an infinite loop and your program will hang or crash. Always ensure that something inside the loop changes the condition toward false.

The do-while Loop

The do-while loop is similar to while, but with one key difference: it always executes the body at least once before checking the condition. The condition is checked after each iteration.

do-while vs while

void main() {
  // This while loop runs 0 times (condition is false immediately)
  int x = 10;
  while (x < 5) {
    print('while: $x');  // Never prints
    x++;
  }

  // This do-while loop runs 1 time (body executes before condition check)
  int y = 10;
  do {
    print('do-while: $y');  // Prints: do-while: 10
    y++;
  } while (y < 5);
}

Practical do-while: Input Validation Simulation

void main() {
  // Simulate a menu that must show at least once
  List<String> simulatedInputs = ['invalid', 'wrong', 'quit'];
  int attempt = 0;

  do {
    String input = simulatedInputs[attempt];
    print('Attempt ${attempt + 1}: You entered "$input"');
    if (input == 'quit') {
      print('Goodbye!');
      break;
    }
    print('Invalid input, try again.');
    attempt++;
  } while (attempt < simulatedInputs.length);
}
When to use do-while: Use it when the loop body must run at least once -- for example, showing a menu, prompting for input, or performing an action before checking whether to continue. If zero iterations is a valid outcome, use a regular while loop instead.

The for-in Loop

The for-in loop is designed for iterating over collections (like Lists, Sets, and other iterables). It is cleaner than a traditional for loop when you don’t need the index.

for-in with Lists

void main() {
  List<String> fruits = ['Apple', 'Banana', 'Cherry', 'Date'];

  // for-in -- clean and readable
  for (String fruit in fruits) {
    print(fruit);
  }
  // Output: Apple  Banana  Cherry  Date

  // Compare with traditional for loop
  for (int i = 0; i < fruits.length; i++) {
    print('${i + 1}. ${fruits[i]}');
  }
  // Output: 1. Apple  2. Banana  3. Cherry  4. Date
}

for-in with Numbers

void main() {
  List<int> numbers = [10, 20, 30, 40, 50];

  // Calculate the sum
  int sum = 0;
  for (int number in numbers) {
    sum += number;
  }
  print('Sum: $sum');  // Sum: 150

  // Find the maximum value
  int max = numbers[0];
  for (int number in numbers) {
    if (number > max) {
      max = number;
    }
  }
  print('Max: $max');  // Max: 50
}
Note: Use for-in when you just need each element. Use a traditional for loop when you need the index, or when you need to modify the list while iterating (which for-in does not allow).

The forEach Method

Dart collections also have a forEach method that accepts a function. It does the same thing as for-in but uses a callback:

forEach Method

void main() {
  List<String> colors = ['Red', 'Green', 'Blue'];

  // Using forEach with an anonymous function
  colors.forEach((color) {
    print('Color: $color');
  });

  // Using forEach with arrow syntax (for single expressions)
  colors.forEach((color) => print('I like $color'));

  // With index using asMap()
  colors.asMap().forEach((index, color) {
    print('$index: $color');
  });
  // Output: 0: Red  1: Green  2: Blue
}

break and continue

Sometimes you need to alter the normal flow of a loop. break exits the loop entirely, while continue skips the rest of the current iteration and moves to the next one.

break -- Exit the Loop Early

void main() {
  // Find the first number divisible by 7
  for (int i = 1; i <= 100; i++) {
    if (i % 7 == 0) {
      print('First number divisible by 7: $i');
      break;  // Stop the loop immediately
    }
  }
  // Output: First number divisible by 7: 7

  // Search for an item in a list
  List<String> names = ['Alice', 'Bob', 'Charlie', 'Diana'];
  String searchFor = 'Charlie';
  bool found = false;

  for (String name in names) {
    if (name == searchFor) {
      found = true;
      break;  // No need to check remaining items
    }
  }
  print('$searchFor found: $found');  // Charlie found: true
}

continue -- Skip to the Next Iteration

void main() {
  // Print only odd numbers from 1 to 10
  for (int i = 1; i <= 10; i++) {
    if (i % 2 == 0) {
      continue;  // Skip even numbers
    }
    print(i);
  }
  // Output: 1  3  5  7  9

  // Process only valid scores
  List<int> scores = [85, -1, 92, 0, 78, -5, 95];

  for (int score in scores) {
    if (score <= 0) {
      continue;  // Skip invalid scores
    }
    print('Valid score: $score');
  }
  // Output: Valid score: 85  Valid score: 92  Valid score: 78  Valid score: 95
}

Nested Loops

You can place one loop inside another. The inner loop completes all its iterations for each single iteration of the outer loop.

Nested for Loops

void main() {
  // Multiplication table (1-5)
  for (int i = 1; i <= 5; i++) {
    String row = '';
    for (int j = 1; j <= 5; j++) {
      row += '${(i * j).toString().padLeft(3)}';
    }
    print(row);
  }
  // Output:
  //   1  2  3  4  5
  //   2  4  6  8 10
  //   3  6  9 12 15
  //   4  8 12 16 20
  //   5 10 15 20 25
}

Breaking Out of Nested Loops with Labels

void main() {
  // Labels let you break out of a specific loop
  outerLoop:
  for (int i = 0; i < 5; i++) {
    for (int j = 0; j < 5; j++) {
      if (i + j == 6) {
        print('Breaking at i=$i, j=$j');
        break outerLoop;  // Breaks the OUTER loop
      }
    }
  }
  // Output: Breaking at i=2, j=4
  // Without the label, break would only exit the inner loop
}
Performance Warning: Nested loops multiply the number of iterations. A loop of 1,000 inside another loop of 1,000 gives you 1,000,000 iterations. Be mindful of performance when nesting loops, especially with large datasets.

Common Loop Patterns

Pattern: Accumulator (Sum, Count, Build String)

void main() {
  List<double> prices = [9.99, 24.50, 3.75, 15.00, 42.99];

  // Sum accumulator
  double total = 0;
  for (double price in prices) {
    total += price;
  }
  print('Total: \$${total.toStringAsFixed(2)}');  // Total: $96.23

  // Count accumulator
  int expensiveCount = 0;
  for (double price in prices) {
    if (price > 20) {
      expensiveCount++;
    }
  }
  print('Expensive items: $expensiveCount');  // Expensive items: 2

  // String builder
  List<String> words = ['Dart', 'is', 'awesome'];
  String sentence = '';
  for (int i = 0; i < words.length; i++) {
    sentence += words[i];
    if (i < words.length - 1) sentence += ' ';
  }
  print(sentence);  // Dart is awesome
}

Pattern: Search and Filter

void main() {
  List<int> numbers = [12, 5, 8, 130, 44, 3, 99, 67];

  // Find all numbers greater than 50
  List<int> large = [];
  for (int n in numbers) {
    if (n > 50) {
      large.add(n);
    }
  }
  print('Numbers > 50: $large');  // Numbers > 50: [130, 99, 67]

  // Find the first even number
  for (int n in numbers) {
    if (n % 2 == 0) {
      print('First even: $n');  // First even: 12
      break;
    }
  }
}

Choosing the Right Loop

Quick Guide:
for -- when you know the exact number of iterations or need an index.
while -- when you loop until a condition changes (unknown iterations).
do-while -- same as while, but the body must run at least once.
for-in -- when iterating over a collection and you don’t need the index.
forEach -- functional style, good for simple operations on each element.

Practical Examples

Example: FizzBuzz

void main() {
  // Classic programming challenge
  for (int i = 1; i <= 20; i++) {
    if (i % 3 == 0 && i % 5 == 0) {
      print('FizzBuzz');
    } else if (i % 3 == 0) {
      print('Fizz');
    } else if (i % 5 == 0) {
      print('Buzz');
    } else {
      print(i);
    }
  }
}

Example: Simple Number Guessing Logic

void main() {
  int secretNumber = 7;
  List<int> guesses = [3, 9, 5, 7, 10];
  int attempts = 0;

  for (int guess in guesses) {
    attempts++;
    if (guess == secretNumber) {
      print('Correct! Found $secretNumber in $attempts attempts.');
      break;
    } else if (guess < secretNumber) {
      print('$guess is too low.');
    } else {
      print('$guess is too high.');
    }
  }
  // Output:
  // 3 is too low.
  // 9 is too high.
  // 5 is too low.
  // Correct! Found 7 in 4 attempts.
}

Example: Reverse a String

void main() {
  String original = 'Hello, Dart!';
  String reversed = '';

  for (int i = original.length - 1; i >= 0; i--) {
    reversed += original[i];
  }

  print('Original: $original');   // Original: Hello, Dart!
  print('Reversed: $reversed');   // Reversed: !traD ,olleH
}

Practice Exercise

Open DartPad and create a program that: (1) Uses a for loop to calculate the factorial of 10 (10! = 10 × 9 × 8 × ... × 1). (2) Uses a while loop to find the smallest power of 2 that is greater than 1000. (3) Uses for-in to iterate over a list of names and print only those with more than 4 characters. (4) Uses nested loops to print a right triangle of stars (5 rows). (5) Implements FizzBuzz from 1 to 30 using any loop type.