Laptop screen showing colorful Dart code in a dark editor

Dart Enums for Beginners: Named Values Made Simple

You have a Task class with a status, and right now that status is a plain string. Somewhere you type 'active', somewhere else 'Active', and in a third spot 'in progress', and the bug that follows eats an hour of your evening because nothing ever warned you the values disagreed. Dart enums are the fix. An enum is a type with a fixed, named set of values, so a status can only ever be one of the options you defined. A typo stops being a silent runtime surprise and becomes a compile error you see right away. By the end of this post you will declare an enum, use it in a switch the compiler checks for you, pull out the built-in .name, .index, and .values, turn a string back into an enum value, and use Dart 3 enhanced enums to give each value its own data and methods.

What a Dart enum is

Start with the smallest version that does anything useful. You declare an enum with the enum keyword and list its values inside the braces. Each value is a constant that belongs to the type.

enum Status { active, paused, closed }

void main() {
  Status current = Status.active;
  print(current);        // Status.active
  print(current.name);   // active
}

Run that and the first line prints Status.active, because that is how Dart shows an enum value by default: the type name, a dot, then the value. The second line prints just active, because .name hands you the value as a string without the type in front. That distinction matters more than it looks. The variable current is not the string “active” and it is not the number 0. It is a Status, and the only things you can ever assign to it are Status.active, Status.paused, or Status.closed. Write current = 'active' and Dart refuses to compile, which is exactly the safety net the string version never gave you. This is the whole reason enums exist: a small, closed set of choices that the language enforces for you.

Enums in a switch, and why they beat magic strings

The place an enum earns its keep is a switch. When you switch over an enum, Dart knows every possible value, so it can tell you when you have missed one. Here is a switch expression that turns a status into a label.

enum Status { active, paused, closed }

String label(Status s) => switch (s) {
  Status.active => 'Running',
  Status.paused => 'On hold',
  Status.closed => 'Finished',
};

void main() {
  print(label(Status.paused)); // On hold
}

Call label(Status.paused) and you get On hold. Notice there is no default case and no fallback. You do not need one, because the three cases cover every value Status can hold, and Dart can see that. Now here is the payoff. Go back to the enum and add a fourth value, archived, then try to run the code again. Dart stops you with an error saying the switch does not handle Status.archived. It caught a gap you would otherwise have shipped. Do the same thing with strings and nothing warns you at all. A missed case just falls through and returns the wrong answer, or worse, nothing. That compile-time check is the single best argument for reaching for an enum the moment a value can only be one of a few known things.

Tidy developer desk workspace with laptop, keyboard and notebook
Reach for a Dart enum when a value can only be one of a few known things.

The properties every enum gives you free

Every enum you write comes with a few built-in members you did not have to define. The three you will use constantly are .values, .index, and .name.

enum Priority { low, medium, high }

void main() {
  print(Priority.values);      // [Priority.low, Priority.medium, Priority.high]
  print(Priority.high.index);  // 2
  for (final p in Priority.values) {
    print(p.name);             // low, then medium, then high
  }
}

Priority.values is a list of every value in the order you declared them, which is perfect for building a dropdown or looping over choices. .index gives you the position, starting at zero, so high is 2. And .name, which you saw earlier, turns a value into its bare string. Lean on .name instead of writing your own text, because it stays correct even if you rename a value later.

Turning a string back into an enum

Reading data from JSON or a database usually hands you a string, and you will want the matching enum value. Dart gives you values.byName for exactly that.

enum Priority { low, medium, high }

void main() {
  Priority p = Priority.values.byName('medium');
  print(p); // Priority.medium
}

Pass the name and you get the value back. One catch worth knowing before it bites you: if the string does not match any value, byName throws instead of returning null. So when the text comes from somewhere you do not fully trust, wrap it in a check or fall back to a default. If null handling still feels shaky, the Dart null safety post covers the operators that make this clean.

Enhanced enums: giving each value its own data

Here is where Dart 3 gets interesting. A plain enum is just names, but an enhanced enum can carry data and behavior on each value, almost like a tiny class. You add fields, a const constructor, and pass the data in the parentheses after each value.

enum Plan {
  free(0),
  basic(9),
  pro(29);

  final int priceUsd;
  const Plan(this.priceUsd);

  String get label => '$name (\$$priceUsd/mo)';
}

void main() {
  print(Plan.basic.priceUsd);  // 9
  print(Plan.pro.label);       // pro ($29/mo)
}

Each value now supplies a price to the constructor, so Plan.basic.priceUsd is 9 and Plan.free.priceUsd is 0. The constructor has to be const, and the fields have to be final, because enum values are compile-time constants and cannot change once defined. If constructors are new to you, the Dart classes post walks through them from the start. The label getter shows the real win: your data and the logic that formats it live together on the type, so Plan.pro.label prints pro ($29/mo) without a lookup table sitting off in some other file. Before enhanced enums, people faked this with a map from enum to price and a second map from enum to label, and the two drifted out of sync the first time someone added a value. Keeping everything on the enum removes that whole class of bug.

Close-up of colorful programming code representing Dart enum values
Enhanced enums let each value carry its own data and its own methods.

When an enum is the wrong tool

Enums are easy to over-apply once they click, so keep the test simple. An enum is right when you have a small, fixed set of options that you control and that rarely changes, like the days of the week, the sizes on a menu, or the states a task moves through. It is the wrong tool the moment that set is large, open-ended, or comes from outside your code. Country codes, user IDs, and tags a person can type are not enums, they are data, and forcing them into an enum means editing your source every time the list grows. There is also a middle case worth naming. If your values need a lot of behavior, several methods, mutable state, or their own subclasses, then you have outgrown an enum and want real classes, most likely with the inheritance patterns from the Dart mixins post. Enums are for a closed set of constants, richer when Dart 3 lets them hold data, but still constants at heart.

Your next step

Here is the short version. An enum is a type with a fixed set of named values you declare with the enum keyword, and using one turns a whole family of typo bugs into compile errors. Switch over an enum and Dart checks that you handled every case, which plain strings will never do for you. Every enum hands you .values, .index, and .name for free, and values.byName turns a string back into a value, as long as you handle the case where it does not match. And in Dart 3, enhanced enums let each value carry its own data and methods, so the price and the label and the logic all live in one place.

The way to make this stick is to build one. Write an enum called Weekday with the seven days, then add an enhanced version where each day carries a bool isWeekend and a method that returns a friendly greeting. Loop over Weekday.values and print each one. When you want the exact rules Dart enforces, keep the official Dart enums guide open beside you. Now go replace a string constant you already regret with an enum that cannot be typed wrong.

Leave a Comment

Your email address will not be published. Required fields are marked *