Watch the video (11 min)
On this page26 sections
  1. Prerequisites
  2. Connection With Previous Lectures
  3. Learning Objectives
  4. 1. A List That Can Change Size
  5. 2. add — One Element at the End
  6. 3. addAll — Many Elements at the End
  7. 4. insert — One Element at a Position
  8. 5. insertAll — Many Elements at a Position
  9. 6. remove — Remove by Value
  10. 7. removeAt and removeLast — Remove by Position
  11. 8. removeRange — Remove a Block
  12. 9. removeWhere — Remove Everything That Matches
  13. 10. clear — Remove Everything
  14. 11. Fixed-Length Lists
  15. 12. final Lists vs const Lists
  16. 13. Don't Change a List While Looping Over It
  17. 14. A Real-World Example
  18. Code Examples
  19. Common Beginner Mistakes
  20. Best Practices
  21. Mini Challenge
  22. Practice Questions
  23. Practice Answers
  24. Quick Revision
  25. Key Takeaways
  26. What's Next?

Lecture 41 — Adding & Removing Elements

Category7 — Lists
Lecture041 of 140
Estimated video length11 min
TargetDart 3.12.2
LevelBeginner

So far a list kept the values it was born with. With add, insert, remove and their relatives, a list grows and shrinks while the program runs.

Contents

Prerequisites · Connection · Objectives · Lesson · Code Examples · Common Mistakes · Best Practices · Mini Challenge · Practice Questions · Answers · Takeaways

Every error message in this lecture is real. Each one was produced by running the code shown on Dart 3.12.2, so you will recognize it when you meet it yourself.


Prerequisites

Students should already understand, from Lectures 001–040:

  • final and const variables (Lecture 006)
  • for-in loops (Lecture 027)
  • named arguments (Lecture 033) and arrow / anonymous functions (Lecture 034)
  • creating lists, List<int>, length, and isEmpty (Lecture 039)
  • reading and changing elements by index, and the RangeError (Lecture 040)

Connection With Previous Lectures

In Lecture 040 we changed the values inside a list:

dart
marks[1] = 95;

But the list still had exactly the same number of elements. A shopping cart gets new items, a class gets a new student, a finished task leaves the to-do list. Writing marks[3] = 70 on a three-element list does not add a fourth element — it crashes with a RangeError. We need operations that change the size of the list.


Learning Objectives

By the end of this lecture you will be able to:

  • Add one element with add and many with addAll
  • Put elements at a chosen position with insert and insertAll
  • Remove elements by value, by index, from the end, or by range
  • Remove every element that matches a condition with removeWhere
  • Empty a list with clear
  • Tell a growable list from a fixed-length list
  • Explain why a final list can still change but a const list cannot
  • Avoid changing a list while a for-in loop is walking through it

1. A List That Can Change Size

A list written with square brackets is growable: its length changes as you add and remove elements.

Analogy — think of a shopping list on a notepad. You can write a new line at the bottom, squeeze one in between, or cross one out. The notepad is the same; the number of lines changes.

dart
void main() {
  var cart = ['Milk', 'Bread'];
  print(cart.length);
  cart.add('Eggs');
  print(cart.length);
}
output
2
3

2. add — One Element at the End

add puts one element after the last one.

dart
void main() {
  var marks = [85, 92];
  marks.add(78);
  marks.add(64);
  print(marks);
}
output
[85, 92, 78, 64]

The new element must match the list's type. marks is a List<int>, so marks.add('70') is rejected before the program runs.


3. addAll — Many Elements at the End

addAll takes another list and adds all of its elements, in order.

dart
void main() {
  var names = ['Ali', 'Sara'];
  names.addAll(['Zayan', 'Hina']);
  print(names);
}
output
[Ali, Sara, Zayan, Hina]

4. insert — One Element at a Position

insert(index, value) puts the value at that index and pushes everything after it one place to the right.

dart
void main() {
  var queue = ['Ali', 'Sara', 'Zayan'];
  queue.insert(1, 'Hina');
  print(queue);
}
output
[Ali, Hina, Sara, Zayan]

