ZetCode

Dart List

last modified October 23, 2026

A List in Dart is an ordered collection of elements that can be accessed by index. Elements keep their insertion order, and the same value may appear more than once. The type is written List<E>, where E is the element type. Dart lists are zero-indexed, and similar to arrays in other languages.

A list is either fixed-length or growable. A fixed-length list is created with a given size and cannot grow or shrink. A growable list can be extended with add, insert and the other mutating methods. List literals create growable lists.

Creating a List

The simplest way to create a list is a list literal in square brackets. The List constructors create fixed-length lists, and List.generate computes each element from its index.

main.dart
void main() {
  // List literal creates a growable list
  var fruits = ['apple', 'banana', 'orange'];
  print(fruits);

  // Fixed-length list created with the List constructor
  var numbers = List<int>.filled(3, 0);
  numbers[0] = 1;
  numbers[1] = 2;
  numbers[2] = 3;
  print(numbers);

  // Growable list created with List.generate
  var squares = List<int>.generate(5, (i) => i * i);
  print(squares);

  // Attempting to grow a fixed-length list throws
  try {
    numbers.add(4);
  } on UnsupportedError catch (e) {
    print('Cannot add to fixed-length list: ${e.runtimeType}');
  }
}

The first list is a growable literal. The second is a fixed-length list filled with zeros, and its elements are then assigned by index. List.generate builds the list of squares from a function of the index. Calling add on the fixed-length list throws an UnsupportedError, which the try block catches.

$ dart main.dart
[apple, banana, orange]
[1, 2, 3]
[0, 1, 4, 9, 16]
Cannot add to fixed-length list: UnsupportedError

Accessing List Elements

Elements are read with index notation. The first and last getters and the length property give the boundary elements and the size. Out-of-range indices are an error, so the index must be checked when it comes from outside the program.

main.dart
void main() {
  var colors = ['red', 'green', 'blue', 'yellow'];

  // Access by index
  print(colors[0]);
  print(colors[3]);

  // elementAt is equivalent to [] for valid indices
  print(colors.elementAt(2));

  // First and last elements
  print('First: ${colors.first}');
  print('Last: ${colors.last}');

  // Length
  print('Length: ${colors.length}');

  // Out-of-range access with [] throws RangeError
  try {
    print(colors[10]);
  } on RangeError catch (e) {
    print('RangeError: ${e.message}');
  }

  // Safe lookup: check bounds before accessing
  var index = 10;
  if (index < colors.length) {
    print(colors[index]);
  } else {
    print('Index $index is out of range');
  }
}

colors[0], colors.elementAt(2) and colors[3] return elements at valid indices. elementAt does not add any safety: it reports an out-of-range index with the same RangeError as []. To avoid the error, compare the index with length before reading the element.

$ dart main.dart
red
yellow
blue
First: red
Last: yellow
Length: 4
RangeError: Invalid value
Index 10 is out of range

Adding and Removing Elements

Growable lists support adding and removing elements in place. The methods below modify the list they are called on.

main.dart
void main() {
  var numbers = [1, 2, 3];

  // Adding elements
  numbers.add(4);
  numbers.addAll([5, 6]);
  numbers.insert(0, 0);
  print('After adding: $numbers');

  // Removing elements
  numbers.remove(6);
  numbers.removeAt(0);
  numbers.removeLast();
  print('After removing: $numbers');

  // Removing by condition
  numbers.removeWhere((n) => n % 2 == 0);
  print('After removeWhere (even): $numbers');

  // Clear all
  numbers.clear();
  print('After clear: $numbers');
}

add appends one element, and addAll appends every element of an iterable. insert places an element at a given index. remove deletes the first occurrence of a value, while removeAt and removeLast delete by position. removeWhere deletes every element that matches a condition.

$ dart main.dart
After adding: [0, 1, 2, 3, 4, 5, 6]
After removing: [1, 2, 3, 4]
After removeWhere (even): [1, 3]
After clear: []

List Iteration

Dart offers several ways to visit each element. A for loop with an index gives access to the position, a for-in loop is the simplest form, and forEach takes a function for each element.

