Watch the video (12 min)
On this page26 sections
  1. Prerequisites
  2. Connection With Previous Lectures
  3. Learning Objectives
  4. 1. What Is a Map?
  5. 2. Writing a Map Literal
  6. 3. The Type Map<K, V>
  7. 4. Empty Maps Need a Type
  8. 5. Reading a Value with map[key]
  9. 6. map[key] Returns V?
  10. 7. Handling the null: ?? and !
  11. 8. The Promotion Trap — Use a Local Variable
  12. 9. Adding and Updating — Keys Are Unique
  13. 10. Updating from the Old Value
  14. 11. remove — Delete by Key
  15. 12. Asking Questions About a Map
  16. 13. putIfAbsent — Add Only If Missing
  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 49 — Maps

Category8 — Sets & Maps
Lecture049 of 140
Estimated video length12 min
TargetDart 3.12.2
LevelBeginner

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)
  • if statements and for-in loops (Lectures 022 and 027)
  • anonymous functions (Lecture 034)
  • lists, List<int>, indexes, length, isEmpty, and remove (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:

dart
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, and isEmpty
  • 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".

dart
void main() {
  var marks = {'Ali': 85, 'Sara': 92, 'Zayan': 78};
  print(marks);
}
output
{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.

dart
void main() {
  var capitals = {'Pakistan': 'Islamabad', 'Japan': 'Tokyo', 'Egypt': 'Cairo'};
  print(capitals);
}
output
{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:

dart
void main() {
  Map<int, String> rollNumbers = {1: 'Ali', 2: 'Sara', 3: 'Zayan'};
  print(rollNumbers[2]);
}
output
Sara

Both types are checked. A String value or an int key in a Map<String, int> is rejected:

dart
void main() {
  var prices = {'apple': 120};
  prices['kiwi'] = '50'; // ✗ value must be int
  prices[5] = 9; // ✗ key must be String
}
output
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:

dart
void main() {
  var anything = {};
  anything['a'] = 1;
  anything[2] = 'x';
  print(anything);
}
output
{a: 1, 2: x}

Give an empty map its types, exactly as with <int>[] in Lecture 039:

dart
void main() {
  var stock = <String, int>{};
  Map<String, double> temps = {};
  print(stock.isEmpty);
  print(temps.length);
}
output
true
0

5. 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:

dart
void main() {
  var prices = {'apple': 120, 'mango': 200};
  print(prices['mango']);
  print(prices['kiwi']);
}
output
200
null

Compare 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:

dart
void main() {
  var prices = {'apple': 120};
  int price = prices['apple']; // ✗
  print(prices['apple'] + 1); // ✗
}
output
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:

dart
void main() {
  var prices = {'apple': 120, 'mango': 200};
  int apple = prices['apple'] ?? 0;
  int kiwi = prices['kiwi'] ?? 0;
  print('$apple $kiwi');
}
output
120 0

! (Lecture 015) says "I promise the key is there". If you are wrong, the program stops:

dart
void main() {
  var stock = {'pen': 10};
  print(stock['pen']!);
  print(stock['eraser']!); // ✗ no such key
}
output
10
Unhandled exception:
Null check operator used on a null value

Prefer ??. 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:

dart
void main() {
  var prices = {'apple': 120};
  if (prices['apple'] != null) {
    print(prices['apple'] + 10); // ✗
  }
}
output
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:

dart
void main() {
  var prices = {'apple': 120};
  final apple = prices['apple'];
  if (apple != null) {
    print(apple + 10);
  }
}
output
130

apple 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.

dart
void main() {
  var marks = {'Ali': 85};
  marks['Sara'] = 92; // new key: added
  marks['Ali'] = 90; // existing key: replaced
  print(marks);
}
output
{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?:

dart
void main() {
  var stock = {'pen': 10};
  stock['pen'] += 5; // ✗
}
output
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:

dart
void main() {
  var stock = {'pen': 10};
  stock['pen'] = (stock['pen'] ?? 0) + 5;
  stock['ink'] = (stock['ink'] ?? 0) + 3;
  print(stock);
}
output
{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?.

dart
void main() {
  var stock = {'pen': 10, 'book': 4};
  print(stock.remove('book'));
  print(stock.remove('ruler'));
  print(stock);
}
output
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.

dart
void main() {
  var marks = {'Ali': 85, 'Sara': 92};
  print(marks.containsKey('Sara'));
  print(marks.containsValue(85));
  print(marks.length);
  print(marks.isNotEmpty);
}
output
true
true
2
true

containsKey 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:

dart
void main() {
  Map<String, String?> nicknames = {'Ali': null, 'Sara': 'Sasa'};
  print(nicknames['Ali']);
  print(nicknames['Zayan']);
  print(nicknames.containsKey('Ali'));
  print(nicknames.containsKey('Zayan'));
}
output
null
null
true
false

13. 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.

dart
void main() {
  var prices = {'apple': 120};
  print(prices.putIfAbsent('apple', () => 999));
  print(prices.putIfAbsent('kiwi', () => 50));
  print(prices);
}
output
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

dart
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');
}
output
No price for eggs
No price for jam
Items in shop: 3
Total: 360

The 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

dart
void main() {
  var capitals = {'Pakistan': 'Islamabad', 'Japan': 'Tokyo'};
  print(capitals);
  print(capitals['Japan']);
}
output
{Pakistan: Islamabad, Japan: Tokyo}
Tokyo

Example 2 — An int Key

dart
void main() {
  Map<int, String> rollNumbers = {1: 'Ali', 2: 'Sara'};
  rollNumbers[3] = 'Zayan';
  print(rollNumbers);
  print(rollNumbers[3]);
}
output
{1: Ali, 2: Sara, 3: Zayan}
Zayan

Example 3 — A Default with ??

dart
void main() {
  var ages = {'Ali': 20, 'Sara': 18};
  print(ages['Sara'] ?? 0);
  print(ages['Hina'] ?? 0);
}
output
18
0

Example 4 — Adding and Replacing

dart
void main() {
  var colors = <String, String>{};
  colors['sky'] = 'blue';
  colors['grass'] = 'green';
  colors['sky'] = 'grey';
  print(colors);
  print(colors.length);
}
output
{sky: grey, grass: green}
2

Example 5 — remove and containsKey

dart
void main() {
  var tasks = {'wash': true, 'cook': false};
  print(tasks.containsKey('cook'));
  tasks.remove('cook');
  print(tasks.containsKey('cook'));
  print(tasks);
}
output
true
false
{wash: true}

Example 6 — putIfAbsent

dart
void main() {
  var settings = {'theme': 'dark'};
  settings.putIfAbsent('theme', () => 'light');
  settings.putIfAbsent('language', () => 'English');
  print(settings);
}
output
{theme: dark, language: English}

Example 7 — Student Marks

dart
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'}');
}
output
Zayan failed
Hina: no mark

How it works

  1. mark is a local int?, so mark != null promotes it and mark >= 50 compiles.
  2. marks['Hina'] is null, so ?? supplies the text 'no mark'.

Example 8 — Shopping Total

dart
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');
}
output
Total: 300

How it works

  1. The loop walks the list; the map supplies each item's price.
  2. ?? 0 turns each int? into an int, so += is allowed.

Example 9 — Bank Balance

dart
void main() {
  var accounts = {'Ali': 1000.0, 'Sara': 2500.0};
  accounts['Sara'] = (accounts['Sara'] ?? 0) + 500;
  accounts['Zayan'] = (accounts['Zayan'] ?? 0) + 150;
  print(accounts);
}
output
{Ali: 1000.0, Sara: 3000.0, Zayan: 150.0}

How it works

  1. Sara's old balance 2500.0 plus 500 is stored back under 'Sara'.
  2. Zayan had no account, so ?? 0 starts him at 0 and the assignment adds his key.

Example 10 — Temperature Lookup

dart
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'));
}
output
Lahore: 106.7°F
false