insert(0, …) adds at the front. The index may also equal length, which adds at the end.


5. insertAll — Many Elements at a Position

dart
void main() {
  var numbers = [1, 5];
  numbers.insertAll(1, [2, 3, 4]);
  print(numbers);
}
output
[1, 2, 3, 4, 5]

6. remove — Remove by Value

remove(value) deletes the first element equal to the value. It returns true if it found one and false if it did not.

dart
void main() {
  var fruits = ['apple', 'mango', 'apple'];
  print(fruits.remove('apple'));
  print(fruits);
  print(fruits.remove('banana'));
}
output
true
[mango, apple]
false

Only the first 'apple' went. Asking for a missing value is not an error — you just get false.


7. removeAt and removeLast — Remove by Position

removeAt(index) deletes the element at that index; removeLast() deletes the last one. Both return the element they removed, so you can keep it.

dart
void main() {
  var tasks = ['wash', 'cook', 'study', 'sleep'];
  var done = tasks.removeAt(1);
  var last = tasks.removeLast();
  print(done);
  print(last);
  print(tasks);
}
output
cook
sleep
[wash, study]

A wrong index gives the same RangeError you met in Lecture 040.


8. removeRange — Remove a Block

removeRange(start, end) deletes from start up to, but not including, end.

dart
void main() {
  var scores = [10, 20, 30, 40, 50];
  scores.removeRange(1, 3);
  print(scores);
}
output
[10, 40, 50]

Indexes 1 and 2 (20 and 30) went; index 3 stayed. end - start is how many elements disappear.


9. removeWhere — Remove Everything That Matches

removeWhere takes a function — the anonymous functions from Lecture 034. Dart calls it once for each element and removes every element for which it returns true.

dart
void main() {
  var marks = [85, 42, 78, 39, 91];
  marks.removeWhere((mark) => mark < 50);
  print(marks);
}
output
[85, 78, 91]

Read (mark) => mark < 50 as "remove a mark if it is below 50". Unlike remove, this removes all matches, not just the first.


10. clear — Remove Everything

dart
void main() {
  var cart = ['Milk', 'Bread', 'Eggs'];
  cart.clear();
  print(cart);
  print(cart.isEmpty);
}
output
[]
true

The list still exists and can be filled again; it is just empty.


11. Fixed-Length Lists

List.filled(length, value) creates a list of a set size, every slot holding the same value. That list is fixed-length: you may change its elements, but not its size.

dart
void main() {
  var seats = List.filled(3, 0);
  seats[1] = 7;
  print(seats);
}
output
[0, 7, 0]

Trying to grow it compiles, then stops the running program:

dart
void main() {
  var seats = List.filled(3, 0);
  seats.add(5); // ✗ fixed-length
}
output
Unhandled exception:
Unsupported operation: Cannot add to a fixed-length list

Pass the named argument growable: true when you want the filled list to grow:

dart
void main() {
  var seats = List.filled(3, 0, growable: true);
  seats.add(5);
  print(seats);
}
output
[0, 0, 0, 5]

12. final Lists vs const Lists

final stops the variable from pointing at a different list. The list itself can still change:

dart
void main() {
  final names = ['Ali'];
  names.add('Sara'); // ✓ changing the list
  print(names);
}
output
[Ali, Sara]

Writing names = ['Sara']; would fail with Error: Can't assign to the final variable 'names'. A const list goes further — nothing about it can change:

dart
void main() {
  const marks = [85, 92];
  marks.add(70); // ✗ const list
}
output
Unhandled exception:
Unsupported operation: Cannot add to an unmodifiable list

We will study const lists and other unmodifiable collections in Lecture 046 — Collection Features. For now: use final for a list you will add to.


13. Don't Change a List While Looping Over It

A for-in loop walks the list one element at a time. If the loop body adds or removes elements, Dart stops the program:

dart
void main() {
  var marks = [85, 92, 78];
  for (final mark in marks) {
    if (mark < 80) {
      marks.remove(mark); // ✗ changes the list mid-loop
    }
  }
  print(marks);
}
output
Unhandled exception:
Concurrent modification during iteration: Instance(length:2) of '_GrowableList'.

