CatOverflow
Posted on December 4, 2022
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
}
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();
}
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");
}
}
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.
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
}
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
}
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
}
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;
}
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
}
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());
}
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 thehasCode
getters, as well as the== operator
- A static or instance member named
values
are not allowed, as it would conflict with the default staticvalues
getter.
Posted on December 4, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.