Watch the video (9 min)
On this page24 sections
  1. Prerequisites
  2. Connection With Previous Lectures
  3. Learning Objectives
  4. 1. The Problem with String Values
  5. 2. Declaring an Enum
  6. 3. Typos Become Errors
  7. 4. An Enum Is a Type
  8. 5. name and index
  9. 6. All Values with values
  10. 7. Comparing Enum Values
  11. 8. Enums in a switch Statement
  12. 9. Switch Expressions and Exhaustiveness
  13. 10. From Text to Enum with byName
  14. 11. Enums in Classes and Collections
  15. 12. A Real-World Example
  16. Code Examples
  17. Common Beginner Mistakes
  18. Best Practices
  19. Mini Challenge
  20. Practice Questions
  21. Practice Answers
  22. Quick Revision
  23. Key Takeaways
  24. What's Next?

Lecture 67 — Enums

Category11 — Enums, Extensions & Typedefs
Lecture067 of 140
Estimated video length9 min
TargetDart 3.12.2
LevelIntermediate

A String like 'shipped' accepts any text, so a typo slips through silently. An enum lists every allowed value once — and Dart rejects anything else before the program runs.

Contents

Prerequisites · Connection · Objectives · Lesson · Code Examples · Common Mistakes · Best Practices · Mini Challenge · Practice Questions · Answers · Takeaways

Every error message in this lecture is real. Each one was produced by running the code shown on Dart 3.12.2, so you will recognize it when you meet it yourself.


Prerequisites

Students should already understand, from Lectures 024–066:

  • switch statements and switch expressions, including exhaustiveness (Lectures 024–025)
  • functions with typed parameters
  • lists, for-in loops, sets, and maps
  • classes, fields, constructors, and toString()

Connection With Previous Lectures

Many values in real programs come from a short, fixed list: an order is pending, shipped, or delivered; a traffic light is red, yellow, or green. So far we have stored such values as strings. But a String can hold any text — 'shiped', 'Shipped', 'banana' — and Dart cannot know that only three spellings are valid. In Lecture 025 we saw that a switch expression on an int needs _, because Dart cannot list every int. We need a type whose values Dart can list.


Learning Objectives

By the end of this lecture you will be able to:

  • Explain why string constants are fragile
  • Declare an enum and use its values
  • Read a value's name and index, and list all values with values
  • Compare enum values correctly
  • Use enums in switch statements and switch expressions without _
  • Explain how exhaustiveness catches a forgotten case
  • Convert text to an enum value with byName
  • Store enum values in fields, lists, sets, and maps

1. The Problem with String Values

Here, one missing letter breaks the program — and Dart says nothing:

dart
void main() {
  var status = 'shiped';
  if (status == 'shipped') {
    print('Your order is on the way');
  } else {
    print('Status unknown');
  }
}
output
Status unknown

'shiped' is a perfectly valid String. The bug only shows up as wrong output, maybe weeks later.


2. Declaring an Enum

An enum (short for enumeration) is a type with a fixed list of named values. Declare it at the top level with enum, a name, and the values in braces:

Analogy — an enum is like a lift's buttons. You can press Ground, 1, or 2, but there is no button for floor 7½. Only the listed choices exist.

dart
enum OrderStatus { pending, shipped, delivered }

void main() {
  var status = OrderStatus.shipped;
  print(status);
}
output
OrderStatus.shipped

You reach a value through the enum's name: OrderStatus.shipped. The enum name is UpperCamelCase like a class; the values are lowerCamelCase.


3. Typos Become Errors

Now misspell the value:

dart
enum OrderStatus { pending, shipped, delivered }

void main() {
  var status = OrderStatus.shiped;
  print(status);
}
output
Error: Member not found: 'shiped'.
  var status = OrderStatus.shiped;
                           ^^^^^^

The bug from section 1 can no longer reach the output — the program does not compile.


4. An Enum Is a Type

OrderStatus is a real type, so it works for variables and parameters. A variable of that type can only hold one of its three values:

dart
enum OrderStatus { pending, shipped, delivered }

