avoid_accessing_collections_by_constant_index
v0.3.0 Warning Collection & Type
Flags a collection read with a constant index — list[0], or list[kFirst] where kFirst is a const — inside a loop body. The index never changes with the iteration, so either the read belongs outside the loop, or it was meant to use the loop variable.
The classic form is a forgotten loop variable: the loop runs n times and every iteration reads the same row.
class Order { const Order(this.id, this.total);
final String id; final double total;}
void printTotals(List<Order> orders) { for (var i = 0; i < orders.length; i++) { print('${orders[0].id}: ${orders[0].total}'); }}class Order { const Order(this.id, this.total);
final String id; final double total;}
void printTotals(List<Order> orders) { for (var i = 0; i < orders.length; i++) { print('${orders[i].id}: ${orders[i].total}'); }}A for-in loop sidesteps the index entirely, which is why it is the usual fix:
void printTotals(List<Order> orders) { for (final order in orders) { print('${order.id}: ${order.total}'); }}The read really is loop-invariant
Section titled “The read really is loop-invariant”Sometimes the constant index is deliberate — a header row, a base currency, a default. Hoist it above the loop so it is read once and the intent is on the page:
// Don't — re-read on every iteration, and it reads like a bugfor (final row in rows) { applyFormat(row, columns[0].format);}
// Dofinal headerFormat = columns[0].format;for (final row in rows) { applyFormat(row, headerFormat);}A const index counts as constant
Section titled “A const index counts as constant”A named constant is no less fixed than a literal, so this reports too:
const kSelectedTab = 0;
// Don'tfor (final event in events) { refresh(tabs[kSelectedTab]);}
// Do — a mutable variable the loop advances is finevar cursor = 0;for (final event in events) { refresh(tabs[cursor]); cursor++;}Known limitations
Section titled “Known limitations”Only a literal or a constant identifier is treated as constant. Any computed index — list[i + 1], list[offset], list[values.length - 1] — is left alone, even when it happens to be invariant.
Nested functions are their own scope. A closure declared inside a loop body is not reported for its constant index, since the loop above it says nothing about how the closure is called.
Configuration
Section titled “Configuration”This rule is in the recommended preset, so it is on with
preset: recommended or preset: opinionated. Add it to preset: core with
avoid_accessing_collections_by_constant_index: true.
To turn it off:
rules: avoid_accessing_collections_by_constant_index: falseTo keep the rule on but skip certain paths, use per-rule exclude.
Related rules
Section titled “Related rules”avoid_unsafe_collection_methods— Check for emptiness before using first, last, single or reduce.prefer_safe_collection_access— list.first throws where list.head returns None.avoid_missing_enum_constant_in_map— Cover every enum constant in a map keyed by that enum.avoid_collection_equality_checks— Avoid comparing collections with == or != as it checks reference equality, not contents.