In Dart, how does a collection for element compare with map().toList() for building lists and maps, including filtering with a nested if?
answer
- a loop that yields elements
- for-in or C-style header
- nest if to filter
- {for (u in users) u.id: u}
- no toList inside a spread
basics
~20 sA collection for element loops inside the literal and inserts one element per iteration; a nested if filters, fors nest, and fixed elements can surround it, replacing where().map().toList() chains and Map.fromIterable with one eager literal.
solid answer
~40 s`[for (final item in items) Tile(item)]` evaluates the loop while the literal is built and inserts the body's element on each pass. The header is either `for (x in iterable)` or C-style `for (var i = 0; i < n; i++)`. The body is an element, not a statement, so it can be another `for`, an `if` that filters, or a spread, but not `break` or a local declaration. Compared with `items.where(...).map(...).toList()` it reads top-down, needs no closures, and mixes naturally with fixed elements such as a header. For maps, `{for (final u in users) u.id: u}` replaces `Map.fromIterable`, which the recommended `prefer_for_elements_to_map_fromIterable` lint flags. The result is built eagerly, in full, every time the literal runs.
code
dart · 16 linesclass User {
User(this.id, this.name, {this.isAdmin = false});
final String id;
final String name;
final bool isAdmin;
}
void main() {
final users = [User('u1', 'Ana', isAdmin: true), User('u2', 'Ben')];
final byId = {for (final u in users) u.id: u};
final adminNames = [for (final u in users) if (u.isAdmin) u.name];
print(byId.keys); // (u1, u2)
print(adminNames); // [Ana]
}go deeper
Know the shape [for (final x in xs) f(x)] and that an if inside it filters without leaving gaps.
Explain that the body is an element rather than a statement, show the map form {for (u in users) u.id: u}, and compare it with method chains.
Enforce the recommended lints in review, and recognise when a for element inside build should become ListView.builder because the list is large.
Set conventions for when a data-driven literal is fine and when list construction belongs in a view model or a lazy builder, balancing readability against rebuild cost.
## What a collection for element is A **collection for element** is a loop placed inside a list, set or map literal. Each time the loop body runs, it contributes zero or more values to the collection being built. It arrived with collection `if` and spread in **Dart 2.3**. Two header forms are allowed, mirroring the `for` statement: - `for (var x in iterable) element` iterates an `Iterable`; - `for (var i = 0; i < n; i++) element` is the C-style form. For example, `[1, for (var x = 5; x > 2; x--) x, 7]` evaluates to `[1, 5, 4, 3, 7]`. ## The body is an element, not a statement This is the rule that shapes everything else. The body is **one element**, which may be: 1. an expression (`Text(item.label)`); 2. a map entry (`u.id: u`) inside a map literal; 3. an `if` element, which **filters**; 4. another `for` element, which **nests**; 5. a spread, which inserts a group per iteration. Because it is not a statement block, you cannot write `break`, `continue`, `return` or a local variable declaration in it. When the logic needs those, build the list in a helper function or use a `sync*` generator instead (lazy generators belong to the iterables topic). ## Filtering and nesting ```dart var numbers = [1, 2, 3, 4, 5, 6, 7]; var evens = [0, for (var n in numbers) if (n.isEven) n, 8]; // [0, 2, 4, 6, 8] ``` The `if` is evaluated for each iteration, so non-matching values are simply skipped; nothing is inserted in their place. Loops nest the same way list comprehensions do in other languages: ```dart var ys = [1, 2, 3, 4]; var pairs = [ for (var x = 0; x < 3; x++) for (var y in ys) if (x < y) x + y * 10, ]; // [10, 20, 30, 40, 21, 31, 41, 32, 42] ``` ## Compared with iterable method chains | Aspect | `for` element | `where().map().toList()` | |---|---|---| | Reads | top-down, like a loop | right-to-left through closures | | Mixing fixed elements | natural: `[header, for (...) tile, footer]` | needs a spread of the chain | | Maps | `{for (u in users) u.id: u}` | `Map.fromIterable` or `Map.fromEntries` | | Evaluation | eager, once per literal evaluation | lazy until `toList()` | Neither is wrong; Effective Dart shows the `for` element as the preferred way to combine fixed and computed items. Two lints in the `recommended` set enforce the common cases: - `prefer_for_elements_to_map_fromIterable` flags `Map.fromIterable` where a `for` element would do; - `unnecessary_to_list_in_spreads` flags `...items.map(f).toList()`, because the spread already iterates the `Iterable` and the `toList()` builds a throwaway list. ## Evaluation order and cost A `for` element runs **when the literal is evaluated**, completely, before the collection exists: - iterations run in source order, and values are appended in that order; - the iterated source is read once per evaluation of the literal; - the finished list is independent of the source, so later changes to `numbers` do not affect `evens`. This is the opposite of a `where(...).map(...)` chain, which returns a lazy `Iterable` that recomputes each time it is iterated until you call `toList()`. Inside a Flutter `build` method the literal is rebuilt on every build, which is fine for a menu of ten entries and wasteful for thousands of rows. ## In a Flutter menu A menu model filtered by role becomes one literal: ```dart ListView( children: [ const DrawerHeader(child: Text('Menu')), for (final entry in entries) if (!entry.adminOnly || isAdmin) ListTile(title: Text(entry.label), onTap: entry.onTap), ], ) ``` The same loop over a large or unbounded data set is a different problem: a `for` element creates **every** widget each build, whereas `ListView.builder` creates only the visible ones. Use the literal for short, bounded lists such as menus and form sections. ## Mistakes to avoid - Expecting a skipped iteration to leave a `null` in the list. - Trying to `break` out of a `for` element. - Writing map entries inside a list literal: `[for (u in users) u.id: u]` does not compile; the braces make it a map. - Keeping `.toList()` inside a spread.
- Can a Dart collection for element produce more than one value per iteration?Yes: make its body a spread, for example `for (final section in sections) ...[Header(section), ...section.tiles]`. Each iteration then inserts the header followed by that section's tiles, all into the same flat list.
- Why can a Dart for element not use break, and what do you do when you need it?Its body is an element, not a statement block, so statements like `break`, `continue` or variable declarations are not allowed. Move the logic into a helper that builds and returns the list, or iterate a bounded source such as `items.take(n)`.
saying these in an interview costs you the question
- A for element whose if filter fails inserts null for that iteration
- You can break out of a collection for element early like a normal loop
- Map entries can be produced by a for element inside a list literal
- A for element is lazy and only runs when the list is first read
- Spreading map() requires toList() first or it will not compile