void track(OrderStatus status) {
  print('Tracking: $status');
}

void main() {
  OrderStatus status = OrderStatus.pending;
  status = OrderStatus.delivered;
  track(status);
}
output
Tracking: OrderStatus.delivered

Calling track('shipped') fails with The argument type 'String' can't be assigned to the parameter type 'OrderStatus'.


5. name and index

Every enum value knows its name as a String and its position as an int, counting from 0:

dart
enum OrderStatus { pending, shipped, delivered }

void main() {
  var status = OrderStatus.shipped;
  print(status.name);
  print(status.index);
}
output
shipped
1

Use name when printing for people — 'Status: ${status.name}' reads better than OrderStatus.shipped.


6. All Values with values

OrderStatus.values is a List of every value, in declaration order:

dart
enum OrderStatus { pending, shipped, delivered }

void main() {
  print(OrderStatus.values.length);
  for (final status in OrderStatus.values) {
    print('${status.index}: ${status.name}');
  }
}
output
3
0: pending
1: shipped
2: delivered

7. Comparing Enum Values

Use == and !=. Each value exists exactly once, so comparing is simple and safe:

dart
enum OrderStatus { pending, shipped, delivered }

void main() {
  var status = OrderStatus.shipped;
  print(status == OrderStatus.shipped);
  print(status != OrderStatus.delivered);
  print(status.index < OrderStatus.delivered.index);
}
output
true
true
true

Enums have no < or >. OrderStatus.pending < OrderStatus.shipped is an error: The operator '<' isn't defined for the type 'OrderStatus'. Compare the index values instead, as in the last line.


8. Enums in a switch Statement

A switch on an enum uses the values as cases:

dart
enum TrafficLight { red, yellow, green }

void main() {
  var light = TrafficLight.yellow;
  switch (light) {
    case TrafficLight.red:
      print('Stop');
    case TrafficLight.yellow:
      print('Get ready');
    case TrafficLight.green:
      print('Go');
  }
}
output
Get ready

No default is needed: the three cases cover every possible TrafficLight.


9. Switch Expressions and Exhaustiveness

Because Dart knows every value, a switch expression on an enum needs no _. And if you forget a value, Dart tells you exactly which one:

dart
enum TrafficLight { red, yellow, green }

