On this page26 sections
- Prerequisites
- Connection With Previous Lectures
- Learning Objectives
- 1. Three Questions You Can Ask a List
- 2. contains — Is the Value There?
- 3. indexOf With a Start Index
- 4. lastIndexOf — Search From the End
- 5. indexWhere — Position of the First Match
- 6. lastIndexWhere — Position of the Last Match
- 7. firstWhere — The First Matching Element
- 8. When Nothing Matches
- 9. orElse — A Fallback When Nothing Matches
- 10. lastWhere — The Last Matching Element
- 11. singleWhere — Exactly One Match
- 12. any and every — Questions About the Whole List
- 13. Choosing the Right Method
- 14. A Real-World Example
- Code Examples
- Common Beginner Mistakes
- Best Practices
- Mini Challenge
- Practice Questions
- Practice Answers
- Quick Revision
- Key Takeaways
- What's Next?
Lecture 42 — List Searching
| Category | 7 — Lists |
| Lecture | 042 of 140 |
| Estimated video length | 10 min |
| Target | Dart 3.12.2 |
| Level | Beginner |
Until now, finding something in a list meant writing a loop and a counter. Dart's search methods answer "is it there?", "where is it?" and "which one is it?" in a single line.
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–041:
- nullable types and
??(Lectures 013–014) for-inloops (Lecture 027)- arrow and anonymous functions (Lecture 034)
- creating lists,
length, andisEmpty(Lecture 039) - indexes,
indexOf, and the-1result (Lecture 040) removeWhereand passing a function to a list method (Lecture 041)
Connection With Previous Lectures
In Lecture 041 the cart grew and shrank. Now we want to ask it questions: Is there milk in the cart? Where is the first failing mark? Which student scored above 90? With what we know, each question needs a loop:
var found = false;
for (final item in cart) {
if (item == 'Milk') found = true;
}Four lines and a variable for a yes/no answer. In Lecture 040 indexOf gave us a position, but
only for an exact value — it cannot find "the first mark below 50". This lecture gives us one
method for each kind of question.
Learning Objectives
By the end of this lecture you will be able to:
- Check whether a value is in a list with
contains - Find positions with
indexOfandlastIndexOf, including a start index - Find the position of the first or last element matching a condition with
indexWhereandlastIndexWhere - Get the matching element itself with
firstWhereandlastWhere - Recognize the
Bad state: No elementerror and prevent it withorElse - Use
singleWherewhen exactly one element should match - Test a condition across the whole list with
anyandevery - Choose the right search method for the question you are asking
1. Three Questions You Can Ask a List
Every search answers one of three questions: is it there? (a bool), where is it? (an
int index) or which one is it? (the element).
Analogy — think of a class register. "Is Sara here?" gets a yes or no. "Which row is she in?" gets a number. "Who is the first student with full marks?" gets a name.
void main() {
var names = ['Ali', 'Sara', 'Zayan'];
print(names.contains('Sara'));
print(names.indexOf('Sara'));
print(names.firstWhere((name) => name.length > 4));
}true
1
Zayan2. contains — Is the Value There?
contains(value) returns true if any element equals the value.
void main() {
var cart = ['Milk', 'Bread', 'Eggs'];
print(cart.contains('Milk'));
print(cart.contains('milk'));
}true
falseThe comparison is exact: 'milk' with a small m is a different string.
3. indexOf With a Start Index
Lecture 040 showed that indexOf returns the first matching index, or -1. An optional second
argument says where to start looking:
void main() {
var marks = [70, 85, 70, 92, 70];
print(marks.indexOf(70));
print(marks.indexOf(70, 1));
print(marks.indexOf(70, 3));
}0
2
4Starting at index 1 skips the first 70, so the next one is found at 2.
4. lastIndexOf — Search From the End
lastIndexOf returns the last matching index. Its optional start index is where the search
begins going backwards.
void main() {
var marks = [70, 85, 70, 92, 70];
print(marks.lastIndexOf(70));
print(marks.lastIndexOf(70, 3));
print(marks.lastIndexOf(100));
}4
2
-15. indexWhere — Position of the First Match
indexOf needs the exact value. indexWhere takes a function instead, like removeWhere
in Lecture 041, and returns the index of the first element for which it returns true.
void main() {
var marks = [85, 92, 42, 78, 39];
print(marks.indexWhere((mark) => mark < 50));
print(marks.indexWhere((mark) => mark > 95));
}2
-1No match gives -1, just like indexOf. It also accepts a start index:
marks.indexWhere((mark) => mark < 50, 3) returns 4.
6. lastIndexWhere — Position of the Last Match
void main() {
var temps = [18.0, 31.5, 22.0, 33.0, 25.5];
print(temps.lastIndexWhere((t) => t > 30));
}3The last day above 30 degrees is at index 3.
7. firstWhere — The First Matching Element
Often we want the element, not its position. firstWhere returns the first element for
which the function returns true.
void main() {
var prices = [450, 1200, 300, 2500];
var expensive = prices.firstWhere((price) => price > 1000);
print(expensive);
}12008. When Nothing Matches
indexWhere can return -1, but firstWhere has to return an element — and there is none. So
the program stops:
void main() {
var prices = [450, 1200, 300];
var huge = prices.firstWhere((price) => price > 5000); // ✗ no match
print(huge);
}Unhandled exception:
Bad state: No elementThis is a StateError: the list was not in a state where an answer exists. We will learn to
catch errors like this in Lecture 084 — try, catch & finally. For now, we prevent it.
9. orElse — A Fallback When Nothing Matches
firstWhere has a named argument orElse: a function whose result is returned when no element
matches.
void main() {
var prices = [450, 1200, 300];
var huge = prices.firstWhere((price) => price > 5000, orElse: () => 0);
print(huge);
}0The fallback must have the list's type. For a List<int> it must be an int, so pick a value
that cannot be a real answer, such as 0 or -1.
10. lastWhere — The Last Matching Element
lastWhere works like firstWhere but searches from the end. It takes orElse too.
void main() {
var names = ['Ali', 'Sara', 'Zayan', 'Sana'];
print(names.lastWhere((name) => name.startsWith('S')));
print(names.lastWhere((name) => name.startsWith('X'), orElse: () => 'none'));
}Sana
none11. singleWhere — Exactly One Match
singleWhere is for when exactly one element should match, such as an ID. It fails if
there are none — or if there are several:
void main() {
var marks = [85, 42, 78];
print(marks.singleWhere((mark) => mark < 50));
print(marks.singleWhere((mark) => mark > 50)); // ✗ two matches
}42
Unhandled exception:
Bad state: Too many elementsUse it only when "more than one" really means something is wrong. Otherwise use firstWhere.
12. any and every — Questions About the Whole List
any returns true if at least one element passes the test. every returns true if
all of them do.
void main() {
var marks = [85, 92, 42, 78];
print(marks.any((mark) => mark < 50));
print(marks.every((mark) => mark >= 50));
print(marks.every((mark) => mark > 0));
}true
false
trueany stops at the first true, every at the first false. On an empty list, any is
false (nothing passed) and every is true (nothing failed).
13. Choosing the Right Method
| Question | Method | Returns | When nothing matches |
|---|---|---|---|
| Is this value there? | contains | bool | false |
| Does any / every element pass? | any / every | bool | false / true |
| Where is this value? | indexOf / lastIndexOf | int | -1 |
| Where is the first / last match? | indexWhere / lastIndexWhere | int | -1 |
| Which element matches? | firstWhere / lastWhere | element | error, or orElse |
| Which one element matches? | singleWhere | element | error, or orElse |
Dart can also collect every matching element into a new collection with where. We will
study it in Lecture 043 — List Transformation.
14. A Real-World Example
void main() {
final students = ['Ali', 'Sara', 'Zayan', 'Hina'];
final marks = [72, 95, 45, 88];
print('Hina enrolled: ${students.contains('Hina')}');
print('Everyone passed: ${marks.every((mark) => mark >= 50)}');
var failing = marks.indexWhere((mark) => mark < 50);
if (failing != -1) {
print('Needs help: ${students[failing]}');
}
var top = marks.firstWhere((mark) => mark > 90, orElse: () => 0);
print('First mark above 90: $top by ${students[marks.indexOf(top)]}');
}Hina enrolled: true
Everyone passed: false
Needs help: Zayan
First mark above 90: 95 by Saracontains and every answer yes/no questions. indexWhere gives a position we can use in the
second list, after checking for -1. firstWhere finds the mark and indexOf turns it back
into a position.
Code Examples
Example 1 — Checking Membership
void main() {
var guests = ['Ali', 'Sara', 'Zayan'];
print(guests.contains('Zayan'));
print(guests.contains('Omar'));
}true
falseExample 2 — Every Position of a Value
void main() {
var letters = ['a', 'b', 'a', 'c', 'a'];
var position = letters.indexOf('a');
while (position != -1) {
print(position);
position = letters.indexOf('a', position + 1);
}
}0
2
4Each search starts one place after the previous match, until indexOf returns -1.
Example 3 — First and Last Occurrence
void main() {
var visits = ['Ali', 'Sara', 'Ali', 'Zayan'];
print('First visit: ${visits.indexOf('Ali')}');
print('Last visit: ${visits.lastIndexOf('Ali')}');
}First visit: 0
Last visit: 2Example 4 — First Even Number
void main() {
var numbers = [7, 13, 8, 21, 4];
print(numbers.indexWhere((n) => n % 2 == 0));
print(numbers.firstWhere((n) => n % 2 == 0));
}2
8The same test gives the position with indexWhere and the element with firstWhere.
Example 5 — A Safe Lookup
void main() {
var names = ['Ali', 'Sara'];
var match = names.firstWhere(
(name) => name.startsWith('Z'),
orElse: () => 'nobody',
);
print('Found: $match');
}Found: nobodyExample 6 — any and every on Words
void main() {
var words = ['dart', 'list', 'search'];
print(words.any((word) => word.length > 5));
print(words.every((word) => word.length > 5));
}true
falseExample 7 — Student Marks: Who Failed First?
void main() {
final students = ['Ali', 'Sara', 'Zayan'];
final marks = [78, 41, 35];
var first = marks.indexWhere((mark) => mark < 50);
var last = marks.lastIndexWhere((mark) => mark < 50);
print('First below 50: ${students[first]}');
print('Last below 50: ${students[last]}');
}First below 50: Sara
Last below 50: ZayanHow it works
- Both searches use the same test, from opposite ends.
- The index found in
marksis used to read the matching name fromstudents.
Example 8 — Shopping: Is Anything Over Budget?
void main() {
final prices = [450, 1200, 300, 800];
const budget = 1000;
if (prices.any((price) => price > budget)) {
var item = prices.firstWhere((price) => price > budget);
print('Over budget: $item');
} else {
print('All items fit the budget');
}
}Over budget: 1200How it works
anychecks first, sofirstWhereonly runs when a match is guaranteed.- Inside the function,
budgetis read frommain— a closure, as in Lecture 037.
Example 9 — Bank Balance: The Last Withdrawal
void main() {
final transactions = [500, -200, 1000, -50, 300];
var lastOut = transactions.lastWhere((amount) => amount < 0, orElse: () => 0);
var allSmall = transactions.every((amount) => amount > -1000);
print('Last withdrawal: $lastOut');
print('No big withdrawals: $allSmall');
}Last withdrawal: -50
No big withdrawals: trueHow it works
- Withdrawals are negative, so
amount < 0finds them;lastWherepicks the most recent. orElse: () => 0means "no withdrawals" instead of a crash.
Example 10 — Temperature: First Hot Day
void main() {
final days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri'];
final temps = [24.5, 28.0, 35.5, 31.0, 36.5];
var hot = temps.indexWhere((t) => t > 35);
if (hot == -1) {
print('No hot days');
} else {
print('First hot day: ${days[hot]} (${temps[hot]}°C)');
}
}First hot day: Wed (35.5°C)How it works
indexWherereturns2, the position of35.5.- The
-1check keepsdays[hot]from ever beingdays[-1].
Common Beginner Mistakes
1. Calling firstWhere without orElse when there may be no match
var huge = prices.firstWhere((price) => price > 5000);Unhandled exception:
Bad state: No elementAdd orElse, or check with any first.
2. Returning null from orElse
void main() {
var names = ['Ali', 'Sara'];
// String? found = names.firstWhere(
// (name) => name.startsWith('Z'),
// orElse: () => null, // ✗ must be a String
// );
}Error: A value of type 'Null' can't be returned from a function with return type 'String'.orElse must return the list's type. Use a fallback like 'none' — or use indexWhere and
check for -1.
3. Passing a value where a function is expected
void main() {
var marks = [85, 42, 78];
// var low = marks.indexWhere(50); // ✗ needs a function
}Error: The argument type 'int' can't be assigned to the parameter type 'bool Function(int)'.Write the test as a function: marks.indexWhere((mark) => mark == 50) — or use indexOf(50).
4. Treating indexOf as a yes/no answer
void main() {
var marks = [85, 42, 78];
// if (marks.indexOf(42)) print('found'); // ✗ int, not bool
}Error: A value of type 'int' can't be assigned to a variable of type 'bool'.Use contains(42), or indexOf(42) != -1.
5. Searching with the wrong type
void main() {
var marks = [85, 42, 78];
print(marks.contains('85'));
}falseNo error — the string '85' simply never equals the number 85. Make sure the value you search
for has the list's type.
6. Using singleWhere when duplicates are allowed
If two elements can match, singleWhere stops with Bad state: Too many elements. Use
firstWhere.
Best Practices
- Use
containsfor a yes/no question about a value,any/everyfor a condition. - Check
indexOfandindexWhereresults for-1before using them as an index. - Give
firstWhereandlastWhereanorElseunless a match is guaranteed. - Choose an
orElsevalue that cannot be confused with a real result. - Prefer a search method over a hand-written loop with a flag variable.
- Reserve
singleWherefor values that must be unique.
Mini Challenge
Given final marks = [64, 88, 47, 95, 71];, print whether any mark is below 50, the position
of the first mark above 90, and the last mark between 60 and 80.
Any below 50: true
First above 90 at: 3
Last in 60–80: 71Try it yourself before opening the solution.
Show solution
void main() {
final marks = [64, 88, 47, 95, 71];
print('Any below 50: ${marks.any((mark) => mark < 50)}');
print('First above 90 at: ${marks.indexWhere((mark) => mark > 90)}');
var middle = marks.lastWhere((mark) => mark >= 60 && mark <= 80);
print('Last in 60–80: $middle');
}Any below 50: true
First above 90 at: 3
Last in 60–80: 71Practice Questions
Work through these in order — they run from beginner to application to challenge.
Beginner
- Given
['red', 'green', 'blue'], print whether it contains'green'and whether it contains'pink'. - Given
[3, 7, 3, 9, 3], print the first and the last index of3. - Given
[12, 5, 30, 8], print the first element greater than10.
Application
- Given
[3, 7, 3, 9, 3], print the index of the first3after index0. - Given
[15, 22, 8, 40, 5], print the index of the last number smaller than10. - Given
['Ali', 'Sara', 'Zayan'], find the first name longer than 6 letters, printing'none'if there is no such name. - Given
[72, 65, 88, 91], print whether every mark is at least60and whether any mark is above90. - Given
[101, 205, 307](product IDs), usesingleWhereto print the ID that starts with2when written as text. Hint:'$id'.startsWith('2').
Challenge
- Given
final items = ['Milk', 'Bread', 'Eggs']andfinal prices = [180, 120, 300], find the first price above150and print the item's name and price asMilk: 180. If no price matches, printNothing above 150. - Given
final temps = [21.0, 19.5, -3.0, 25.0, -1.5], print the index of the first reading below0, the last reading below0, and whether all readings are below30.
Practice Answers
Question 1 — Solution
void main() {
var colors = ['red', 'green', 'blue'];
print(colors.contains('green'));
print(colors.contains('pink'));
}true
falseQuestion 2 — Solution
void main() {
var numbers = [3, 7, 3, 9, 3];
print(numbers.indexOf(3));
print(numbers.lastIndexOf(3));
}0
4Question 3 — Solution
void main() {
var numbers = [12, 5, 30, 8];
print(numbers.firstWhere((n) => n > 10));
}12Question 4 — Solution
void main() {
var numbers = [3, 7, 3, 9, 3];
print(numbers.indexOf(3, 1));
}2Question 5 — Solution
void main() {
var numbers = [15, 22, 8, 40, 5];
print(numbers.lastIndexWhere((n) => n < 10));
}4Question 6 — Solution
void main() {
var names = ['Ali', 'Sara', 'Zayan'];
var long = names.firstWhere((name) => name.length > 6, orElse: () => 'none');
print(long);
}noneQuestion 7 — Solution
void main() {
var marks = [72, 65, 88, 91];
print(marks.every((mark) => mark >= 60));
print(marks.any((mark) => mark > 90));
}true
trueQuestion 8 — Solution
void main() {
var ids = [101, 205, 307];
print(ids.singleWhere((id) => '$id'.startsWith('2')));
}205Question 9 — Solution
void main() {
final items = ['Milk', 'Bread', 'Eggs'];
final prices = [180, 120, 300];
var index = prices.indexWhere((price) => price > 150);
if (index == -1) {
print('Nothing above 150');
} else {
print('${items[index]}: ${prices[index]}');
}
}Milk: 180indexWhere is the right tool here: the position lets us read both lists.
Question 10 — Solution
void main() {
final temps = [21.0, 19.5, -3.0, 25.0, -1.5];
print(temps.indexWhere((t) => t < 0));
print(temps.lastWhere((t) => t < 0));
print(temps.every((t) => t < 30));
}2
-1.5
trueThe question asks for the first index but the last reading, so one line uses
indexWhere and the other lastWhere.
Quick Revision
- Which method answers "is this value in the list?"
- What does the second argument of
indexOfdo? - What is the difference between
indexOfandindexWhere? - What do
indexWhereandfirstWheregive back when nothing matches? - What does
orElsedo, and what type must it return? - When does
singleWherefail? - What do
anyandeveryreturn on an empty list?
Key Takeaways
contains(value)answers "is it there?" with abool.indexOf/lastIndexOffind an exact value's first / last index, or-1; both accept a start index.indexWhere/lastIndexWherefind the index of the first / last element passing a test, or-1.firstWhere/lastWherereturn the matching element.- With no match and no
orElse, they stop the program withBad state: No element. orElsesupplies a fallback of the list's own type.singleWhereexpects exactly one match and fails on zero or several.anyistrueif at least one element passes;everyistrueif all do.
What's Next?
Lecture 43 — List Transformation. Searching finds one element or one position. Next we
build new lists from old ones: collecting every match with where, changing every element
with map, and more.