Dart calls this a ConcurrentModificationError: the list changed under the loop's feet. The fix is to let one method do the whole job:

dart
void main() {
  var marks = [85, 92, 78];
  marks.removeWhere((mark) => mark < 80);
  print(marks);
}
output
[85, 92]

14. A Real-World Example

dart
void main() {
  final cart = <String>[];
  cart.add('Milk');
  cart.addAll(['Bread', 'Eggs', 'Tea']);
  cart.insert(0, 'Rice');
  cart.remove('Eggs');
  var last = cart.removeLast();
  cart.removeWhere((item) => item.startsWith('B'));

  print('Took out: $last');
  print('Cart: $cart');
  print('Items: ${cart.length}');
}
output
Took out: Tea
Cart: [Rice, Milk]
Items: 2

The cart starts empty, grows to five items, then shrinks: 'Eggs' by value, 'Tea' from the end, and every item starting with B. final keeps cart pointing at the same list the whole time.


Code Examples

Example 1 — Building a List Step by Step

dart
void main() {
  var colors = <String>[];
  colors.add('red');
  colors.add('green');
  colors.add('blue');
  print(colors);
}
output
[red, green, blue]

Example 2 — Joining Two Groups

dart
void main() {
  var morning = ['Ali', 'Sara'];
  var evening = ['Zayan', 'Hina'];
  morning.addAll(evening);
  print(morning);
  print(evening);
}
output
[Ali, Sara, Zayan, Hina]
[Zayan, Hina]

addAll copies the elements; evening is unchanged.

Example 3 — Front, Middle, End

dart
void main() {
  var letters = ['b', 'd'];
  letters.insert(0, 'a');
  letters.insert(2, 'c');
  letters.insert(letters.length, 'e');
  print(letters);
}
output
[a, b, c, d, e]

Example 4 — remove Reports Success

dart
void main() {
  var guests = ['Ali', 'Sara'];
  var removed = guests.remove('Zayan');
  print(removed ? 'Removed' : 'Not on the list');
}
output
Not on the list

Example 5 — Undo the Last Step

dart
void main() {
  var history = ['open', 'type', 'delete'];
  var undone = history.removeLast();
  print('Undo: $undone');
  print(history);
}
output
Undo: delete
[open, type]

Example 6 — Trim the Ends

dart
void main() {
  var readings = [0, 12, 15, 14, 0];
  readings.removeAt(0);
  readings.removeLast();
  print(readings);
}
output
[12, 15, 14]

Example 7 — Student Marks: Drop the Failing Marks

dart
void main() {
  final marks = [72, 45, 88, 30, 65];
  marks.removeWhere((mark) => mark < 50);
  print('Passing marks: $marks');
  print('Count: ${marks.length}');
}
output
Passing marks: [72, 88, 65]
Count: 3

How it works

  1. The anonymous function returns true for 45 and 30, so those two are removed.
  2. final is fine: the list changes, the variable does not.

Example 8 — Shopping Total After Removing an Item

dart
void main() {
  final prices = [450, 1200, 300];
  prices.add(800);
  prices.remove(1200);
  var total = 0;
  for (final price in prices) {
    total += price;
  }
  print('Prices: $prices');
  print('Total: $total');
}
output
Prices: [450, 300, 800]
Total: 1550

How it works

  1. The list changes first (add, remove) — before the loop starts.
  2. The loop only reads, so no concurrent modification can happen.

Example 9 — Bank Balance: Recent Transactions Only

dart
void main() {
  final transactions = [500, -200, 1000, -50, 300, -120];
  transactions.removeRange(0, 3);
  var balance = 0;
  for (final amount in transactions) {
    balance += amount;
  }
  print('Recent: $transactions');
  print('Change: $balance');
}
output
Recent: [-50, 300, -120]
Change: 130

How it works

  1. removeRange(0, 3) removes the three oldest transactions — indexes 0, 1, 2.
  2. The loop adds up what is left: -50 + 300 - 120 is 130.

Example 10 — Temperature Log With a Fixed Week