void main() {
  var light = TrafficLight.green;
  var action = switch (light) {
    TrafficLight.red => 'Stop',
    TrafficLight.yellow => 'Get ready',
  };
  print(action);
}
output
Error: The type 'TrafficLight' is not exhaustively matched by the switch cases since it doesn't match 'TrafficLight.green'.
Try adding a wildcard pattern or cases that match 'TrafficLight.green'.
  var action = switch (light) {
                       ^

Add TrafficLight.green => 'Go', and it prints Go. A switch statement on an enum is checked the same way: leave out a case and you get the same error with "Try adding a default case".

This is the real power of enums. Add a fourth value, flashing, next month, and Dart points at every switch that forgot to handle it. A _ arm would hide those errors — so on enums, list the values instead.


10. From Text to Enum with byName

Text from a user or a file arrives as a String. values.byName finds the value with that name:

dart
enum OrderStatus { pending, shipped, delivered }

void main() {
  var text = 'delivered';
  var status = OrderStatus.values.byName(text);
  print(status);
  print(status == OrderStatus.delivered);
}
output
OrderStatus.delivered
true

The name must match exactly. byName('huge') on an enum without huge stops the program:

output
Unhandled exception:
Invalid argument (name): No enum value with that name: "huge"

We will learn to catch such failures in Lecture 084 — try, catch & finally.


11. Enums in Classes and Collections

An enum type works anywhere a type does — as a field, in a List, a Set, or as Map keys:

dart
enum Subject { math, science, english }

class Exam {
  final String student;
  final Subject subject;
  Exam(this.student, this.subject);
}

void main() {
  var exam = Exam('Ali', Subject.math);
  print('${exam.student}: ${exam.subject.name}');

  var favourites = {Subject.science, Subject.english};
  print(favourites.contains(Subject.math));

  var marks = {Subject.math: 80, Subject.science: 75};
  print(marks[Subject.science]);
}
output
Ali: math
false
75

A map with enum keys is a tidy way to attach a value — a mark, a price — to each choice.


12. A Real-World Example

dart
enum OrderStatus { pending, shipped, delivered }

class Order {
  final String customer;
  OrderStatus status;
  Order(this.customer, this.status);
}

String message(OrderStatus status) => switch (status) {
  OrderStatus.pending => 'Preparing your order',
  OrderStatus.shipped => 'On the way',
  OrderStatus.delivered => 'Delivered',
};

void main() {
  var orders = [
    Order('Ali', OrderStatus.pending),
    Order('Sara', OrderStatus.values.byName('shipped')),
    Order('Zayan', OrderStatus.delivered),
  ];

  for (final order in orders) {
    print('${order.customer}: ${message(order.status)}');
  }

  var done = orders.where((o) => o.status == OrderStatus.delivered);
  print('Delivered: ${done.length} of ${orders.length}');
}
output
Ali: Preparing your order
Sara: On the way
Zayan: Delivered
Delivered: 1 of 3

The status is a field, one value comes from text with byName, the switch expression covers every status without _, and == filters the list.


Code Examples

Example 1 — Declaring an Enum

dart
enum Weekday { monday, tuesday, wednesday }

void main() {
  var today = Weekday.tuesday;
  print(today);
}
output
Weekday.tuesday

Example 2 — name and index

dart
enum Size { small, medium, large }

void main() {
  var size = Size.large;
  print('${size.name} is at position ${size.index}');
}
output
large is at position 2

Example 3 — Looping over values

dart
enum Size { small, medium, large }

void main() {
  for (final size in Size.values) {
    print(size.name.toUpperCase());
  }
}
output
SMALL
MEDIUM
LARGE

Example 4 — Comparing Values

dart
enum Size { small, medium, large }

void main() {
  var cup = Size.medium;
  print(cup == Size.medium);
  print(cup.index > Size.small.index);
}
output
true
true

Example 5 — byName

dart
enum Size { small, medium, large }

void main() {
  var size = Size.values.byName('small');
  print(size.index);
}
output
0

Example 6 — switch Statement

dart
enum Season { winter, spring, summer, autumn }

void main() {
  var season = Season.summer;
  switch (season) {
    case Season.winter || Season.autumn:
      print('Bring a jacket');
    case Season.spring || Season.summer:
      print('T-shirt weather');
  }
}
output
T-shirt weather

Example 7 — Student Marks

dart
enum Grade { a, b, c, fail }

void main() {
  var marks = 82;
  var grade = switch (marks) {
    >= 90 => Grade.a,
    >= 75 => Grade.b,
    >= 50 => Grade.c,
    _ => Grade.fail,
  };
  print('Sara: ${grade.name.toUpperCase()}');
}
output
Sara: B

How it works

  1. marks is an int, so that switch needs _.
  2. The result is a Grade — later code can only receive one of four grades.

Example 8 — Shopping Total

dart
enum Delivery { standard, express, pickup }

void main() {
  var subtotal = 2400;
  var delivery = Delivery.express;
  var fee = switch (delivery) {
    Delivery.standard => 200,
    Delivery.express => 500,
    Delivery.pickup => 0,
  };
  print('Total: ${subtotal + fee}');
}
output
Total: 2900

How it works

  1. Each delivery option gets exactly one fee.
  2. No _: a new option such as drone would make this switch an error until it gets a fee.

Example 9 — Bank Balance

dart
enum TransactionType { deposit, withdrawal }

class Transaction {
  final TransactionType type;
  final double amount;
  Transaction(this.type, this.amount);
}

void main() {
  var balance = 1000.0;
  var history = [
    Transaction(TransactionType.deposit, 500),
    Transaction(TransactionType.withdrawal, 200),
  ];
  for (final t in history) {
    balance += switch (t.type) {
      TransactionType.deposit => t.amount,
      TransactionType.withdrawal => -t.amount,
    };
  }
  print('Balance: $balance');
}
output
Balance: 1300.0

How it works

  1. Each Transaction stores its type as an enum field.
  2. The switch turns a withdrawal into a negative amount; 1000 + 500 - 200 = 1300.

Example 10 — Temperature Units

dart
enum Unit { celsius, fahrenheit }

double toCelsius(double value, Unit unit) => switch (unit) {
  Unit.celsius => value,
  Unit.fahrenheit => (value - 32) * 5 / 9,
};

void main() {
  print(toCelsius(25, Unit.celsius));
  print(toCelsius(77, Unit.fahrenheit));
}
output
25.0
25.0

How it works

  1. The Unit parameter makes the caller say which scale the number is in.
  2. (77 - 32) * 5 / 9 is 25.0.

Common Beginner Mistakes

1. Declaring an enum inside main()

dart
// void main() {
//   enum Size { small, medium, large } // ✗
// }
enum Size { small, medium, large } // ✓ top level

void main() {}
output
Error: 'enum' can't be used as an identifier because it's a keyword.

Enums, like classes, are declared at the top level.

2. Leaving out the enum name

dart
enum Size { small, medium, large }

void main() {
  // var s = small; // ✗
  var s = Size.small; // ✓
  print(s);
}
output
Error: Undefined name 'small'.

3. Trying to create an enum value

dart
enum Size { small, medium, large }

void main() {
  // var s = Size(); // ✗
}
output
Error: Enums can't be instantiated.

The values are already created. Use one of them.

4. Comparing an enum with a String

dart
enum Size { small, medium, large }

void main() {
  var s = Size.medium;
  print(s == 'medium'); // ✗ always false
  print(s.name == 'medium'); // ✓
}
output
false
true

No error, just a wrong answer. Compare with Size.medium, or compare name with the text.

5. Using < on enum values

dart
enum Size { small, medium, large }

void main() {
  // print(Size.small < Size.large); // ✗
  print(Size.small.index < Size.large.index); // ✓
}
output
Error: The operator '<' isn't defined for the type 'Size'.

6. Adding _ to an enum switch

It compiles, but a newly added value then falls into _ silently instead of being reported. List every value instead.


Best Practices

  • Use an enum whenever a value must be one of a fixed set of choices.
  • Name the enum in UpperCamelCase (OrderStatus) and its values in lowerCamelCase (shipped).
  • Type fields and parameters with the enum, not String.
  • Switch on enums without _ so Dart reports forgotten cases.
  • Print name for users; keep the enum value itself in your data.
  • Convert text with values.byName only at the edge of your program, right where it arrives.
  • Don't depend on index for meaning that lasts — reordering the values changes every index.

Mini Challenge

Zayan's game has three levels: easy, medium, hard. Points are multiplied by 1, 2, and 3. Write an enum and a switch expression, then print every level with the score for 50 base points.

Your program should print exactly:

output
easy: 50
medium: 100
hard: 150

Try it yourself before opening the solution.

Show solution
dart
enum Level { easy, medium, hard }

void main() {
  var basePoints = 50;
  for (final level in Level.values) {
    var multiplier = switch (level) {
      Level.easy => 1,
      Level.medium => 2,
      Level.hard => 3,
    };
    print('${level.name}: ${basePoints * multiplier}');
  }
}
output
easy: 50
medium: 100
hard: 150

How it works

  1. Level.values gives the levels in declaration order.
  2. The switch covers all three levels, so no _ is needed.

Practice Questions

Work through these in order — they run from beginner to application to challenge. Every question gives you exact values, so you can compare your output with the answers.

Beginner

  1. Declare enum Color { red, green, blue } and print Color.green.
  2. Print the name and index of Color.blue, each on its own line.
  3. Loop over Color.values and print each name.

Application

  1. Write void describe(Color color) that prints Color is red (using name), and call it with Color.red.
  2. Use a switch expression on Color to print #00FF00 for Color.green (red is #FF0000, blue is #0000FF). Use no _.
  3. Convert the text 'blue' into a Color with byName, then print whether it equals Color.blue.
  4. Store Ali's marks in a map with keys from enum Subject { math, science }: math 80, science 75. Print the total.

Challenge

  1. Declare enum Priority { low, medium, high }. Given the list [Priority.high, Priority.low, Priority.high], count how many are high and print High: 2.
  2. Write a class Ticket with a String title and a Priority priority. Create a ticket 'Login bug' with Priority.high and print Login bug (high).
  3. A switch expression on Priority returns 'Later' for low and 'Today' for medium. What does Dart say, and how do you fix it so Priority.high prints Now?

Practice Answers

Question 1 — Solution

dart
enum Color { red, green, blue }

void main() {
  print(Color.green);
}
output
Color.green

Question 2 — Solution

dart
enum Color { red, green, blue }

void main() {
  print(Color.blue.name);
  print(Color.blue.index);
}
output
blue
2

Question 3 — Solution

dart
enum Color { red, green, blue }

void main() {
  for (final color in Color.values) {
    print(color.name);
  }
}
output
red
green
blue

Question 4 — Solution

dart
enum Color { red, green, blue }

void describe(Color color) {
  print('Color is ${color.name}');
}

void main() {
  describe(Color.red);
}
output
Color is red

Question 5 — Solution

dart
enum Color { red, green, blue }

void main() {
  var color = Color.green;
  var hex = switch (color) {
    Color.red => '#FF0000',
    Color.green => '#00FF00',
    Color.blue => '#0000FF',
  };
  print(hex);
}
output
#00FF00

Question 6 — Solution

dart
enum Color { red, green, blue }

void main() {
  var color = Color.values.byName('blue');
  print(color == Color.blue);
}
output
true

Question 7 — Solution

dart
enum Subject { math, science }

void main() {
  var marks = {Subject.math: 80, Subject.science: 75};
  var total = marks[Subject.math]! + marks[Subject.science]!;
  print('Total: $total');
}
output
Total: 155

marks[...] may return null, so ! (Lecture 015) says the key is certainly there.

Question 8 — Solution

dart
enum Priority { low, medium, high }

void main() {
  var tasks = [Priority.high, Priority.low, Priority.high];
  var count = tasks.where((p) => p == Priority.high).length;
  print('High: $count');
}
output
High: 2

Question 9 — Solution

dart
enum Priority { low, medium, high }

class Ticket {
  final String title;
  final Priority priority;
  Ticket(this.title, this.priority);
}

void main() {
  var ticket = Ticket('Login bug', Priority.high);
  print('${ticket.title} (${ticket.priority.name})');
}
output
Login bug (high)

Question 10 — Solution

Dart reports the missing value:

output
Error: The type 'Priority' is not exhaustively matched by the switch cases since it doesn't match 'Priority.high'.

Add an arm for Priority.high:

dart
enum Priority { low, medium, high }

void main() {
  var priority = Priority.high;
  var timing = switch (priority) {
    Priority.low => 'Later',
    Priority.medium => 'Today',
    Priority.high => 'Now',
  };
  print(timing);
}
output
Now

Quick Revision

  1. What problem do enums solve that strings cannot?
  2. Where must an enum be declared?
  3. What do name and index return?
  4. What does values give you, and in what order?
  5. Why can't you write Size.small < Size.large?
  6. Why does a switch on an enum not need _?
  7. What happens when byName gets a name that does not exist?

Key Takeaways

  • An enum is a type with a fixed list of named values: enum OrderStatus { pending, … }.
  • A misspelled value is a compile error, not a silent bug.
  • name gives the value's text, index its position from 0, and values a list of all of them.
  • Compare enum values with == and !=; for ordering, compare index.
  • Switches on enums are exhaustive without _, and Dart names any forgotten value.
  • values.byName('text') turns text into a value, and fails loudly on an unknown name.
  • Enum types work as fields, parameters, and in lists, sets, and map keys.

What's Next?

Lecture 68 — Enhanced Enums. Our enums are just names; the delivery fees and the level multipliers had to live in separate switches. Next we will give enum values their own fields, constructors, and methods, so each value carries its data with it.


Video lecture

Subscribe
Placeholder video — the real recording isn't linked yet.