In Dart, what do an enum value's index and name return, and how do you list all values and look one up from a string?
answer
- zero-based declaration position
- the source identifier as a String
- a static values list
- values.byName throws on a miss
- asNameMap for a nullable lookup
basics
~20 sindex is the value's zero-based position in the declaration and name is its source identifier as a String. The generated static values list holds every value in order; values.byName('x') finds one by name and throws ArgumentError when none matches.
solid answer
~40 sEvery Dart enum value has `index`, its zero-based position in declaration order, and `name`, the identifier it was declared with, so `OrderStatus.shipped.name` is `'shipped'`. `toString()` returns the qualified form, `'OrderStatus.shipped'`. The compiler generates a static `values` list containing all values in declaration order, so `OrderStatus.values[i].index == i`. To go from a string back to a value, `OrderStatus.values.byName('shipped')` scans the list and throws an `ArgumentError` if nothing matches; `values.asNameMap()` builds a `Map<String, OrderStatus>` whose lookup returns `null` for an unknown name, which suits untrusted input. `name` is an extension getter, `EnumName.name`, which is why an enum may still declare its own member called `name`. Flutter's old `describeEnum` helper is deprecated in favour of `.name`.
code
dart · 17 linesenum OrderStatus { pending, shipped, delivered, cancelled }
void main() {
print(OrderStatus.shipped.index); // 1
print(OrderStatus.shipped.name); // shipped
print(OrderStatus.shipped); // OrderStatus.shipped
print(OrderStatus.values.length); // 4
print(OrderStatus.values.byName('delivered')); // OrderStatus.delivered
print(OrderStatus.values.asNameMap()['lost']); // null
try {
OrderStatus.values.byName('lost');
} on ArgumentError catch (e) {
print('no such status: ${e.invalidValue}');
}
}go deeper
Recall index as the declaration position, name as the identifier string, and values as the generated list, and know that byName throws on a miss.
Explain why name is an extension getter, how asNameMap gives a null-returning lookup, and how Enum.compareByIndex and compareByName sort values.
Flag index-based persistence and byName on untrusted input as production risks, and choose lookups with explicit fallbacks.
Set conventions for how enums cross boundaries such as storage, APIs and analytics, so identifiers stay stable as the enum evolves.
## What every enum value carries A Dart `enum` declares a type with a **fixed set of constant instances**. Even the simplest form gives each value a few members for free: ```dart enum OrderStatus { pending, shipped, delivered, cancelled } ``` | Member | Example | Result | |---|---|---| | `index` | `OrderStatus.shipped.index` | `1` | | `name` | `OrderStatus.shipped.name` | `'shipped'` | | `toString()` | `OrderStatus.shipped.toString()` | `'OrderStatus.shipped'` | | `values` (static) | `OrderStatus.values` | `[pending, shipped, delivered, cancelled]` | - **`index`** is the value's **zero-based position in the declaration**. The first value is `0`, the next `1`, and so on. It is also the value's position in `values`. - **`name`** is the **source identifier** as a `String`. It is declared as an extension getter, `EnumName.name` in `dart:core`, rather than an instance member, so that an enum can declare its own field or getter called `name` without a conflict. - **`toString()`** includes the enum type: `'OrderStatus.shipped'`. Code that needs just the identifier should use `name`, not string-split `toString()`. - **`values`** is a static list the compiler generates, in declaration order. An enum cannot declare its own member called `values`. ## From a string back to a value `dart:core` adds two extension members, `EnumByName`, on any `Iterable` of enum values, intended for `values`: 1. **`byName(String name)`** walks the list and returns the first value whose `name` matches. If none does, it **throws an `ArgumentError`**. Use it when a miss is a programming error, such as a name you wrote in code. 2. **`asNameMap()`** builds a `Map<String, T>` from names to values. Indexing the map with an unknown name returns **`null`** instead of throwing, which suits data from a server, a deep link or local storage. The `byName` documentation itself suggests caching the map in a static field if lookups are frequent, since `byName` is a linear scan. For a one-off lookup with a fallback you can also write `OrderStatus.values.asNameMap()[raw] ?? OrderStatus.pending`. ## Ordering and comparison Enum values compare with `==` by identity; each value is a canonical constant, so there is exactly one `OrderStatus.shipped` in the program. For sorting, `Enum.compareByIndex` orders by declaration position and `Enum.compareByName` orders alphabetically by name (case-sensitive). A plain enum does not implement `Comparable`, so `list.sort()` without a comparator does not work on it; pass one of those comparators instead. ## Where these members show up in Flutter code - **Dropdowns and filter chips**: `OrderStatus.values.map(...)` builds one menu item or chip per value, and the list updates automatically when a value is added. - **Route and query parameters**: a `?status=shipped` parameter arrives as a `String`; `asNameMap()` turns it back into a value without crashing on a typo. - **Logging**: `status.name` gives a compact identifier for log lines, while `toString()` gives the qualified form. - **Ordering**: sorting a list of orders by status in declaration order is `orders.sort((a, b) => Enum.compareByIndex(a.status, b.status))`. ## Legacy you will still see - **`describeEnum(value)`** from Flutter's `foundation` library returned the part after the dot. It is **deprecated** in favour of the `name` getter. - Hand-written `toString().split('.').last` is the pre-`name` workaround, and it breaks if an enum overrides `toString`. - `name`, `byName`, `asNameMap` and the comparators arrived in Dart 2.15, so any Dart 3 codebase has them. ## Common mistakes - Treating `index` as a stable identifier. It changes whenever someone inserts or reorders values, so it is a poor choice to persist or send over the network. - Using `byName` on untrusted input and letting the `ArgumentError` crash a screen. - Expecting `name` to be a human-readable label. It is the code identifier, such as `'inTransit'`; user-facing text belongs in a field of an enhanced enum or in localized strings.
- Why is name an extension getter rather than an ordinary member of Enum?If `name` were an instance member of every enum, an enum could not declare its own field or getter called `name` without clashing with it. Declaring it as the `EnumName` extension in `dart:core` means a member with that name on the enum itself simply wins, while every other enum still gets `.name` for free.
- A server may send a status string your app version does not know. Which lookup do you use?Not `byName`, which throws an `ArgumentError` for an unknown name. Use `OrderStatus.values.asNameMap()[raw]`, cached in a static field if it runs often, and fall back to a default such as a dedicated `unknown` value when it returns `null`. That keeps an older app working when the backend adds a new status.
saying these in an interview costs you the question
- An enum value's name returns 'OrderStatus.shipped', including the type.
- values.byName returns null when no value matches.
- index is a stable ID that is safe to persist forever.
- You still need describeEnum to get an enum value's name.
- Plain enums implement Comparable, so values sort without a comparator.