On this page24 sections
- Prerequisites
- Connection With Previous Lectures
- Learning Objectives
- 1. The Problem with String Values
- 2. Declaring an Enum
- 3. Typos Become Errors
- 4. An Enum Is a Type
- 5. name and index
- 6. All Values with values
- 7. Comparing Enum Values
- 8. Enums in a switch Statement
- 9. Switch Expressions and Exhaustiveness
- 10. From Text to Enum with byName
- 11. Enums in Classes and Collections
- 12. A Real-World Example
- Code Examples
- Common Beginner Mistakes
- Best Practices
- Mini Challenge
- Practice Questions
- Practice Answers
- Quick Revision
- Key Takeaways
- What's Next?
Lecture 67 — Enums
| Category | 11 — Enums, Extensions & Typedefs |
| Lecture | 067 of 140 |
| Estimated video length | 9 min |
| Target | Dart 3.12.2 |
| Level | Intermediate |
A
Stringlike'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:
switchstatements and switch expressions, including exhaustiveness (Lectures 024–025)- functions with typed parameters
- lists,
for-inloops, 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
nameandindex, and list all values withvalues - 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:
void main() {
var status = 'shiped';
if (status == 'shipped') {
print('Your order is on the way');
} else {
print('Status unknown');
}
}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.
enum OrderStatus { pending, shipped, delivered }
void main() {
var status = OrderStatus.shipped;
print(status);
}OrderStatus.shippedYou 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:
enum OrderStatus { pending, shipped, delivered }
void main() {
var status = OrderStatus.shiped;
print(status);
}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:
enum OrderStatus { pending, shipped, delivered }
void track(OrderStatus status) {
print('Tracking: $status');
}
void main() {
OrderStatus status = OrderStatus.pending;
status = OrderStatus.delivered;
track(status);
}Tracking: OrderStatus.deliveredCalling 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:
enum OrderStatus { pending, shipped, delivered }
void main() {
var status = OrderStatus.shipped;
print(status.name);
print(status.index);
}shipped
1Use 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:
enum OrderStatus { pending, shipped, delivered }
void main() {
print(OrderStatus.values.length);
for (final status in OrderStatus.values) {
print('${status.index}: ${status.name}');
}
}3
0: pending
1: shipped
2: delivered7. Comparing Enum Values
Use == and !=. Each value exists exactly once, so comparing is simple and safe:
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);
}true
true
trueEnums 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:
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');
}
}Get readyNo 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:
enum TrafficLight { red, yellow, green }
void main() {
var light = TrafficLight.green;
var action = switch (light) {
TrafficLight.red => 'Stop',
TrafficLight.yellow => 'Get ready',
};
print(action);
}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:
enum OrderStatus { pending, shipped, delivered }
void main() {
var text = 'delivered';
var status = OrderStatus.values.byName(text);
print(status);
print(status == OrderStatus.delivered);
}OrderStatus.delivered
trueThe name must match exactly. byName('huge') on an enum without huge stops the program:
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:
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]);
}Ali: math
false
75A map with enum keys is a tidy way to attach a value — a mark, a price — to each choice.
12. A Real-World Example
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}');
}Ali: Preparing your order
Sara: On the way
Zayan: Delivered
Delivered: 1 of 3The 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
enum Weekday { monday, tuesday, wednesday }
void main() {
var today = Weekday.tuesday;
print(today);
}Weekday.tuesdayExample 2 — name and index
enum Size { small, medium, large }
void main() {
var size = Size.large;
print('${size.name} is at position ${size.index}');
}large is at position 2Example 3 — Looping over values
enum Size { small, medium, large }
void main() {
for (final size in Size.values) {
print(size.name.toUpperCase());
}
}SMALL
MEDIUM
LARGEExample 4 — Comparing Values
enum Size { small, medium, large }
void main() {
var cup = Size.medium;
print(cup == Size.medium);
print(cup.index > Size.small.index);
}true
trueExample 5 — byName
enum Size { small, medium, large }
void main() {
var size = Size.values.byName('small');
print(size.index);
}0Example 6 — switch Statement
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');
}
}T-shirt weatherExample 7 — Student Marks
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()}');
}Sara: BHow it works
marksis anint, so that switch needs_.- The result is a
Grade— later code can only receive one of four grades.
Example 8 — Shopping Total
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}');
}Total: 2900How it works
- Each delivery option gets exactly one fee.
- No
_: a new option such asdronewould make this switch an error until it gets a fee.
Example 9 — Bank Balance
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');
}Balance: 1300.0How it works
- Each
Transactionstores its type as an enum field. - The switch turns a withdrawal into a negative amount;
1000 + 500 - 200 = 1300.
Example 10 — Temperature Units
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));
}25.0
25.0How it works
- The
Unitparameter makes the caller say which scale the number is in. (77 - 32) * 5 / 9is25.0.
Common Beginner Mistakes
1. Declaring an enum inside main()
// void main() {
// enum Size { small, medium, large } // ✗
// }
enum Size { small, medium, large } // ✓ top level
void main() {}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
enum Size { small, medium, large }
void main() {
// var s = small; // ✗
var s = Size.small; // ✓
print(s);
}Error: Undefined name 'small'.3. Trying to create an enum value
enum Size { small, medium, large }
void main() {
// var s = Size(); // ✗
}Error: Enums can't be instantiated.The values are already created. Use one of them.
4. Comparing an enum with a String
enum Size { small, medium, large }
void main() {
var s = Size.medium;
print(s == 'medium'); // ✗ always false
print(s.name == 'medium'); // ✓
}false
trueNo error, just a wrong answer. Compare with Size.medium, or compare name with the text.
5. Using < on enum values
enum Size { small, medium, large }
void main() {
// print(Size.small < Size.large); // ✗
print(Size.small.index < Size.large.index); // ✓
}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
namefor users; keep the enum value itself in your data. - Convert text with
values.byNameonly at the edge of your program, right where it arrives. - Don't depend on
indexfor 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:
easy: 50
medium: 100
hard: 150Try it yourself before opening the solution.
Show solution
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}');
}
}easy: 50
medium: 100
hard: 150How it works
Level.valuesgives the levels in declaration order.- 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
- Declare
enum Color { red, green, blue }and printColor.green. - Print the
nameandindexofColor.blue, each on its own line. - Loop over
Color.valuesand print each name.
Application
- Write
void describe(Color color)that printsColor is red(usingname), and call it withColor.red. - Use a switch expression on
Colorto print#00FF00forColor.green(red is#FF0000, blue is#0000FF). Use no_. - Convert the text
'blue'into aColorwithbyName, then print whether it equalsColor.blue. - Store Ali's marks in a map with keys from
enum Subject { math, science }: math80, science75. Print the total.
Challenge
- Declare
enum Priority { low, medium, high }. Given the list[Priority.high, Priority.low, Priority.high], count how many arehighand printHigh: 2. - Write a class
Ticketwith aString titleand aPriority priority. Create a ticket'Login bug'withPriority.highand printLogin bug (high). - A switch expression on
Priorityreturns'Later'forlowand'Today'formedium. What does Dart say, and how do you fix it soPriority.highprintsNow?
Practice Answers
Question 1 — Solution
enum Color { red, green, blue }
void main() {
print(Color.green);
}Color.greenQuestion 2 — Solution
enum Color { red, green, blue }
void main() {
print(Color.blue.name);
print(Color.blue.index);
}blue
2Question 3 — Solution
enum Color { red, green, blue }
void main() {
for (final color in Color.values) {
print(color.name);
}
}red
green
blueQuestion 4 — Solution
enum Color { red, green, blue }
void describe(Color color) {
print('Color is ${color.name}');
}
void main() {
describe(Color.red);
}Color is redQuestion 5 — Solution
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);
}#00FF00Question 6 — Solution
enum Color { red, green, blue }
void main() {
var color = Color.values.byName('blue');
print(color == Color.blue);
}trueQuestion 7 — Solution
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');
}Total: 155marks[...] may return null, so ! (Lecture 015) says the key is certainly there.
Question 8 — Solution
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');
}High: 2Question 9 — Solution
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})');
}Login bug (high)Question 10 — Solution
Dart reports the missing value:
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:
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);
}NowQuick Revision
- What problem do enums solve that strings cannot?
- Where must an enum be declared?
- What do
nameandindexreturn? - What does
valuesgive you, and in what order? - Why can't you write
Size.small < Size.large? - Why does a switch on an enum not need
_? - What happens when
byNamegets 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.
namegives the value's text,indexits position from0, andvaluesa list of all of them.- Compare enum values with
==and!=; for ordering, compareindex. - 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.