How to use enum in dart

catoverflow

CatOverflow

Posted on December 4, 2022

How to use enum in dart

Basic usage

Enumerated types are very common data type in programming languages. It's used to represent a fixed number of constant values. In dart, you can declare a enumerated type with the enum keyword.

enum Pet { cat, dog, fish }

void main() {
  final values = Pet.values;
  print(values); // [Pet.cat, Pet.dog, Pet.fish]
  print(Pet.cat); // Pet.cat
  print(Pet.dog.index); // 1
  print(Pet.fish == values[2]); // true
}
Enter fullscreen mode Exit fullscreen mode

All enums implicitly extend the Enum class, the desugaring version of code above is:

class Pet extends Enum {  
  static const Pet cat = Pet._$(0, "cat");  
  static const Pet dog = Pet._$(1, "dog");  
  static const Pet fish = Pet._$(2, "fish");  
  static const List<Pet> values = [cat, dog, fish];  
  const Pet._$(int _$index, String _$name) : super._(_$index, _$name);  

  String _$enumToString() => "Pet.${_$name}";
}

abstract class Enum {
  Enum._(this.index, this._name);  
  final int index;
  final String _name;
  String _$enumToString();
  String toString() => _$enumToString();
}
Enter fullscreen mode Exit fullscreen mode

The enum types are designed to be primitive equality, so enums can be used in switch statement.

void main() {
  final pet = Pet.fish;

  switch (pet) {
    case Pet.cat:
      print('Cat');
      break;
    case Pet.dog:
      print('Dog');
      break;
    default:
      print("Fish");
  }
}
Enter fullscreen mode Exit fullscreen mode

The enum types are completely sealed by default. Being sealed means they cannot be subclassed, implemented, mixed in, or otherwise explicitly instantiated.

class FurryPet extends Pet {} 
// Error: 'Pet' is an enum and can't be extended or implemented.
class PocketPet implements Pet {} 
// Error: 'Pet' is an enum and can't be extended or implemented.
mixin PetMixin on Pet {} 
// Error: 'Pet' is an enum and can't be extended or implemented.
const bird = Pet(3, 'Bird'); 
// Error: Enums can't be instantiated.
Enter fullscreen mode Exit fullscreen mode

Quite simple, right? But what if we wanna get the name of a enum value, or get value by name. We need to do something like below

void main() {
  print(Pet.cat.toString().split('.').last); // cat

  // names in non-english
  const names = ['chat', 'chien', 'poisson']; 
  print(names[Pet.fish.index]); // poisson

  // find enum value by name
  final dog = Pet.values.firstWhere((e) => e.toString().split('.').last == 'dog');
  print(dog); // Pet.dog
}
Enter fullscreen mode Exit fullscreen mode

Enum extension

Since dart 2.6, we can add custom methods to enum with extension methods.

enum Color {
  red,
  green,
  blue,
}

extension ColorExt on Color {
  // names in non-english
  String get name {
    switch (this) {
      case Color.red:
        return 'rojo';
      case Color.green:
        return 'verde';
      default:
        return 'azul';
    }
  }

  String get hexCode {
    const hexCodes = ['#FF0000', '#00FF00', '#0000FF'];
    return hexCodes[this.index];
  }
}

void main() {
  print(Color.green.name); // verde
  print(Color.blue.hexCode); // #0000FF
}
Enter fullscreen mode Exit fullscreen mode

As getting the name of a enum value as String is quite often needed, dart 2.15 introduced an official EnumExtension to do the very same thing.

void main() {
  // dart < 2.15
  print(Color.red.toString().split('.').last) // red
  // dart >= 2.15, with official EnumExtension
  print(Color.red.name); // red
}
Enter fullscreen mode Exit fullscreen mode

Enhanced enum

Dart 2.17 shipped with the enhanced enum, which makes enum behaves more like a class. Enhanced enum supports generic and can be declared with fields(static or instance member), methods or implements interfaces and applies mixins.

// enhanced enum syntax
enum Name<T extends Object?> with Mixin1, Mixin2 implements Interface1, Interface2 {
  id1<int>(args1), id2<String>(args2), id3<bool>(args3);
  memberDeclaration*
  const Name(params) : initList;
}
Enter fullscreen mode Exit fullscreen mode

Let's rewrite our previous extension method version Color enum into a enhanced one.

enum Color {
  red('#FF0000', 'Red'),
  green('#00FF00', 'Green'),
  blue('#0000FF', 'Blue');

  const Color(this.hexCode, this.name);

  final String hexCode;
  final String name;

  @override
  String toString() => '$name: $hexCode';
}

void main() {
  final blue = Color.blue;
  print(blue.hexCode); // #00FF00
  print(blue.name); // Blue
  print(blue); // Blue: #00FF00
}
Enter fullscreen mode Exit fullscreen mode

Thanks to this feature the code becomes much more clear to read and easy to maintain.

Let's write a more complex one

mixin Pet {
  keepCompany() => print('keep you company');
}

enum Dog with Pet implements Comparable<Dog> {
  akita.guardDog('Akita', 'japan'), // generate instance with named constructor
  bulldog('Bulldog', 'england', false),
  chihuahua('Chihuahua', 'mexico', false);

  final bool guardDog;
  final String name;
  final String originCountry;

  // generative constructor can only called in the enum 
  const Dog(this.name, this.originCountry, this.guardDog);

  // named constructor with initializers
  const Dog.guardDog(String name, String country)
      : this.name = name,
        this.originCountry = country,
        this.guardDog = true;

  // factory constructor can only return one of the known values
  // redirect to generative constructor is illegal
  factory Dog.japanDog() {
    return values.firstWhere((value) => value.originCountry == 'japan');
  }

  // implements the Comparable interface to make it sortable
  int compareTo(Dog another) => this.index - another.index;

  // override the default toString method
  String toString() => name;
}

void main() {
  List<Dog> dogs = [Dog.bulldog, Dog.chihuahua, Dog.akita];
  dogs.sort((a, b) => a.compareTo(b));
  print(dogs);
  print(Dog.chihuahua.keepCompany());
}
Enter fullscreen mode Exit fullscreen mode

As enum is used for fixed number of constant values, there are some restrictions applied to enhanced enum.

  • Enhanced enum can not extend class other than the Enum class.
  • All generative constructors (constructors that generate instance of class) must be constant. this ensures all enum instances are constant value .
  • Factory constructors can only return one of the fixed, known enum instances.
  • All instances must be declared in the beginning of the enum declaration, and there must be at least one instance(empty enum are useless).
  • Instance member of enum must be final
  • Override the index and the hasCode getters, as well as the == operator
  • A static or instance member named values are not allowed, as it would conflict with the default static values getter.
💖 💪 🙅 🚩
catoverflow
CatOverflow

Posted on December 4, 2022

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related