Skip to content

prefer_for_loop_in_children

v0.4.0 Warning Fix Code Organization

Flags four functional list-building shapes that collection-for expresses directly: .map().toList(), a spread of .map(), List.generate(), and a .fold() that starts from an empty list.

Collection-for allocates no intermediate iterable, sits inline in the children: list alongside collection-if, and reads as one list rather than as a chain that must be unwound to see what it produces.

A quick fix rewrites each shape.

This rule is in the opinionated preset, so it is on with preset: opinionated or preset: pedantic.

See also: Flutter - Flex.children | Dart collection elements

Widget build(BuildContext context) {
return Column(
children: items.map((item) => Text(item)).toList(),
);
}
Widget build(BuildContext context) {
return Column(
children: [for (final item in items) Text(item)],
);
}

The shape that appears once a list has a fixed header or footer around the generated part:

// Don't
Column(
children: [
const Text('Header'),
...items.map((item) => Text(item)),
],
);
// Do
Column(
children: [
const Text('Header'),
for (final item in items) Text(item),
],
);

The trailing .toList() makes no difference — ...items.map(...).toList() is reported too.

Reported with or without explicit type arguments:

// Don't
Column(
children: List.generate(5, (index) => Text('Item $index')),
);
// Don't — the same with type arguments
Column(
children: List<Widget>.generate(5, (index) => Text('Item $index')),
);
// Do
Column(
children: [for (var i = 0; i < 5; i++) Text('Item $i')],
);

A .fold() that accumulates into an empty list

Section titled “A .fold() that accumulates into an empty list”

Only a fold whose seed is an empty list literal is reported — that is the one collection-for replaces exactly:

// Don't
final tiles = items.fold<List<Widget>>([], (acc, item) {
acc.add(Text(item));
return acc;
});
// Do
final tiles = [for (final item in items) Text(item)];

A fold with any other seed is a real reduction and is left alone:

// Accepted — not a list build
final total = prices.fold<int>(0, (acc, price) => acc + price);

.map() without .toList() is not reported. A bare .map() returns a lazy iterable, which is a different value from a list — rewriting it would change the type.

.map().toSet() is not reported, for the same reason: the result is a Set.

A .map() taking a named function is not reported. items.map(buildTile).toList() has no closure body to inline into the loop.

generate on anything but List is not reported. The receiver is checked, so a custom class with its own generate is left alone.

The rule fires anywhere, not only inside children:. Despite the name, any of these four shapes in ordinary code is reported — the children: list is simply where it matters most.

To turn it off:

many_lints.yaml
rules:
prefer_for_loop_in_children: false

To keep the rule on but skip certain paths, use per-rule exclude.