Skip to content

prefer_enums_by_name

v0.4.0 Warning Fix Collection & Type

Flags EnumType.values.firstWhere((e) => e.name == value), which the built-in .byName() does in one call. The quick fix rewrites it.

The usual place this appears is decoding an enum out of JSON or a query parameter:

enum ShippingSpeed { standard, express, overnight }
ShippingSpeed parseSpeed(String raw) {
return ShippingSpeed.values.firstWhere((speed) => speed.name == raw);
}
enum ShippingSpeed { standard, express, overnight }
ShippingSpeed parseSpeed(String raw) {
return ShippingSpeed.values.byName(raw);
}

.byName() throws an ArgumentError naming the enum and the bad value, where firstWhere throws a bare StateError with no clue which lookup failed.

The comparison written the other way round

Section titled “The comparison written the other way round”
// Don't
final speed = ShippingSpeed.values.firstWhere(
(s) => 'express' == s.name,
);
// Do
final speed = ShippingSpeed.values.byName('express');

firstWhere with an orElse is still reported — pair .byName() with a try/catch, or reach for the null-returning form from package:collection:

// Don't
final speed = ShippingSpeed.values.firstWhere(
(s) => s.name == raw,
orElse: () => ShippingSpeed.standard,
);
// Do
final speed = ShippingSpeed.values.asNameMap()[raw] ?? ShippingSpeed.standard;

Only a comparison against .name is matched. A lookup keyed on a custom field — Currency.values.firstWhere((c) => c.code == raw) — has no byName equivalent and is never reported.

The callback must be a single-parameter function expression whose body is one comparison — (e) { return e.name == raw; } counts, a multi-statement body does not. A tear-off passed as the predicate is left alone.

This rule is in the recommended preset, so it is on with preset: recommended or preset: opinionated. Add it to preset: core with prefer_enums_by_name: true.

To turn it off:

many_lints.yaml
rules:
prefer_enums_by_name: false

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