You already know how to make a list in Dart, where every item sits in a numbered slot. Maps are the tool you reach for when a number is the wrong way to find something. You do not want the score in slot 2, you want Ana’s score. You do not want the third setting, you want the one called “darkMode.” A list answers “what is at position 5,” but real apps mostly ask “what is the value for this name,” and that is exactly what Dart maps are built for. A map stores key-value pairs, so you look things up by a meaningful key instead of counting positions. By the end of this post you will know how to build a map, pull values out of it safely, add and update entries, and loop through the whole thing, which covers almost everything you will actually do with a Map in a real Flutter app.
Making a map and looking inside it
A map literal uses curly braces, and each entry is a key, a colon, then a value. Picture a few students and their quiz scores. The key is the name, the value is the score.
Map<String, int> scores = {
'Ana': 92,
'Ben': 67,
'Cora': 78,
};
print(scores); // {Ana: 92, Ben: 67, Cora: 78}
That Map<String, int> part reads as “a map with String keys and int values.” Dart wants both types so it can catch your mistakes early, and being specific here saves you real headaches later. Looking a value up is the whole point, and it looks like indexing a list, except you pass the key instead of a number.
print(scores['Ana']); // 92
print(scores['Dan']); // null
Ana is in the map, so you get 92 back. Dan is not, and this is the part that trips people up: a missing key does not crash, it quietly hands you null. That is the single most common surprise I see first-semester students hit with maps. They assume a missing key throws an error like a bad list index would, but a Map just shrugs and returns null. Because of that, a lookup always gives you back a nullable type, and if null safety is still new to you, the Dart null safety guide is worth a read alongside this one.
You have two clean ways to handle the missing case. Use the ?? operator to supply a fallback, or ask containsKey before you reach in.
int danScore = scores['Dan'] ?? 0;
print(danScore); // 0
print(scores.containsKey('Ana')); // true
The ?? reads as “use the value on the left, but if it is null, use 0 instead.” That one little operator turns a possible null into a safe default, and you will sprinkle it all over your map code. Reach for containsKey when you genuinely need to know whether a key exists, rather than just wanting a stand-in value.

Adding, changing, and counting with a map
Putting something into a map uses that same square-bracket syntax, just on the left of an equals sign. If the key is new, Dart adds a pair. If the key already exists, Dart replaces its value. Same line of code, two outcomes, depending on what is already there.
scores['Dan'] = 85; // Dan is new, so this adds a pair
scores['Ben'] = 70; // Ben exists, so this changes his value
print(scores); // {Ana: 92, Ben: 70, Cora: 78, Dan: 85}
Notice Ben stays where he was in the order even though his value changed, while Dan lands at the end as a brand new entry. A Dart map remembers the order you inserted keys, which is handy when you loop later. Now for the pattern that makes maps genuinely powerful: counting things. Say you have a pile of votes and you want a tally of each answer. The update method is built for this. It takes a key, a function describing how to change the existing value, and an ifAbsent function for the first time it meets a key.
List<String> votes = ['yes', 'no', 'yes', 'yes', 'no'];
Map<String, int> tally = {};
for (String vote in votes) {
tally.update(vote, (count) => count + 1, ifAbsent: () => 1);
}
print(tally); // {yes: 3, no: 2}
Walk through it once and it clicks. The first time “yes” shows up it is not in the map, so ifAbsent runs and sets it to 1. Every time after that, the first function runs and adds one. The same thing happens for “no.” You end up with a clean count without writing a single if-statement to check whether the key already existed. This counting trick shows up constantly, from tallying votes to counting word frequencies to grouping items by category, and once you have written it once you will use it for the rest of your coding life.
Looping through a Dart map
A list has one thing per slot, but a map has two, a key and a value, so looping looks a little different. The friendliest option is forEach, which hands you both at once.
scores.forEach((name, score) {
print('$name scored $score');
});
// Ana scored 92
// Ben scored 70
// Cora scored 78
// Dan scored 85
That reads almost like a sentence, and the order matches how you inserted the keys. When you would rather use a regular for loop, reach for .entries, which gives you each pair as a MapEntry with a .key and a .value.
for (final entry in scores.entries) {
print('${entry.key}: ${entry.value}');
}
Both styles do the same job, so pick whichever reads more clearly to you in the moment. And when you only care about one half of the map, .keys and .values hand you just that part, ready to turn into a list.
print(scores.keys.toList()); // [Ana, Ben, Cora, Dan]
print(scores.values.toList()); // [92, 70, 78, 85]
If you have read the Dart list methods post, that .toList() on the end should look familiar. The keys and values come back as a lazy Iterable, and .toList() turns them into a real list you can hold onto and pass around.

Where Dart maps meet Flutter
Here is where this pays off on a real screen. In Flutter you constantly turn data into widgets, and a lot of your data arrives as a map: a user profile, a set of settings, a row of stats. When you want to show every pair on screen, you combine .entries with map from the list methods family and hand the result to a Column or a ListView.
Column(
children: scores.entries
.map((entry) => Text('${entry.key}: ${entry.value}'))
.toList(),
)
Each key-value pair becomes a Text widget, and .toList() gives Column the real list of widgets it expects. Leave that .toList() off and Flutter will refuse to build, because an Iterable is not a List<Widget>. Swap Column for a scrolling list and you have a screen that grows with your data, which is the whole idea behind the Flutter ListView guide. The pattern is always the same: store your data in a map keyed by something meaningful, then turn its entries into widgets when it is time to draw.
Your next step
That is the core of it. Build a map with curly braces and key-value pairs, look things up by key while remembering a missing key returns null, use ?? or containsKey to stay safe, add and change entries with square brackets, count things with update, and loop with forEach or .entries. Those few moves cover almost every map you will ever write. The best way to lock it in is to open your editor and change the examples. Add a fifth student to the scores map, then print only the names with .keys. Build a tally of letters in your own name. Turn the scores map into a column of Text widgets and run it on a real screen. When you want the full catalog of what a map can do, the official Dart Map class documentation lists every method, and the Dart collections guide is a friendly read once these basics feel comfortable. Paste that scores map into your editor and run each snippet once. Seeing the output yourself is what turns these from words on a page into a tool you actually reach for.

