The Day Dart Fooled Me: A List, A Loop, and a Vanishing Value


A few days ago, I was working on a seemingly simple task in one of my Dart projects removing some unwanted elements from a list.
It was part of a feature I was building for a chat app. I needed to filter out banned users from the group list before displaying it in the UI. So, I wrote something quick and simple:
final users = ['Alice', 'Bob', 'Charlie', 'David'];
final banned = ['Bob', 'Charlie'];
for (var user in users) {
if (banned.contains(user)) {
users.remove(user);
}
}
print(users);
Expected Output?['Alice', 'David']
Actual Output?['Alice', 'Charlie']
Wait… what?
I stared at the console like it just betrayed me. Charlie was supposed to be gone. He was in the banned list. Why was he still there?
My Debugging Spiral
At first, I thought maybe I’d made a typo or maybe contains()
was being weird. So, I added print statements to check what was going on:
for (var user in users) {
print('Checking: $user');
if (banned.contains(user)) {
print('Removing: $user');
users.remove(user);
}
}
Here’s what I saw in the console:
Checking: Alice
Checking: Bob
Removing: Bob
Checking: David
Wait, what happened to Charlie?!
He didn’t even get checked! Dart just… skipped over him.
That’s when I realized, I’d stumbled into a classic trap: modifying a list while iterating over it.
What Really Happened (Under the Hood)
Here’s the breakdown of how Dart handled it:
Start with
users = ['Alice', 'Bob', 'Charlie', 'David']
First iteration:
user = Alice
→ not bannedSecond iteration:
user = Bob
→ banned →remove('Bob')
→ list becomes['Alice', 'Charlie', 'David']
The loop moves to the next index… but now it skips Charlie and lands on David!
So, Charlie was never checked. The list shifted, and Dart’s internal iterator just kept moving forward like nothing happened.
So, I Tried This Instead…
I remembered a better way a declarative approach:
users.removeWhere((user) => banned.contains(user));
Or even safer:
final filteredUsers = users.where((user) => !banned.contains(user)).toList();
Both gave me the clean, expected result:
['Alice', 'David']
No surprises. No skipped elements. Just clean, predictable code.
Another Test: Minimal Repro
To make sure I fully understood what was happening, I tested it with a simpler list:
final list = [1, 2, 3];
for (var i in list) {
list.remove(i);
}
print(list); // 🤔
Guess what?
Output: [2]
Most people would expect []
, but 2 gets skipped for the same reason once you remove an item, the list shifts, and the iterator keeps walking forward, blind to the changes.
The Lesson: Be Careful When Mutating in a Loop
This bug was subtle but dangerous. It taught me a valuable lesson:
Never modify a list while iterating over it.
It’s one of those little things that seems harmless until it bites you in production.
Go Declarative, Go Safe
Since that day, I’ve made it a rule of thumb:
Avoid manual mutation during iteration
Prefer
removeWhere
,where
, or.toList()
when filteringEmbrace immutability whenever possible
Because honestly, why deal with index voodoo when Dart gives us clean, expressive tools?
TL; DR
Mutating a list while looping (
for-in
) can cause elements to be skipped.Dart’s iterator doesn’t update when the list changes mid-loop.
Always use declarative approaches like
removeWhere()
orwhere()
for predictable behavior.
What About You?
Have you ever hit this kind of issue in Dart or another language? I’d love to hear your debugging war stories! Let’s learn from each other.
Subscribe to my newsletter
Read articles from Md. Al - Amin directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Md. Al - Amin
Md. Al - Amin
Experienced Android Developer with a demonstrated history of working for the IT industry. Skilled in JAVA, Dart, Flutter, and Teamwork. Strong Application Development professional with a Bachelor's degree focused in Computer Science & Engineering from Daffodil International University-DIU.