Chapters

Hide chapters

Dart Apprentice: Beyond the Basics

First Edition · Flutter · Dart 2.18 · VS Code 1.71

Dart Apprentice: Beyond the Basics

Section 1: 15 chapters
Show chapters Hide chapters

9. Enhanced Enums
Written by Jonathan Sande

Heads up... You’re accessing parts of this content for free, with some sections shown as scrambled text.

Heads up... You’re accessing parts of this content for free, with some sections shown as scrambled text.

Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now

Back in Dart Apprentice: Fundamentals, you learned about basic enums. They’re a way to give names to a fixed number of options. An example would be the days of the week. Under the hood, each day has an index: monday is 0, tuesday is 1 and so on through sunday is 6. That’s why they’re called enums. They’re enumerated values with names.

Using enums in this way has always been useful, but with Dart 2.17, they got even better. You can treat the new enhanced enums like classes. And that’s what they are. Dart enums are subclasses of the Enum class. That means you can do many of the same things to enums that you would do with a class, including adding properties and methods, implementing interfaces and using mixins and generics.

Sound good? Get ready to enhance your enum skills then!

Reviewing the Basics

To get started, review what you already know about enums.

What to Use Enums For

Enums are great when you have a fixed number of options you want to represent. Here are some examples of that:

Advantages of Using Enums

In the past, people often wrote logic like this:

const int GREEN = 0;
const int YELLOW = 1;
const int RED = 2;

void printMessage(int lightColor) {
  switch (lightColor) {
    case GREEN:
      print('Go!');
      break;
    case YELLOW:
      print('Slow down!');
      break;
    case RED:
      print('Stop!');
      break;
    default:
      print('Unrecognized option');
  }
}

Coding a Basic Enum

Can you write an enum for the colors of a traffic light?

enum TrafficLight {
  green,
  yellow,
  red,
}
final color = TrafficLight.green;
switch (color) {
  case TrafficLight.green:
    print('Go!');
    break;
  case TrafficLight.yellow:
    print('Slow down!');
    break;
  case TrafficLight.red:
    print('Stop!');
    break;
}

Treating Enums Like Classes

Enums are just classes, and enum values are instances of the class. This means that you can apply much of your other knowledge about classes.

Adding Constructors and Properties

Just as classes have constructors and properties, so do enums.

enum TrafficLight {
  green('Go!'),
  yellow('Slow down!'),
  red('Stop!');

  const TrafficLight(this.message);
  final String message;
}
// alternate formatting
green('Go!'),
yellow('Slow down!'),
red('Stop!'),           // trailing comma
;                       // semicolon
final color = TrafficLight.green;
print(color.message);
Go!

Operator Overloading

This is a good opportunity to teach you an aspect of classes you might not know about yet. The topic is operator overloading.

print(3 + 2); // 5
print('a' + 'b'); // ab

Overloading an Operator in a Class

Create the following Point class, which has x-y coordinates:

class Point {
  const Point(this.x, this.y);
  final double x;
  final double y;

  @override
  String toString() => '($x, $y)';
}
l q 1 2 8 5 2 5 9 3 2 2 1 0 1 1 7 3 7 5 9, 2 9, 5
Vne boibzp il o dfitq

const pointA = Point(1, 4);
const pointB = Point(3, 2);
w g 8 8 1 7 7 4 4 9 7 3 5 6 6 4 8 4 1 7 0, 9 3, 4 6, 4
Wzu peirgf uvmem maciktiy

Point operator +(Point other) {
  return Point(x + other.x, y + other.y);
}
final pointC = pointA + pointB;
print(pointC);
(4.0, 6.0)

Overloading an Operator in an Enum

Because enums are classes, they also support operator overloading.

enum Day {
  monday,
  tuesday,
  wednesday,
  thursday,
  friday,
  saturday,
  sunday,
}
enum Day {
  monday,
  tuesday,
  wednesday,
  thursday,
  friday,
  saturday,
  sunday;

  Day operator +(int days) {
    // 1
    final numberOfItems = Day.values.length;
    // 2
    final index = (this.index + days) % numberOfItems;
    // 3
    return Day.values[index];
  }
}
var day = Day.monday;

day = day + 2;
print(day.name); // wednesday

day += 4;
print(day.name); // sunday

day++;
print(day.name); // monday

Adding Methods

You can also add methods to an enum just as you would to a normal class. Technically, operator overloading is already adding a method, but this section will provide an additional example.

Day get next {
  return this + 1;
}
final restDay = Day.saturday;
print(restDay.next);

Implementing Interfaces

Say you have the following interface that you use to serialize objects for storage in a database:

abstract class Serializable {
  String serialize();
}
enum Weather implements Serializable {
  sunny,
  cloudy,
  rainy;

  @override
  String serialize() => name;
}
static Weather deserialize(String value) {
  return values.firstWhere(
    (element) => element.name == value,
    orElse: () => Weather.sunny,
  );
}
final weather = Weather.cloudy;

String serialized = weather.serialize();
print(serialized);

Weather deserialized = Weather.deserialize(serialized);
print(deserialized);
cloudy
Weather.cloudy

Adding Mixins

If you have a bunch of different enums where you’re repeating the same logic, you can simplify your code by adding a mixin.

enum Fruit with Describer {
  cherry,
  peach,
  banana,
}

enum Vegetable with Describer {
  carrot,
  broccoli,
  spinach,
}

mixin Describer on Enum {
  void describe() {
    print('This $runtimeType is a $name.');
  }
}
final fruit = Fruit.banana;
final vegi = Vegetable.broccoli;

fruit.describe();
vegi.describe();
This Fruit is a banana.
This Vegetable is a broccoli.

Using Generics

Normally, all the values in an enum will be of the same type. For example, in the Size enum below, the value of each enum item is an int:

enum Size {
  small(1),
  medium(5),
  large(10);

  const Size(this.value);
  final int value;
}
enum Default {
  font,
  size,
  weight,
}
enum Default<T extends Object> {
  font<String>('roboto'),
  size<double>(17.0),
  weight<int>(400);

  const Default(this.value);
  final T value;
}
String defaultFont = Default.font.value;
double defaultSize = Default.size.value;
int defaultWeight = Default.weight.value;

Challenges

Before moving on, here are some challenges to test your knowledge of enhanced enums. It’s best if you try to solve them yourself, but solutions are available with the supplementary materials for this book if you get stuck.

Challenge 1: Better Days Ahead

In this chapter, you wrote a Day enum with the seven days of the week.

Challenge 2: Not Found, 404

Create an enum for HTTP response status codes. The enum should have properties for the code and the meaning. For example, 404 and 'Not Found'. If you aren’t familiar with the HTTP codes, look them up online. You don’t need to cover every possible code, just a few of the common ones.

Key Points

  • Dart enums are subclasses of Enum.
  • Enums are good for representing a fixed number of options.
  • Prefer enums over strings or integers as option markers.
  • Enhanced enums support constructors, properties, methods, operator overloading, interfaces, mixins and generics.
  • Enums must have const constructors.
  • The enum values are instances of the enum class.
  • Operator overloading allows classes to give their own definitions to some operators.
Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum here.
© 2024 Kodeco Inc.

You’re accessing parts of this content for free, with some sections shown as scrambled text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.

Unlock now