A collection is a structure that allows us to group several pieces of data under the same variable.
In the real world, data rarely comes alone. Users come in lists, configurations in dictionaries, and categories in groups. To handle this, Dart offers us Collections.
If you come from JavaScript, this will feel familiar, but watch out: in Dart, collections are typed. That is, we don’t mix apples and oranges (unless we explicitly want to).
Today we’ll look at the three main types: List, Set, and Map, and how to operate them elegantly.
Lists (List)
A list is an ordered collection of elements. It’s the equivalent of Arrays in other languages. They are denoted with square brackets [].
Declaration and typing
We can let Dart infer the type or specify it ourselves using “Generics” (the < > symbols).
// Inference: Dart knows it's List<int>
var numbers = [1, 2, 3, 4, 5];
// Explicit typing: We only allow Strings
List<String> names = ["Ana", "Luis", "Carlos"];
// numbers.add("Text"); // ❌ Error: You can't put text in a list of integersAccess and modification
Since they are ordered, we access their elements by index (starting at 0).
var fruits = ["Apple", "Pear"];
print(fruits[0]); // Apple
fruits.add("Banana"); // Adds to the end
print(fruits.length); // 3In Flutter you’ll use List constantly. For example, a Column or a ListView receives a List<Widget> as children.
Sets (Set)
A Set is a collection of unique elements with no index-based access. It is denoted with curly braces { }. The default Set preserves insertion order when iterated, although it shouldn’t be used as a substitute for a list.
The main difference from a list is that it does not allow duplicates. If you try to add something that already exists, it simply ignores it.
Set<String> countries = {"Spain", "France", "Italy"};
countries.add("Spain"); // Does nothing, it already exists.
print(countries); // {Spain, France, Italy}They are very useful for filtering duplicates from a list or for quick existence checks.
Maps (Map)
A Map is a dictionary. It relates a Key to a Value. It’s the structure we use to represent JSON objects.
It is also defined with curly braces {}, but we differentiate it as a map because it has the key: value structure.
// Map<String, dynamic>
// String for the key, dynamic for the value (because it can be text, number, etc.)
Map<String, dynamic> user = {
"name": "Luis",
"age": 30,
"isAdmin": true
};Accessing data
We access it using the key inside square brackets, similar to a list, but using the key name.
print(user["name"]); // Luis
// Assign new value
user["age"] = 31;The Map<String, dynamic> type appears very frequently in Flutter to process data coming from a REST API or Firebase.
Spread operator ...
A small gem of modern Dart that you’ll use a lot in user interfaces is the Spread Operator (three dots ...). It’s used to “spread” the elements of one list into another.
var firstPhase = ["Sketch", "Design"];
var secondPhase = ["Development", "Test"];
var completeProject = [
...firstPhase, // Inserts the elements here
...secondPhase,
"Deployment"
];
// Result: [Sketch, Design, Development, Test, Deployment]Functional methods: .map() and .where()
This is where we go from “knowing how to program” to “programming like a pro”.
Instead of using for loops to transform lists, in Dart we use functional methods. This is critical in Flutter, where you often want to transform a List of Data into a List of Widgets.
.map() for transformation
It traverses the list, executes a function for each element, and returns a new collection with the transformed results.
var numbers = [1, 2, 3, 4, 5];
// We want the double of each number
var doubles = numbers.map((n) {
return n * 2;
}).toList(); // ⚠️ Important: .map returns an Iterable, we need to convert it to a List
print(doubles); // [2, 4, 6, 8, 10]Or using arrow functions (cleaner):
var doubles = numbers.map((n) => n * 2).toList();.where() for filtering
Returns a new collection only with the elements that meet a condition (return true).
var numbers = [1, 2, 3, 4, 5, 6];
// Only the even ones
var evens = numbers.where((n) => n % 2 == 0).toList();
print(evens); // [2, 4, 6]Iterables and lists
A technical detail that often confuses people: methods like map or where do not return a List, but an Iterable.
An Iterable is a collection that is evaluated lazily. That’s why we almost always chain a .toList() at the end of these operations to materialize the data and be able to use it in our UI.