How can I build an enum with Dart? [duplicate] - dart

This question already has answers here:
Does Dart support enumerations?
(8 answers)
Closed 7 years ago.
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?

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;
}
}

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

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;
}

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);

Related

A map functions? don't understand this dart

I see this source from google_fonts of dart pub. It seems we have a function map? and asMap() has a arrow function? Don't understand this.
static Map<
String,
TextStyle Function({
TextStyle? textStyle,
Color? color,
Color? backgroundColor,
double? fontSize,
FontWeight? fontWeight,
FontStyle? fontStyle,
double? letterSpacing,
double? wordSpacing,
TextBaseline? textBaseline,
double? height,
Locale? locale,
Paint? foreground,
Paint? background,
List<ui.Shadow>? shadows,
List<ui.FontFeature>? fontFeatures,
TextDecoration? decoration,
Color? decorationColor,
TextDecorationStyle? decorationStyle,
double? decorationThickness,
})> asMap() => const {
'ABeeZee': GoogleFonts.aBeeZee,
'Abel': GoogleFonts.abel}
You're seeing a generated code, in the very beginning of the file:
// GENERATED CODE - DO NOT EDIT
// Copyright 2019 The Flutter team. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
This function is defined as:
Name: asMap.
Scope: top-level (Since it is a static method).
Return type: Map<String, TextStyle Function({ ...args })>.
It seems we have a function map?
We have a function that returns a map of Strings and Closures. In Dart this is perfectly fine:
final Map<String, int Function(int, int)> operations = {
'sum': (int a, int b) => a + b,
'subtract': (int a, int b) => a - b,
'multiply': (int a, int b) => a * b,
'divide': (int a, int b) => a * b
};
operations['sum']!(30, 30); // 60
operations['subtract']!(30, 30); // 0
operations['multiply']!(30, 30); // 900
operations['divide']!(30, 30); // 1
In general this is not something we do by raw hands due to type implications (hard to maintain, you can see that it's pretty confusing + it's pretty easy to lost track of type definitions and, consequently, all intellisense features). So that's why this is being done through code generation.
And in this particular case, we are generating a map, like this example below:
class Arithmetic {
static int sum(int a, int b) {
return a + b;
}
static int subtract(int a, int b) {
return a - b;
}
static int multiply(int a, int b) {
return a * b;
}
static int divide(int a, int b) {
return a * b;
}
static Map<String, int Function(int, int)> asMap() {
const Map<String, int Function(int, int)> operationsAsMap = {
'sum': sum,
'subtract': subtract,
'multiply': multiply,
'divide': divide
};
return operationsAsMap;
}
}
asMap() has a arrow function?
No, the asMap() implementation is using an arrow function, which is an alias for:
// This is a shorthand...
static Map<...> asMap() => const { ... };
// For this:
static Map<...> asMap() {
return const { ... };
}

Difference between const constructor and cost?

I have the following code snippet:
class ImmutablePoint {
final double x, y;
const ImmutablePoint(this.x, this.y);
}
void main() {
var i = const ImmutablePoint(4,6);
print(i.x);
}
As you can see, a const constructor is defined in the class. However, what is the difference between const and const constructor?
What is the difference when I change from var i = const ImmutablePoint(4,6); to var i = ImmutablePoint(4,6);?
The point is, sometimes I see in flutter example const Text("FFF"), although a const constructor is already defined for the text.
A class with a const constructor only allows you to create const instances of that class, but it does not make all instances const.
That is controlled at the constructor invocation site - the const constructor is only used within a const context. A const context is basically inferred by any precedingconst keyword, the following all would use the const constructor:
var foo = const ImmutablePoint(1,2);
const foo = ImmutablePoint(1,2);
var foos = const [ImmutablePoint(1,2)];
But the following would not, as it is not in a const context:
var foo = ImmutablePoint(1,2);

How to extend from const constructor