main.dart
void main() {
  var languages = ['Dart', 'Python', 'Java', 'C#'];

  // for loop with index
  print('for loop:');
  for (var i = 0; i < languages.length; i++) {
    print('$i: ${languages[i]}');
  }

  // for-in loop
  print('\nfor-in loop:');
  for (var lang in languages) {
    print(lang);
  }

  // forEach
  print('\nforEach:');
  languages.forEach((lang) => print(lang));

  // map returns a lazy Iterable; toList materializes it
  print('\nmap:');
  var upper = languages.map((lang) => lang.toUpperCase()).toList();
  print(upper);
}

The for loop prints the index together with each value. The for-in loop and forEach print the values only. map returns a lazy Iterable, so toList is called to build a new List that holds the transformed values.

$ dart main.dart
for loop:
0: Dart
1: Python
2: Java
3: C#

for-in loop:
Dart
Python
Java
C#

forEach:
Dart
Python
Java
C#

map:
[DART, PYTHON, JAVA, C#]

List Manipulation

Lists provide methods for sorting, reversing, filtering and slicing. Some of these modify the list, while others return a new value or a lazy view.

main.dart
import 'dart:math';

void main() {
  var numbers = [5, 2, 8, 1, 7, 3, 9, 4, 6];

  // Sorting in place
  numbers.sort();
  print('Sorted: $numbers');

  // reversed is a lazy view; toList creates a new list
  var reversed = numbers.reversed.toList();
  print('Reversed: $reversed');

  // Filtering with where (lazy) and toList
  var even = numbers.where((n) => n % 2 == 0).toList();
  print('Even numbers: $even');

  // sublist: start inclusive, end exclusive
  var middle = numbers.sublist(2, 5);
  print('Middle sublist: $middle');

  // shuffle with a seeded Random is deterministic
  numbers.shuffle(Random(42));
  print('Shuffled: $numbers');
}

sort and shuffle change the list in place. reversed and where return lazy iterables, so toList is needed to store the results in a list. The end index of sublist is exclusive, so sublist(2, 5) returns three elements. The shuffle uses a fixed seed, so the output is the same on every run. Without a seed, the order changes between runs.

$ dart main.dart
Sorted: [1, 2, 3, 4, 5, 6, 7, 8, 9]
Reversed: [9, 8, 7, 6, 5, 4, 3, 2, 1]
Even numbers: [2, 4, 6, 8]
Middle sublist: [3, 4, 5]
Shuffled: [4, 5, 9, 1, 3, 2, 6, 7, 8]

Spread Operator and Collection If/For

List literals accept the spread operator ..., and they can contain if and for elements. These make it possible to build a list from other lists in one expression.

main.dart
void main() {
  var list1 = [1, 2, 3];
  var list2 = [4, 5, 6];

  // Spread operator
  var combined = [...list1, ...list2];
  print('Combined: $combined');

  // Collection if
  var includeZero = true;
  var numbers = [if (includeZero) 0, ...list1];
  print('With optional zero: $numbers');

  // Collection for
  var squares = [for (var n in list2) n * n];
  print('Squares: $squares');
}

The spread operator copies every element of a list into the literal. The if element adds 0 only when the condition is true. The for element computes one value per element of list2.

$ dart main.dart
Combined: [1, 2, 3, 4, 5, 6]
With optional zero: [0, 1, 2, 3]
Squares: [16, 25, 36]

Fixed-length and Unmodifiable Lists

List.filled creates a fixed-length list, and List.empty(growable: true) creates an empty growable list. List.unmodifiable creates a list that cannot be changed at all. Mutating an unmodifiable list throws an UnsupportedError.

main.dart
void main() {
  var fixed = List<int>.filled(3, 0);
  var growable = List<int>.empty(growable: true);
  growable.add(1);

  print('fixed: $fixed');
  print('growable: $growable');

  // List.unmodifiable: any mutation throws
  var readOnly = List.unmodifiable([1, 2, 3]);
  try {
    readOnly.add(4);
  } on UnsupportedError catch (e) {
    print('Unmodifiable list: ${e.runtimeType}');
  }

  // A const list literal is also unmodifiable
  const constList = [10, 20];
  print('const: $constList');
}

The fixed-length list keeps its size of three, and the growable list accepts add. The unmodifiable list rejects add with an UnsupportedError. A const list literal is unmodifiable as well, and it is created at compile time.

$ dart main.dart
fixed: [0, 0, 0]
growable: [1]
Unmodifiable list: UnsupportedError
const: [10, 20]

Best Practices

Source

Dart List Documentation

This tutorial covered Dart's List with practical examples demonstrating its key features and usage patterns.

Author

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.

List all Dart tutorials.