dart
void main() {
  var week = List.filled(7, 0.0);
  week[0] = 21.5;
  week[1] = 24.0;
  week[6] = 19.0;
  print(week);
  print('Days: ${week.length}');
}
output
[21.5, 24.0, 0.0, 0.0, 0.0, 0.0, 19.0]
Days: 7

How it works

  1. A week always has seven days, so a fixed-length list fits: values change, the size never does.
  2. Every slot starts at 0.0; filling a slot uses an index, not add.

Common Beginner Mistakes

1. Removing inside a for-in loop

dart
for (final mark in marks) {
  if (mark < 80) marks.remove(mark);
}
output
Unhandled exception:
Concurrent modification during iteration: Instance(length:2) of '_GrowableList'.

Use removeWhere instead (see section 13).

2. Expecting remove to delete every copy

['apple', 'mango', 'apple'].remove('apple') deletes only the first 'apple'. To delete all, use removeWhere((fruit) => fruit == 'apple').

3. Passing a single value to addAll

dart
void main() {
  var marks = [85, 92];
  marks.addAll(70); // ✗ needs a list
}
output
Error: The argument type 'int' can't be assigned to the parameter type 'Iterable<int>'.

Use add(70), or addAll([70]). Iterable is the family that lists belong to; we meet it in Lecture 046.

4. Using the result of add

dart
void main() {
  var marks = [85, 92];
  var result = marks.add(70); // add returns nothing (void)
  print(result); // ✗ using that nothing is the error
}
output
Error: This expression has type 'void' and can't be used.

add changes the list and gives nothing back. Dart reports the error on the line that uses result. Print the list instead.

5. Removing from an empty list

dart
void main() {
  var names = <String>[];
  names.removeLast(); // ✗ nothing to remove
}
output
Unhandled exception:
RangeError (length): Invalid value: Valid value range is empty: -1

Check isNotEmpty first.

6. Forgetting that removeRange excludes end

removeRange(1, 3) removes two elements (indexes 1 and 2), not three.

7. Adding to a fixed-length or const list

Both compile and then crash with Unsupported operation. Use a bracket list, or List.filled(…, growable: true).


Best Practices

  • Declare a list you will add to with final, not const.
  • Use add for one element and addAll for a group.
  • Prefer removeWhere over a hand-written loop when removing by condition.
  • Never add or remove inside a for-in loop over the same list.
  • Keep the value returned by removeAt / removeLast when you need it.
  • Check isNotEmpty before removeLast.
  • Use List.filled only when the size is truly fixed.

Mini Challenge

Start with the class list ['Ali', 'Sara']. Add 'Zayan' at the end, put 'Hina' at the front, remove 'Sara', then print the list and its length.

output
[Hina, Ali, Zayan]
Students: 3

Try it yourself before opening the solution.

Show solution
dart
void main() {
  final students = ['Ali', 'Sara'];
  students.add('Zayan');
  students.insert(0, 'Hina');
  students.remove('Sara');
  print(students);
  print('Students: ${students.length}');
}
output
[Hina, Ali, Zayan]
Students: 3

Practice Questions

Work through these in order — they run from beginner to application to challenge.

Beginner

  1. Start with [1, 2, 3], add 4, and print the list.
  2. Start with ['Ali'], add 'Sara' and 'Zayan' with one call, and print the list.
  3. Start with [10, 30], insert 20 between them, and print the list.

Application

  1. Start with ['red', 'green', 'blue']. Remove 'green' by value and print the result of remove, then the list.
  2. Start with ['a', 'b', 'c', 'd']. Remove the element at index 2, print the removed element, then the list.
  3. Start with [5, 10, 15, 20, 25, 30]. Remove the middle two elements (15 and 20) with removeRange and print the list.
  4. Start with [3, 8, 12, 5, 20]. Remove every number greater than 9 and print the list.
  5. Create a fixed-length list of four false values, set index 2 to true, and print it.