class and const gives a nice possibility to define structured constants. Now I try to extend from one, works ok. Try to use a constant from the inherited class, it works, still I only manage to get through with final instead of const. No big deal in my project, but is it possible to have it const?
class A {
final String name;
final int value;
const A(final String this.name, final this.value);
static const A ONE = const A('One', 1);
}
class B extends A {
const B(final String name, final int val) : super(name, val);
static const B B_ONE = const B('One', 1);//Ok
static const A A_ONE = A.ONE;//Ok
static final B BA_ONE = new B(A.ONE.name, A.ONE.value);//Ok
static const B BA_ONE_CONST = const B(A.ONE);//No B constructor!?!
}
Your B class indeed does not have a constructor that takes a single A instance. You haven't declared one, and you can't do it directly without a little workaround.
The problem is that B extends A, it doesn't just contain an A instance. So, if you want to start with an A instance (like A.ONE) and create a B instance from it, where B extends A and has its own name and value fields, you'll have to extract the name and value from the A to create the new object ... and you can't do property extraction in a const expression. So, that's a no-go.
What you can do is to have a different implementation of B that gets its values directly from an A instance, and then create a constant from that:
class B extends A {
const B(String name, int value) : super(name, value);
const factory B.fromA(A instance) = _SecretB;
...
static const B BA_ONE_CONST = const B.fromA(A.ONE);
}
class _SecretB implements B {
final A _instance;
const _SecretB(this._instance);
String get name => _instance.name;
int get value => _instance.value;
}
If you are the only one using the B.fromA constructor, you could just remove it and call the _SecretB constructor directly. If you want to expose it to other clients of your class, you can make it a const factory constructor like here.

How do I add Methods or Values to Enums in Dart?

