On this page26 sections
- Prerequisites
- Connection With Previous Lectures
- Learning Objectives
- 1. What Is a Map?
- 2. Writing a Map Literal
- 3. The Type Map<K, V>
- 4. Empty Maps Need a Type
- 5. Reading a Value with map[key]
- 6. map[key] Returns V?
- 7. Handling the null: ?? and !
- 8. The Promotion Trap — Use a Local Variable
- 9. Adding and Updating — Keys Are Unique
- 10. Updating from the Old Value
- 11. remove — Delete by Key
- 12. Asking Questions About a Map
- 13. putIfAbsent — Add Only If Missing
- 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 49 — Maps
| Category | 8 — Sets & Maps |
| Lecture | 049 of 140 |
| Estimated video length | 12 min |
| Target | Dart 3.12.2 |
| Level | Beginner |
A list finds a value by its position. A map finds a value by a name you choose — a student's name, a product, a city — so you never have to remember where something is stored.
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–048:
- nullable types,
??,??=, and!(Lectures 013–015) - type promotion, and why only local variables are promoted (Lectures 016 and 022)
ifstatements andfor-inloops (Lectures 022 and 027)- anonymous functions (Lecture 034)
- lists,
List<int>, indexes,length,isEmpty, andremove(Lectures 039–041) - sets and curly-brace literals (Lectures 047–048)
Connection With Previous Lectures
With a list, Ali's mark lives at an index:
var marks = [85, 92, 78];
print(marks[1]);But which student is marks[1]? You have to remember that index 1 means Sara. Add a student
in the middle and every index shifts. A set (Lecture 047) can't help either — it has no way to
look a value up at all. We want to ask the question the way a person would: "What is
Sara's mark?"
Learning Objectives
By the end of this lecture you will be able to:
- Explain what keys and values are
- Create a map with a literal and a
Map<K, V>type - Read a value with
map[key]and explain why the result is nullable - Handle a missing key with
??,!, or a promoted local variable - Add, update, and remove entries
- Explain why keys are unique
- Ask questions with
containsKey,containsValue,length, andisEmpty - Add a value only when it is missing with
putIfAbsent
1. What Is a Map?
A map stores pairs. Each pair, called an entry, has a key and a value. You give the map a key; it gives you back the value.
Analogy — think of a contacts app on your phone. You search for "Sara" (the key) and get her number (the value). You never ask for "contact number 7".
void main() {
var marks = {'Ali': 85, 'Sara': 92, 'Zayan': 78};
print(marks);
}{Ali: 85, Sara: 92, Zayan: 78}2. Writing a Map Literal
A map uses curly braces, like a set — but every item is a key: value pair, separated by
commas.
void main() {
var capitals = {'Pakistan': 'Islamabad', 'Japan': 'Tokyo', 'Egypt': 'Cairo'};
print(capitals);
}{Pakistan: Islamabad, Japan: Tokyo, Egypt: Cairo}The colon is what makes it a map: {'a', 'b'} is a set, {'a': 1, 'b': 2} is a map.
3. The Type Map<K, V>
A map type has two type arguments: K for the keys and V for the values. Dart inferred
Map<String, int> for marks above. You can write it yourself, and keys don't have to be
strings:
void main() {
Map<int, String> rollNumbers = {1: 'Ali', 2: 'Sara', 3: 'Zayan'};
print(rollNumbers[2]);
}SaraBoth types are checked. A String value or an int key in a Map<String, int> is rejected:
void main() {
var prices = {'apple': 120};
prices['kiwi'] = '50'; // ✗ value must be int
prices[5] = 9; // ✗ key must be String
}Error: A value of type 'String' can't be assigned to a variable of type 'int'.
prices['kiwi'] = '50'; // ✗ value must be int
^
Error: A value of type 'int' can't be assigned to a variable of type 'String'.
prices[5] = 9; // ✗ key must be String
^4. Empty Maps Need a Type
As you saw in Lecture 047, plain {} is an empty map, not a set. With nothing inside,
Dart can't infer the types, so it makes a Map<dynamic, dynamic> that accepts anything:
void main() {
var anything = {};
anything['a'] = 1;
anything[2] = 'x';
print(anything);
}{a: 1, 2: x}Give an empty map its types, exactly as with <int>[] in Lecture 039:
void main() {
var stock = <String, int>{};
Map<String, double> temps = {};
print(stock.isEmpty);
print(temps.length);
}true
05. Reading a Value with map[key]
Square brackets with a key read its value. A key that isn't there is not an error — you
get null:
void main() {
var prices = {'apple': 120, 'mango': 200};
print(prices['mango']);
print(prices['kiwi']);
}200
nullCompare that with a list, where a missing index crashes with a RangeError (Lecture 040). A
map answers "no such key" with null.
6. map[key] Returns V?
Because any key might be missing, Dart types prices['apple'] as int?, not int — even when
you can see the key is there. Null safety (Lectures 012–016) then guards every use:
void main() {
var prices = {'apple': 120};
int price = prices['apple']; // ✗
print(prices['apple'] + 1); // ✗
}Error: A value of type 'int?' can't be assigned to a variable of type 'int'.
int price = prices['apple']; // ✗
^
Error: Operator '+' cannot be called on 'int?' because it is potentially null.
print(prices['apple'] + 1); // ✗
^These are the same messages you met in Lecture 013. The map is fine; you just have to say what happens when the key is missing.
7. Handling the null: ?? and !
?? (Lecture 014) gives a fallback for a missing key and turns int? into int:
void main() {
var prices = {'apple': 120, 'mango': 200};
int apple = prices['apple'] ?? 0;
int kiwi = prices['kiwi'] ?? 0;
print('$apple $kiwi');
}120 0! (Lecture 015) says "I promise the key is there". If you are wrong, the program stops:
void main() {
var stock = {'pen': 10};
print(stock['pen']!);
print(stock['eraser']!); // ✗ no such key
}10
Unhandled exception:
Null check operator used on a null valuePrefer ??. Use ! only for keys you put in the map yourself a few lines earlier.
8. The Promotion Trap — Use a Local Variable
From Lecture 022 you might expect an if check to promote the value. It doesn't:
void main() {
var prices = {'apple': 120};
if (prices['apple'] != null) {
print(prices['apple'] + 10); // ✗
}
}Error: Operator '+' cannot be called on 'int?' because it is potentially null.
print(prices['apple'] + 10); // ✗
^prices['apple'] is not a variable — it is a fresh lookup every time you write it, and Dart
won't assume two lookups return the same thing. Promotion only works on local variables, so
read the value once into one:
void main() {
var prices = {'apple': 120};
final apple = prices['apple'];
if (apple != null) {
print(apple + 10);
}
}130apple is a local int?; the if promotes it to int inside the block. This is the same fix
as copying a top-level variable into a local one in Lecture 022.
9. Adding and Updating — Keys Are Unique
map[key] = value adds the entry if the key is new and replaces the value if the key
already exists. A map never holds the same key twice.
void main() {
var marks = {'Ali': 85};
marks['Sara'] = 92; // new key: added
marks['Ali'] = 90; // existing key: replaced
print(marks);
}{Ali: 90, Sara: 92}A duplicate key in a literal is the same thing: the last value wins, and dart analyze warns
Two keys in a map literal shouldn't be equal. Values, on the other hand, may repeat freely —
two students can both score 92.
10. Updating from the Old Value
Adding 5 to a stored value looks easy, but the old value is an int?:
void main() {
var stock = {'pen': 10};
stock['pen'] += 5; // ✗
}Error: Operator '+' cannot be called on 'int?' because it is potentially null.
stock['pen'] += 5; // ✗
^
Error: A value of type 'num' can't be assigned to a variable of type 'int'.
stock['pen'] += 5; // ✗
^The first error is the one that matters: the old value might be null.
Read with a fallback, then write back:
void main() {
var stock = {'pen': 10};
stock['pen'] = (stock['pen'] ?? 0) + 5;
stock['ink'] = (stock['ink'] ?? 0) + 3;
print(stock);
}{pen: 15, ink: 3}A missing key counts as 0, so the same line works for new and existing keys. Lecture 050
shows the update method, which does this more neatly.
11. remove — Delete by Key
remove(key) deletes the entry and returns its value — or null if the key wasn't there.
Its return type is V?.
void main() {
var stock = {'pen': 10, 'book': 4};
print(stock.remove('book'));
print(stock.remove('ruler'));
print(stock);
}4
null
{pen: 10}12. Asking Questions About a Map
containsKey and containsValue return a bool; length, isEmpty, and isNotEmpty work
just as they do for lists.
void main() {
var marks = {'Ali': 85, 'Sara': 92};
print(marks.containsKey('Sara'));
print(marks.containsValue(85));
print(marks.length);
print(marks.isNotEmpty);
}true
true
2
truecontainsKey is the honest way to ask "is this key here?". When values themselves may be
null, map[key] can't tell a missing key from a stored null:
void main() {
Map<String, String?> nicknames = {'Ali': null, 'Sara': 'Sasa'};
print(nicknames['Ali']);
print(nicknames['Zayan']);
print(nicknames.containsKey('Ali'));
print(nicknames.containsKey('Zayan'));
}null
null
true
false13. putIfAbsent — Add Only If Missing
putIfAbsent(key, function) adds the entry only when the key is new. The second argument is a
function (Lecture 034) that produces the value. Either way it returns the value now stored.
void main() {
var prices = {'apple': 120};
print(prices.putIfAbsent('apple', () => 999));
print(prices.putIfAbsent('kiwi', () => 50));
print(prices);
}120
50
{apple: 120, kiwi: 50}apple kept 120; only kiwi was added. Because the result is the stored value, its type is
int, not int?.
14. A Real-World Example
void main() {
var prices = {'milk': 180, 'bread': 120, 'eggs': 300};
var cart = ['milk', 'eggs', 'jam', 'milk'];
prices.putIfAbsent('tea', () => 250);
prices['bread'] = 140;
prices.remove('eggs');
var total = 0;
for (final item in cart) {
final price = prices[item];
if (price != null) {
total += price;
} else {
print('No price for $item');
}
}
print('Items in shop: ${prices.length}');
print('Total: $total');
}No price for eggs
No price for jam
Items in shop: 3
Total: 360The loop walks the list cart; each item is looked up in the map once, stored in a local,
and promoted by the if. Missing keys are reported instead of crashing.
Code Examples
Example 1 — Creating and Printing a Map
void main() {
var capitals = {'Pakistan': 'Islamabad', 'Japan': 'Tokyo'};
print(capitals);
print(capitals['Japan']);
}{Pakistan: Islamabad, Japan: Tokyo}
TokyoExample 2 — An int Key
void main() {
Map<int, String> rollNumbers = {1: 'Ali', 2: 'Sara'};
rollNumbers[3] = 'Zayan';
print(rollNumbers);
print(rollNumbers[3]);
}{1: Ali, 2: Sara, 3: Zayan}
ZayanExample 3 — A Default with ??
void main() {
var ages = {'Ali': 20, 'Sara': 18};
print(ages['Sara'] ?? 0);
print(ages['Hina'] ?? 0);
}18
0Example 4 — Adding and Replacing
void main() {
var colors = <String, String>{};
colors['sky'] = 'blue';
colors['grass'] = 'green';
colors['sky'] = 'grey';
print(colors);
print(colors.length);
}{sky: grey, grass: green}
2Example 5 — remove and containsKey
void main() {
var tasks = {'wash': true, 'cook': false};
print(tasks.containsKey('cook'));
tasks.remove('cook');
print(tasks.containsKey('cook'));
print(tasks);
}true
false
{wash: true}Example 6 — putIfAbsent
void main() {
var settings = {'theme': 'dark'};
settings.putIfAbsent('theme', () => 'light');
settings.putIfAbsent('language', () => 'English');
print(settings);
}{theme: dark, language: English}Example 7 — Student Marks
void main() {
var marks = {'Ali': 85, 'Sara': 92, 'Zayan': 47};
final mark = marks['Zayan'];
if (mark != null) {
print(mark >= 50 ? 'Zayan passed' : 'Zayan failed');
}
print('Hina: ${marks['Hina'] ?? 'no mark'}');
}Zayan failed
Hina: no markHow it works
markis a localint?, somark != nullpromotes it andmark >= 50compiles.marks['Hina']isnull, so??supplies the text'no mark'.
Example 8 — Shopping Total
void main() {
var prices = {'pen': 50, 'notebook': 200, 'bag': 1500};
var cart = ['notebook', 'pen', 'pen'];
var total = 0;
for (final item in cart) {
total += prices[item] ?? 0;
}
print('Total: $total');
}Total: 300How it works
- The loop walks the list; the map supplies each item's price.
?? 0turns eachint?into anint, so+=is allowed.
Example 9 — Bank Balance
void main() {
var accounts = {'Ali': 1000.0, 'Sara': 2500.0};
accounts['Sara'] = (accounts['Sara'] ?? 0) + 500;
accounts['Zayan'] = (accounts['Zayan'] ?? 0) + 150;
print(accounts);
}{Ali: 1000.0, Sara: 3000.0, Zayan: 150.0}How it works
- Sara's old balance
2500.0plus500is stored back under'Sara'. - Zayan had no account, so
?? 0starts him at0and the assignment adds his key.
Example 10 — Temperature Lookup
void main() {
Map<String, double> temps = {'Karachi': 34.0, 'Lahore': 41.5};
final celsius = temps['Lahore'];
if (celsius != null) {
final fahrenheit = celsius * 9 / 5 + 32;
print('Lahore: $fahrenheit°F');
}
print(temps.containsKey('Quetta'));
}Lahore: 106.7°F
falseHow it works
- The lookup goes into the local
celsius, which theifpromotes todouble. 41.5 * 9 / 5 + 32is106.7.
Common Beginner Mistakes
1. Treating map[key] as non-nullable
void main() {
var prices = {'apple': 120};
// int price = prices['apple']; // ✗
}Error: A value of type 'int?' can't be assigned to a variable of type 'int'.Write prices['apple'] ?? 0, or read it into a local and check it.
2. Expecting if (map[key] != null) to promote
void main() {
var prices = {'apple': 120};
if (prices['apple'] != null) {
// print(prices['apple'] + 10); // ✗
}
}Error: Operator '+' cannot be called on 'int?' because it is potentially null.Each prices['apple'] is a new lookup. Store it in a local variable first.
3. Writing map[key] += value
void main() {
var stock = {'pen': 10};
// stock['pen'] += 5; // ✗
}Error: Operator '+' cannot be called on 'int?' because it is potentially null.Use stock['pen'] = (stock['pen'] ?? 0) + 5;.
4. Expecting a repeated key to add a second entry
marks['Ali'] = 90; on a map that already has 'Ali' replaces his mark. Keys are unique;
choose a key that really identifies one thing.
5. Using ! on a key that might be missing
stock['eraser']! stops the program with Null check operator used on a null value. Use ??
unless you just added that key yourself.
6. Creating an empty map with plain {}
var stock = {}; is a Map<dynamic, dynamic> — no type checks at all. Write
<String, int>{}.
Best Practices
- Use a map when you look values up by a meaningful key, a list when order and position matter.
- Give empty maps both types:
<String, int>{}. - Read a value once into a local variable, then check it with
ifor??. - Prefer
??over!for lookups. - Use
containsKeyto test for a key, especially when values can benull. - Use
putIfAbsentto set a value only the first time.
Mini Challenge
Sara keeps a library map: {'Dart Basics': 3, 'Python 101': 1} (title → copies). Lend one copy
of 'Python 101', add 2 copies of a new book 'Algorithms', then print the map and whether
'Python 101' still has copies.
Your program should print exactly:
{Dart Basics: 3, Python 101: 0, Algorithms: 2}
Python 101 available: falseTry it yourself before opening the solution.
Show solution
void main() {
var library = {'Dart Basics': 3, 'Python 101': 1};
library['Python 101'] = (library['Python 101'] ?? 0) - 1;
library['Algorithms'] = (library['Algorithms'] ?? 0) + 2;
print(library);
final copies = library['Python 101'] ?? 0;
print('Python 101 available: ${copies > 0}');
}{Dart Basics: 3, Python 101: 0, Algorithms: 2}
Python 101 available: falseHow it works
- Each update reads with
?? 0, changes the number, and writes it back under the same key. 'Algorithms'was missing, so the assignment adds it at the end.copiesis a non-nullable local, socopies > 0compiles.
Practice Questions
Work through these in order — they run from beginner to application to challenge. Every question gives you exact values, so you can compare your output with the answers.
Beginner
- Create a map of fruit prices
apple → 120,mango → 200and print it. - Create an empty
Map<String, int>calledscores, addAli → 40, and print itslength. - From
{'Ali': 20, 'Sara': 18}, print Sara's age and then the value for'Hina'.
Application
-
From
{'pen': 10, 'book': 4}, remove'book'and print the valueremovereturned. -
Given
{'Ali': 85}, set Ali's mark to95and addZayan → 70. Print the map. -
Given
{'milk': 180}, printPrice: 0for'jam'using??. -
This program does not compile. Fix it without using
!, so it prints130:void main() { var prices = {'apple': 120}; if (prices['apple'] != null) { print(prices['apple'] + 10); } } -
Given
{'theme': 'dark'}, useputIfAbsentfor'theme' → 'light'and'font' → 'Inter', then print the map.
Challenge
- Count the votes in the list
['Ali', 'Sara', 'Ali', 'Zayan', 'Ali']into aMap<String, int>using afor-inloop over the list. Print the map. - Accounts are
{'Ali': 500.0, 'Sara': 1200.0}. Withdraw700.0from each name in['Ali', 'Sara', 'Hina']: if the account is missing printHina: no account, if the balance is too low printAli: insufficient funds, otherwise subtract it. Finally print the map.
Practice Answers
Question 1 — Solution
void main() {
var prices = {'apple': 120, 'mango': 200};
print(prices);
}{apple: 120, mango: 200}Question 2 — Solution
void main() {
var scores = <String, int>{};
scores['Ali'] = 40;
print(scores.length);
}1Question 3 — Solution
void main() {
var ages = {'Ali': 20, 'Sara': 18};
print(ages['Sara']);
print(ages['Hina']);
}18
nullQuestion 4 — Solution
void main() {
var stock = {'pen': 10, 'book': 4};
print(stock.remove('book'));
}4Question 5 — Solution
void main() {
var marks = {'Ali': 85};
marks['Ali'] = 95;
marks['Zayan'] = 70;
print(marks);
}{Ali: 95, Zayan: 70}Question 6 — Solution
void main() {
var prices = {'milk': 180};
print('Price: ${prices['jam'] ?? 0}');
}Price: 0Question 7 — Solution
Read the value once into a local variable, which if can promote:
void main() {
var prices = {'apple': 120};
final apple = prices['apple'];
if (apple != null) {
print(apple + 10);
}
}130Question 8 — Solution
void main() {
var settings = {'theme': 'dark'};
settings.putIfAbsent('theme', () => 'light');
settings.putIfAbsent('font', () => 'Inter');
print(settings);
}{theme: dark, font: Inter}Question 9 — Solution
void main() {
var votes = ['Ali', 'Sara', 'Ali', 'Zayan', 'Ali'];
var counts = <String, int>{};
for (final name in votes) {
counts[name] = (counts[name] ?? 0) + 1;
}
print(counts);
}{Ali: 3, Sara: 1, Zayan: 1}A name's first vote finds no key, so ?? 0 starts it at 0.
Question 10 — Solution
void main() {
var accounts = {'Ali': 500.0, 'Sara': 1200.0};
for (final name in ['Ali', 'Sara', 'Hina']) {
final balance = accounts[name];
if (balance == null) {
print('$name: no account');
} else if (balance < 700.0) {
print('$name: insufficient funds');
} else {
accounts[name] = balance - 700.0;
}
}
print(accounts);
}Ali: insufficient funds
Hina: no account
{Ali: 500.0, Sara: 500.0}After balance == null fails, Dart promotes the local balance to double in the other
branches.
Quick Revision
- What are the two parts of every map entry?
- What do
KandVmean inMap<K, V>? - What does
map[key]return for a key that isn't there? - Why is
prices['apple']anint?even when'apple'exists? - Why doesn't
if (prices['apple'] != null)promoteprices['apple']? - What happens when you assign to a key that already exists?
- What does
removereturn? - When is
containsKeybetter than checkingmap[key] != null?
Key Takeaways
- A map stores entries: each key leads to one value.
- Literals use
{key: value}; the type isMap<K, V>, and both sides are checked. - Plain
{}is aMap<dynamic, dynamic>— give empty maps their types. map[key]returnsV?: a missing key givesnull, never a crash.- Handle it with
??, with!only when certain, or by reading it into a local and checking. map[key]is never promoted — each use is a new lookup.map[key] = valueadds a new key or replaces an existing one; keys are unique.remove(key)returns the removed value ornull.containsKey,containsValue,length,isEmpty, andisNotEmptyanswer questions.putIfAbsentadds a value only when the key is missing.
What's Next?
Lecture 50 — Map Iteration & Methods. So far we looked up one key at a time. Next we will
walk through a whole map — its keys, its values, and its entries — and use methods like
forEach, update, and map to process every entry at once.