DMarlon Studio

Lesson 8 · 8 of 20

Enhanced enums · Dart

Give your Dart enums fields, a constructor and methods, and use one to model an order's life cycle with a switch the compiler checks.

Dart · Enhanced enums in DMarlon Studio: the example's code and its output in the terminal

A Dart enum can be much more than a list of names: enhanced enums have fields, a constructor and methods. OrderStatus lists the states of an order, and each value carries its label and whether it can still be cancelled (canCancel).

The values come first, each with its arguments, and the list ends with a semicolon. The constructor must be `const` and the fields final, because every value is a constant created only once. canCancel is a named parameter that defaults to false, so only pending and paid need to mention it.

The next getter uses a `switch` expression on this to return the following state. There is no final _ case: the compiler knows every value of the enum and checks they are all covered (delivered || cancelled groups two of them in one case). If a state is added tomorrow, the switch stops compiling until it is handled. isFinal builds on next.

In main, the loop walks through the order's flow until next returns null. Every enum also comes with values (the list of values), name (its name as text), index (its position) and values.byName, which turns a string back into the value: very handy when reading a state stored in JSON or a database.

Code · main.dart

// An enhanced enum: each value carries data and the enum has methods.enum OrderStatus {  pending('Pending', canCancel: true),  paid('Paid', canCancel: true),  shipped('Shipped'),  delivered('Delivered'),  cancelled('Cancelled');  const OrderStatus(this.label, {this.canCancel = false});  final String label;  final bool canCancel;  OrderStatus? get next => switch (this) {        pending => paid,        paid => shipped,        shipped => delivered,        delivered || cancelled => null,      };  bool get isFinal => next == null;}void main() {  OrderStatus? status = OrderStatus.pending;  while (status != null) {    final flags = '${status.canCancel ? 'can cancel' : 'locked'}, final: ${status.isFinal}';    print('${status.label.padRight(9)} -> $flags');    status = status.next;  }  print(OrderStatus.values.map((s) => s.name).join(' | '));  final shipped = OrderStatus.values.byName('shipped');  print('${shipped.label} is value number ${shipped.index}');}

Output · dart run main.dart

Pending   -> can cancel, final: false
Paid      -> can cancel, final: false
Shipped   -> locked, final: false
Delivered -> locked, final: true
pending | paid | shipped | delivered | cancelled
Shipped is value number 2

Key points

  • An enhanced enum has fields, a const constructor and methods.
  • Each value is declared with its arguments.
  • A switch over an enum must cover every value.
  • values, name, index and byName come built in.
  • A new state forces you to revisit every switch, so nothing is forgotten.

Exercise

Add a returned('Returned') state that follows delivered; the compiler will make you handle it in the next switch.