How it works

  1. The lookup goes into the local celsius, which the if promotes to double.
  2. 41.5 * 9 / 5 + 32 is 106.7.

Common Beginner Mistakes

1. Treating map[key] as non-nullable

dart
void main() {
  var prices = {'apple': 120};
  // int price = prices['apple']; // ✗
}
output
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

dart
void main() {
  var prices = {'apple': 120};
  if (prices['apple'] != null) {
    // print(prices['apple'] + 10); // ✗
  }
}
output
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

dart
void main() {
  var stock = {'pen': 10};
  // stock['pen'] += 5; // ✗
}
output
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 if or ??.
  • Prefer ?? over ! for lookups.
  • Use containsKey to test for a key, especially when values can be null.
  • Use putIfAbsent to 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:

output
{Dart Basics: 3, Python 101: 0, Algorithms: 2}
Python 101 available: false

Try it yourself before opening the solution.

Show solution
dart
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}');
}
output
{Dart Basics: 3, Python 101: 0, Algorithms: 2}
Python 101 available: false

How it works

  1. Each update reads with ?? 0, changes the number, and writes it back under the same key.
  2. 'Algorithms' was missing, so the assignment adds it at the end.
  3. copies is a non-nullable local, so copies > 0 compiles.

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

  1. Create a map of fruit prices apple → 120, mango → 200 and print it.
  2. Create an empty Map<String, int> called scores, add Ali → 40, and print its length.
  3. From {'Ali': 20, 'Sara': 18}, print Sara's age and then the value for 'Hina'.