Challenge

  1. Start with final cart = ['Milk', 'Bread', 'Eggs', 'Butter']. Remove the last item and keep it, remove every item containing the letter e, then print Removed: … and Cart: ….
  2. Start with final temps = [18.5, -2.0, 25.0, 99.0, 21.0]. Remove readings below 0 or above 50, then add 22.5, and print the list and the average of its values.

Practice Answers

Question 1 — Solution

dart
void main() {
  var numbers = [1, 2, 3];
  numbers.add(4);
  print(numbers);
}
output
[1, 2, 3, 4]

Question 2 — Solution

dart
void main() {
  var names = ['Ali'];
  names.addAll(['Sara', 'Zayan']);
  print(names);
}
output
[Ali, Sara, Zayan]

Question 3 — Solution

dart
void main() {
  var numbers = [10, 30];
  numbers.insert(1, 20);
  print(numbers);
}
output
[10, 20, 30]

Question 4 — Solution

dart
void main() {
  var colors = ['red', 'green', 'blue'];
  print(colors.remove('green'));
  print(colors);
}
output
true
[red, blue]

Question 5 — Solution

dart
void main() {
  var letters = ['a', 'b', 'c', 'd'];
  var removed = letters.removeAt(2);
  print(removed);
  print(letters);
}
output
c
[a, b, d]

Question 6 — Solution

dart
void main() {
  var numbers = [5, 10, 15, 20, 25, 30];
  numbers.removeRange(2, 4);
  print(numbers);
}
output
[5, 10, 25, 30]

15 is at index 2 and 20 at index 3, so the end is 4.

Question 7 — Solution

dart
void main() {
  var numbers = [3, 8, 12, 5, 20];
  numbers.removeWhere((n) => n > 9);
  print(numbers);
}
output
[3, 8, 5]

Question 8 — Solution

dart
void main() {
  var flags = List.filled(4, false);
  flags[2] = true;
  print(flags);
}
output
[false, false, true, false]

Question 9 — Solution

dart
void main() {
  final cart = ['Milk', 'Bread', 'Eggs', 'Butter'];
  var removed = cart.removeLast();
  cart.removeWhere((item) => item.contains('e'));
  print('Removed: $removed');
  print('Cart: $cart');
}
output
Removed: Butter
Cart: [Milk, Eggs]

contains('e') is case-sensitive: 'Bread' has a small e, but 'Eggs' only has a capital E, so it stays.

Question 10 — Solution

dart
void main() {
  final temps = [18.5, -2.0, 25.0, 99.0, 21.0];
  temps.removeWhere((t) => t < 0 || t > 50);
  temps.add(22.5);
  var sum = 0.0;
  for (final t in temps) {
    sum += t;
  }
  print(temps);
  print('Average: ${sum / temps.length}');
}
output
[18.5, 25.0, 21.0, 22.5]
Average: 21.75

The condition uses || from Lecture 019, so a reading is removed if either side is true.


Quick Revision

  1. What is the difference between add and addAll?
  2. What does insert(0, x) do?
  3. What does remove return, and how many copies does it delete?
  4. Which two methods return the element they removed?
  5. Is the end index of removeRange removed?
  6. What does the function passed to removeWhere decide?
  7. Why does List.filled(3, 0).add(1) crash?
  8. Why can a final list grow while a const list cannot?

Key Takeaways

  • A bracket list is growable: its length changes as you add and remove.
  • add / addAll append at the end; insert / insertAll put elements at an index.
  • remove(value) deletes the first match and returns true or false.
  • removeAt(index) and removeLast() delete by position and return the removed element.
  • removeRange(start, end) deletes from start up to, but not including, end.
  • removeWhere removes every element for which its function returns true.
  • clear empties the list; the list itself remains.
  • List.filled makes a fixed-length list unless you pass growable: true.
  • A final list can change its contents; a const list cannot.
  • Adding or removing inside a for-in over the same list throws a ConcurrentModificationError.

What's Next?

Lecture 42 — List Searching. Now that a list can grow and shrink, we need to find things in it: checking whether a value is there with contains, finding its position with indexOf, and picking out the first element that matches a condition.


Video lecture

Subscribe
Placeholder video — the real recording isn't linked yet.