In Java when you are defining an enum, you can do something similar to the following, i.e. add members to an enum. Is this possible in Dart?
enum Foo {
one(1), two(2);
final num value;
Foo(this.value);
}
Starting with Dart 2.6 you can define extensions on classes (Enums included).
enum Cat {
black,
white
}
extension CatExtension on Cat {
String get name {
switch (this) {
case Cat.black:
return 'Mr Black Cat';
case Cat.white:
return 'Ms White Cat';
default:
return null;
}
}
void talk() {
print('meow');
}
}
Example:
Cat cat = Cat.black;
String catName = cat.name;
cat.talk();
Here's one more live example (uses a constant map instead of a switch):
https://dartpad.dartlang.org/c4001d907d6a420cafb2bc2c2507f72c
Dart Enhanced Enum Classes
Starting with Dart 2.17, the Enhanced Enum Classes feature has been introduced. With that, the example from the question would look like this:
enum Foo {
one(1),
two(2);
const Foo(this.value);
final num value;
}
Now, you can just use the enum class like this:
void main() {
const foo = Foo.one;
print(foo.value); // 1
}
Note that you need to update your SDK constraint as the feature requires Dart 2.17:
environment:
sdk: '>=2.17.0-0 <3.0.0'
Adding members
With enhanced enums, you can add any member to your enum as long as the constructor is const.
This also means that you can add getters or methods to existing enums, for example:
enum Cake {
cherry,
apple,
strawberry;
String get description => '$name cake';
}
Generics
Enhanced enum classes also enable you to use generics for you enums. If you combine this with members, you can do the following:
enum Bar<T extends Object> {
number<int>(42),
name<String>('creativecreatorormaybenot'),
baz(true); // Note that type inference also works.
const Bar(this.value);
final T value;
}
Mixins and interfaces
In addition to declaring members, you can also mixin mixins and implement interfaces with enhanced enums and override any missing implementations.
mixin Foo {
int get n;
}
abstract class Bar {
void printNumber();
}
enum Baz with Foo implements Bar {
one(1),
two(2);
const Baz(this.n);
#override
final int n;
#override
void printNumber() => print(n);
}
Multiple arguments
Finally note that even if I did not make use of it in any of the examples above, it is possible to have an arbitrary number of arguments (and an initializer list):
enum Foo {
bar(42, description: 'The answer to life, the universe, and everything.'),
baz(0, enabled: false, description: 'noop');
const Foo(
int number, {
this.enabled = true,
required this.description,
}) : n = number;
final int n;
final bool enabled;
final String description;
}
Dart enums are used only for the simplest cases. If you need more powerful or more flexible enums, use classes with static const fields like shown in https://stackoverflow.com/a/15854550/217408
This way you can add whatever you need.
Nope. In Dart, enums can only contain the enumerated items:
enum Color {
red,
green,
blue
}
However, each item in the enum automatically has an index number associated with it:
print(Color.red.index); // 0
print(Color.green.index); // 1
You can get the values by their index numbers:
print(Color.values[0] == Color.red); // True
See: https://www.dartlang.org/guides/language/language-tour#enums
It may not be "Effective Dart" , I add a static method inside a Helper class ( there is no companion object in Dart) .
In your color.dart file
enum Color {
red,
green,
blue
}
class ColorHelper{
static String getValue(Color color){
switch(color){
case Color.red:
return "Red";
case Color.green:
return "Green";
case Color.blue:
return "Blue";
default:
return "";
}
}
}
Since the method is in the same file as the enum, one import is enough
import 'package:.../color.dart';
...
String colorValue = ColorHelper.getValue(Color.red);
extension is good, but it cannot add static methods. If you want to do something like MyType.parse(string), consider using a class with static const fields instead (as Günter Zöchbauer suggested before).
Here is an example
class PaymentMethod {
final String string;
const PaymentMethod._(this.string);
static const online = PaymentMethod._('online');
static const transfer = PaymentMethod._('transfer');
static const cash = PaymentMethod._('cash');
static const values = [online, transfer, cash];
static PaymentMethod parse(String value) {
switch (value) {
case 'online':
return PaymentMethod.online;
break;
case 'transfer':
return PaymentMethod.transfer;
break;
case 'cash':
return PaymentMethod.cash;
default:
print('got error, invalid payment type $value');
return null;
}
}
#override
String toString() {
return 'PaymentMethod.$string';
}
}
I found this much handier than using a helper function.
final method = PaymentMethod.parse('online');
assert(method == PaymentMethod.online);
I did this (inspired form the accepted answer by #vovahost)
enum CodeVerifyFlow {
SignUp, Recovery, Settings
}
extension CatExtension on CodeVerifyFlow {
String get name {
return ["sign_up", "recovery", "settings"][index];
}
}
// use it like
CodeVerifyFlow.SignUp.name
thank me later!
There's an upcoming feature in Dart known as enhanced enums, and it allows for enum declarations with many of the features known from classes. For example:
enum Blah {
one(1), two(2);
final num value;
const Blah(this.value);
}
The feature is not yet released (and note that several things are not yet working), but experiments with it can be performed with a suitably fresh version of the tools by passing --enable-experiment=enhanced-enums.
The outcome is that Blah is an enum declaration with two values Blah.one and Blah.two, and we have Blah.one.value == 1 and Blah.two.value == 2. The current bleeding edge handles this example in the common front end (so dart and dart2js will handle it), but it is not yet handled by the analyzer.
As an improvement on the other suggestions of using Extensions, you can define your assigned values in a list or map, and the extension will be concise.
enum Numbers {
one,
two,
three,
}
// Numbers.one.value == 1
// Numbers.two.value == 2
// Numbers.three.value == 3
example with list
extension NumbersExtensionList on Numbers {
static const values = [1, 2, 3];
int get value => values[this.index];
}
example with map
extension NumbersExtensionMap on Numbers {
static const valueMap = const {
Numbers.one: 1,
Numbers.two: 2,
Numbers.three: 3,
};
int get value => valueMap[this];
}
Note: This approach has the limitation that you can not define a static factory method on the Enum, e.g. Numbers.create(1) (as of Dart 2.9). You can define this method on the NumbersExtension, but it would need to be called like NumbersExtension.create(1)
For String returns :
enum Routes{
SPLASH_SCREEN,
HOME,
// TODO Add according to your context
}
String namedRoute(Routes route){
final runtimeType = '${route.runtimeTypes.toString()}.';
final output = route.toString();
return output.replaceAll(runtimeType, "");
}
You can add extra fields and methods with my package enum_extendable.
It generates extensions on enum, so you can use your enum values in the similar way to instances of a regular Dart class.
For example, if you have enum MathOperator { plus, minus } the symbol and calculate(...) can be added to it.
So, the enum can be used in such way:
final n1 = 1;
final n2 = 2.0;
MathOperator.values.forEach((operator) {
print('$n1 ${operator.symbol} $n2 = ${operator.calculate(n1, n2)}');
});
Usage:
Add dependencies to pubspec.yaml:
dependencies:
enum_extendable_annotation:
dev_dependencies:
build_runner:
enum_extendable_generator:
Install these dependencies:
# Dart
pub get
# Flutter
flutter packages get
Add imports to your enum file:
import 'package:enum_extendable_annotation/enum_extendable_annotation.dart';
part '<your enum file name>.enum_extendable.g.dart';
Create a PODO class with fields and methods you wanted.
Create a map with instances of this PODO class for each enum value.
Annotate elements:
the enum with #ExtendableEnum();
the PODO class - #ExtendableEnumPodo();
the map of PODO instances - #ExtendableEnumValues().
Run code generator:
if your package depends on Flutter:
flutter pub run build_runner build
if your package does not depend on Flutter:
dart pub run build_runner build
The file with extensions should be generated.
Example of the enum file:
import 'package:enum_extendable_annotation/enum_extendable_annotation.dart';
part 'math_operator.enum_extendable.g.dart';
#ExtendableEnum()
enum MathOperator { plus, minus }
#ExtendableEnumPodo()
class _MathOperatorPodo {
final String symbol;
final num Function(num, num) calculate;
_MathOperatorPodo(
this.symbol,
this.calculate,
);
#ExtendableEnumValues()
static final Map<MathOperator, _MathOperatorPodo> _values = {
MathOperator.plus: _MathOperatorPodo(
'+',
(n1, n2) => n1 + n2,
),
MathOperator.minus: _MathOperatorPodo(
'-',
(n1, n2) => n1 - n2,
),
};
}

print address of object

Is there any trick to print the address of a dart object?
I'm having an issue in which I have a standard enum class as described in Does Dart support enumerations?.
class Access {
static const IA = const Access._(0);
static const RO = const Access._(1);
static const RW = const Access._(2);
final int value;
const Access._(this.value);
...
}
The variable access is of type Access and the value is 1, yet access==Access.RO is returning false.
var access = _.access;
print('''
access => ($access, ${access.runtimeType}, ${access.value})
static => (${Access.RO}, ${Access.RO.runtimeType}, ${Access.RO.value})
equal ? => ${(access == Access.RO)}
''');
prints
access => (RO, Access, 1)
static => (RO, Access, 1)
equal ? => false
If I provide an operator==(Access other) that compares the values it returns the expected value. So, I figured maybe this has to do with coming at the class from different libraries (maybe isolate related) and if I could print the address of access and Access.RO I could see if they are different. Of course, if they were different I'd then need to know why as well.
When you deal with const you have to be very careful. If you use new Access._(0) instead of const Access._(0) you will not get the same object. Here is an example :
class Access {
static const IA = const Access._(0);
static const RO = const Access._(1);
static const RW = const Access._(2);
final int value;
const Access._(this.value);
}
main(){
print(Access.IA == const Access._(0)); // prints true
print(Access.IA == new Access._(0)); // prints false
}
This could explain your problem.
If identical(a, b) returns false you can be sure that their pointers are different. (The inverse is not necessarily true for numbers, but that's an implementation detail).
If you are dealing with isolates you need to be careful when you transmit objects. Even, if they are canonicalized on one side, they won't be, after they have been transmitted to another isolate:
import 'dart:async';
import 'dart:isolate';
class A { const A(); }
foo() {
port.receive((x, _) {
print(x == const A()); // Prints false.
});
}
main() {
var p = new ReceivePort(); // To keep the program running.
spawnFunction(foo).send(const A(), null);
}
Also note that dart2js doesn't allow to transmit arbitrary objects.

Resources