Application

  1. From {'pen': 10, 'book': 4}, remove 'book' and print the value remove returned.

  2. Given {'Ali': 85}, set Ali's mark to 95 and add Zayan → 70. Print the map.

  3. Given {'milk': 180}, print Price: 0 for 'jam' using ??.

  4. This program does not compile. Fix it without using !, so it prints 130:

    dart
    void main() {
      var prices = {'apple': 120};
      if (prices['apple'] != null) {
        print(prices['apple'] + 10);
      }
    }
  5. Given {'theme': 'dark'}, use putIfAbsent for 'theme' → 'light' and 'font' → 'Inter', then print the map.

Challenge

  1. Count the votes in the list ['Ali', 'Sara', 'Ali', 'Zayan', 'Ali'] into a Map<String, int> using a for-in loop over the list. Print the map.
  2. Accounts are {'Ali': 500.0, 'Sara': 1200.0}. Withdraw 700.0 from each name in ['Ali', 'Sara', 'Hina']: if the account is missing print Hina: no account, if the balance is too low print Ali: insufficient funds, otherwise subtract it. Finally print the map.

Practice Answers

Question 1 — Solution

dart
void main() {
  var prices = {'apple': 120, 'mango': 200};
  print(prices);
}
output
{apple: 120, mango: 200}

Question 2 — Solution

dart
void main() {
  var scores = <String, int>{};
  scores['Ali'] = 40;
  print(scores.length);
}
output
1

Question 3 — Solution

dart
void main() {
  var ages = {'Ali': 20, 'Sara': 18};
  print(ages['Sara']);
  print(ages['Hina']);
}
output
18
null

Question 4 — Solution

dart
void main() {
  var stock = {'pen': 10, 'book': 4};
  print(stock.remove('book'));
}
output
4

Question 5 — Solution

dart
void main() {
  var marks = {'Ali': 85};
  marks['Ali'] = 95;
  marks['Zayan'] = 70;
  print(marks);
}
output
{Ali: 95, Zayan: 70}

Question 6 — Solution

dart
void main() {
  var prices = {'milk': 180};
  print('Price: ${prices['jam'] ?? 0}');
}
output
Price: 0

Question 7 — Solution

Read the value once into a local variable, which if can promote:

dart
void main() {
  var prices = {'apple': 120};
  final apple = prices['apple'];
  if (apple != null) {
    print(apple + 10);
  }
}
output
130

Question 8 — Solution

dart
void main() {
  var settings = {'theme': 'dark'};
  settings.putIfAbsent('theme', () => 'light');
  settings.putIfAbsent('font', () => 'Inter');
  print(settings);
}
output
{theme: dark, font: Inter}

Question 9 — Solution

dart
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);
}
output
{Ali: 3, Sara: 1, Zayan: 1}

A name's first vote finds no key, so ?? 0 starts it at 0.

Question 10 — Solution

dart
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);
}
output
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

  1. What are the two parts of every map entry?
  2. What do K and V mean in Map<K, V>?
  3. What does map[key] return for a key that isn't there?
  4. Why is prices['apple'] an int? even when 'apple' exists?
  5. Why doesn't if (prices['apple'] != null) promote prices['apple']?
  6. What happens when you assign to a key that already exists?
  7. What does remove return?
  8. When is containsKey better than checking map[key] != null?

Key Takeaways

  • A map stores entries: each key leads to one value.
  • Literals use {key: value}; the type is Map<K, V>, and both sides are checked.
  • Plain {} is a Map<dynamic, dynamic> — give empty maps their types.
  • map[key] returns V?: a missing key gives null, 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] = value adds a new key or replaces an existing one; keys are unique.
  • remove(key) returns the removed value or null.
  • containsKey, containsValue, length, isEmpty, and isNotEmpty answer questions.
  • putIfAbsent adds 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.


Video lecture

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