55

The Dart language does not have enums (yet??). What is the proper or idiomatic way to construct an enum, at least until a language feature arrives?

Seth Ladd
  • 112,095
  • 66
  • 196
  • 279

4 Answers4

79

Dart now has support for enums.

The rest of this answer is for Dart <= 1.8. If using > 1.8, use Dart's formal support for enums (explained in another answer).

It's true, the Dart language does not (yet?) have enums. There is an open issue for it.

In the meantime, here is an idiomatic Dart snippet to create your own enum.

class Enum {
  final _value;
  const Enum._internal(this._value);
  toString() => 'Enum.$_value';

  static const FOO = const Enum._internal('FOO');
  static const BAR = const Enum._internal('BAR');
  static const BAZ = const Enum._internal('BAZ');
}

Using const constructors means you can use this enum in a switch. Here's an example:

class Fruits {
  final _value;
  const Fruits._internal(this._value);
  toString() => 'Enum.$_value';

  static const APPLE = const Fruits._internal('APPLE');
  static const PEAR = const Fruits._internal('PEAR');
  static const BANANA = const Fruits._internal('BANANA');
}

void main() {
  var yummy = Fruits.BANANA;

  switch (yummy) {
    case Fruits.APPLE:
      print('an apple a day');
      break;
    case Fruits.PEAR:
      print('genus Pyrus in the family Rosaceae');
      break;
    case Fruits.BANANA:
      print('open from the bottom, it is easier');
      break;
  }
}
Seth Ladd
  • 112,095
  • 66
  • 196
  • 279
  • 11
    Small warning: it is a common error to create a const object without any value. For example "class Enum { const Enum(); }". This wouldn't work because Dart canonicalizes constant objects. So always make sure to have a value that differentiates the individual enum-values. – Florian Loitsch Apr 07 '13 at 11:06
  • 1
    yummy =='BANANA' returns *false*... What a shame... :/ – Anthony Bobenrieth Oct 02 '14 at 04:05
  • Dart has now experimental support for enums: http://news.dartlang.org/2014/11/dart-18-library-improvements-and.html – Fedy2 Dec 15 '14 at 09:09
  • 2
    @AnthonyBobenrieth Yeah. But you can do yummy.value == 'BANANA' if you add a getter for _value. – Amsakanna Jan 19 '19 at 17:52
  • 1
    Is it possible to add a `fromString(String)` constructor? I tried adding a `const factory Fruits.fromString(String s) = _internal;` but it seems to create instances that are not equal to `APPLE`, `PEAR` and `BANANA`... – Magnus Sep 08 '20 at 08:55
  • @MagnusW You would need to override the `==` operator and the `hashCode` getter assuming you would be using `==` for comparison. If that doesn't work for you I don't see an easy way to get the actual static const instances. – Christopher Moore Feb 04 '21 at 02:34
30

With r41815 Dart got native Enum support see http://dartbug.com/21416 and can be used like

enum Status {
  none,
  running,
  stopped,
  paused
}

void main() {
  print(Status.values);
  Status.values.forEach((v) => print('value: $v, index: ${v.index}'));
  print('running: ${Status.running}, ${Status.running.index}');
  print('running index: ${Status.values[1]}');
}

[Status.none, Status.running, Status.stopped, Status.paused]
value: Status.none, index: 0
value: Status.running, index: 1
value: Status.stopped, index: 2
value: Status.paused, index: 3
running: Status.running, 1
running index: Status.running

A limitation is that it is not possibly to set custom values for an enum item, they are automatically numbered.

More details at in this draft https://www.dartlang.org/docs/spec/EnumsTC52draft.pdf

Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
17

I use a little bit simpler version of the Enum class in Dart Web Toolkit:

/**
 * Emulation of Java Enum class.
 *
 * Example:
 *
 * class Meter<int> extends Enum<int> {
 *
 *  const Meter(int val) : super (val);
 *
 *  static const Meter HIGH = const Meter(100);
 *  static const Meter MIDDLE = const Meter(50);
 *  static const Meter LOW = const Meter(10);
 * }
 *
 * and usage:
 *
 * assert (Meter.HIGH, 100);
 * assert (Meter.HIGH is Meter);
 */
abstract class Enum<T> {

  final T _value;

  const Enum(this._value);

  T get value => _value;
}
akserg
  • 436
  • 2
  • 4
  • I like top-level constants for my enums. You can use imports to fix any collisions. This makes using the enums much less verbose. Edit - added an example. – Greg Lowe Apr 06 '13 at 20:52
  • Is it ok to add an method: toString() => value; ? – Rui Lima Jan 09 '14 at 15:05
5

I like top-level constants for my enums. You can use imports to fix any collisions. This makes using the enums much less verbose.

i.e.

if (m == high) {}

instead of:

if (m == Meter.high) {}

Enum definition:

class Meter<int> extends Enum<int> {
   const Meter(int val) : super (val);
}

const Meter high = const Meter(100);
const Meter middle = const Meter(50);
const Meter low = const Meter(10);
Greg Lowe
  • 15,430
  • 2
  • 30
  • 33
  • is this a good idea? I feel like using imports to clear out polluted namespace is not the way to go in a large project – nipunasudha Jul 11 '21 at 11:43