Enum from String - dart

I have an Enum and a function to create it from a String because i couldn't find a built in way to do it
enum Visibility{VISIBLE,COLLAPSED,HIDDEN}
Visibility visibilityFromString(String value){
return Visibility.values.firstWhere((e)=>
e.toString().split('.')[1].toUpperCase()==value.toUpperCase());
}
//used as
Visibility x = visibilityFromString('COLLAPSED');
but it seems like i have to rewrite this function for every Enum i have, is there a way to write the same function where it takes the Enum type as parameter? i tried to but i figured out that i can't cast to Enum.
//is something with the following signiture actually possible?
dynamic enumFromString(Type enumType,String value){
}

Mirrors aren't always available, but fortunately you don't need them. This is reasonably compact and should do what you want.
enum Fruit { apple, banana }
// Convert to string
String str = Fruit.banana.toString();
// Convert to enum
Fruit f = Fruit.values.firstWhere((e) => e.toString() == 'Fruit.' + str);
assert(f == Fruit.banana); // it worked
Thanks to #frostymarvelous for correcting the answer

As from Dart version 2.15, you can lookup an enum value by name a lot more conveniently, using .values.byName or using .values.asNameMap():
enum Visibility {
visible, collapsed, hidden
}
void main() {
// Both calls output `true`
print(Visibility.values.byName('visible') == Visibility.visible);
print(Visibility.values.asNameMap()['visible'] == Visibility.visible);
}
You can read more about other enum improvements in the official Dart 2.15 announcement blog post.

My solution is identical to Rob C's solution but without string interpolation:
T enumFromString<T>(Iterable<T> values, String value) {
return values.firstWhere((type) => type.toString().split(".").last == value,
orElse: () => null);
}
Null safe example using firstWhereOrNull() from the collection package
static T? enumFromString<T>(Iterable<T> values, String value) {
return values.firstWhereOrNull((type) => type.toString().split(".").last == value);
}

Update:
void main() {
Day monday = Day.values.byName('monday'); // This is all you need
}
enum Day {
monday,
tuesday,
}
Old solution:
Your enum
enum Day {
monday,
tuesday,
}
Add this extension (need a import 'package:flutter/foundation.dart';)
extension EnumEx on String {
Day toEnum() => Day.values.firstWhere((d) => describeEnum(d) == toLowerCase());
}
Usage:
void main() {
String s = 'monday'; // String
Day monday = s.toEnum(); // Converted to enum
}

This is all so complicated I made a simple library that gets the job done:
https://pub.dev/packages/enum_to_string
import 'package:enum_to_string:enum_to_string.dart';
enum TestEnum { testValue1 };
convert(){
String result = EnumToString.parse(TestEnum.testValue1);
//result = 'testValue1'
String resultCamelCase = EnumToString.parseCamelCase(TestEnum.testValue1);
//result = 'Test Value 1'
final result = EnumToString.fromString(TestEnum.values, "testValue1");
// TestEnum.testValue1
}
Update: 2022/02/10
Dart v2.15 has implemented some additional enum methods that may solve your problems.
From here: https://medium.com/dartlang/dart-2-15-7e7a598e508a
Improved enums in the dart:core library
We’ve made a number of convenience additions to the enum APIs in the dart:core library (language issue #1511). You can now get the String value for each enum value using .name:
enum MyEnum {
one, two, three
}
void main() {
print(MyEnum.one.name); // Prints "one".
}
You can also look up an enum value by name:
print(MyEnum.values.byName('two') == MyEnum.two); // Prints "true".
Finally, you can get a map of all name-value pairs:
final map = MyEnum.values.asNameMap();
print(map['three'] == MyEnum.three); // Prints "true".

Using mirrors you could force some behaviour. I had two ideas in mind. Unfortunately Dart does not support typed functions:
import 'dart:mirrors';
enum Visibility {VISIBLE, COLLAPSED, HIDDEN}
class EnumFromString<T> {
T get(String value) {
return (reflectType(T) as ClassMirror).getField(#values).reflectee.firstWhere((e)=>e.toString().split('.')[1].toUpperCase()==value.toUpperCase());
}
}
dynamic enumFromString(String value, t) {
return (reflectType(t) as ClassMirror).getField(#values).reflectee.firstWhere((e)=>e.toString().split('.')[1].toUpperCase()==value.toUpperCase());
}
void main() {
var converter = new EnumFromString<Visibility>();
Visibility x = converter.get('COLLAPSED');
print(x);
Visibility y = enumFromString('HIDDEN', Visibility);
print(y);
}
Outputs:
Visibility.COLLAPSED
Visibility.HIDDEN

Collin Jackson's solution didn't work for me because Dart stringifies enums into EnumName.value rather than just value (for instance, Fruit.apple), and I was trying to convert the string value like apple rather than converting Fruit.apple from the get-go.
With that in mind, this is my solution for the enum from string problem
enum Fruit {apple, banana}
Fruit getFruitFromString(String fruit) {
fruit = 'Fruit.$fruit';
return Fruit.values.firstWhere((f)=> f.toString() == fruit, orElse: () => null);
}

Here is an alternative way to #mbartn's approach using extensions, extending the enum itself instead of String.
Faster, but more tedious
// We're adding a 'from' entry just to avoid having to use Fruit.apple['banana'],
// which looks confusing.
enum Fruit { from, apple, banana }
extension FruitIndex on Fruit {
// Overload the [] getter to get the name of the fruit.
operator[](String key) => (name){
switch(name) {
case 'banana': return Fruit.banana;
case 'apple': return Fruit.apple;
default: throw RangeError("enum Fruit contains no value '$name'");
}
}(key);
}
void main() {
Fruit f = Fruit.from["banana"];
print("$f is ${f.runtimeType}"); // Outputs: Fruit.banana is Fruit
}
Less tedius, but slower
If O(n) performance is acceptable you could also incorporate #Collin Jackson's answer:
// We're adding a 'from' entry just to avoid having to use Fruit.apple['banana']
// which looks confusing.
enum Fruit { from, apple, banana }
extension FruitIndex on Fruit {
// Overload the [] getter to get the name of the fruit.
operator[](String key) =>
Fruit.values.firstWhere((e) => e.toString() == 'Fruit.' + key);
}
void main() {
Fruit f = Fruit.from["banana"];
print("$f is ${f.runtimeType}"); // Outputs: Fruit.banana is Fruit
}

I use this function, I think it's simple and doesn't need any kind of 'hack':
T enumFromString<T>(List<T> values, String value) {
return values.firstWhere((v) => v.toString().split('.')[1] == value,
orElse: () => null);
}
You can use it like this:
enum Test {
value1,
value2,
}
var test = enumFromString(Test.value, 'value2') // Test.value2

With Dart 2.15 we can now do this which is much cleaner
// Convert to string
String fruitName = Fruit.banana.name;
// Convert back to enum
Fruit fruit = Fruit.values.byName(fruitName);
print(fruit); // Fruit.banana
assert(fruit == Fruit.banana);

Since Dart 2.17 you can solve this elegantly with Enhanced Enums.
(see https://stackoverflow.com/a/71412047/15760132 )
Just add a static method to your enum of choice, like this:
enum Example {
example1,
example2,
example3;
static Example? fromName(String name) {
for (Example enumVariant in Example.values) {
if (enumVariant.name == name) return enumVariant;
}
return null;
}
}
Then you can look for the enum like this:
Example? test = Example.fromName("example1");
print(test); // returns Example.example1

I improved Collin Jackson's answer using Dart 2.7 Extension Methods to make it more elegant.
enum Fruit { apple, banana }
extension EnumParser on String {
Fruit toFruit() {
return Fruit.values.firstWhere(
(e) => e.toString().toLowerCase() == 'fruit.$this'.toLowerCase(),
orElse: () => null); //return null if not found
}
}
main() {
Fruit apple = 'apple'.toFruit();
assert(apple == Fruit.apple); //true
}

I had the same problem with building objects from JSON. In JSON values are strings, but I wanted enum to validate if the value is correct. I wrote this helper which works with any enum, not a specified one:
class _EnumHelper {
var cache = {};
dynamic str2enum(e, s) {
var o = {};
if (!cache.containsKey(e)){
for (dynamic i in e) {
o[i.toString().split(".").last] = i;
}
cache[e] = o;
} else {
o = cache[e];
}
return o[s];
}
}
_EnumHelper enumHelper = _EnumHelper();
Usage:
enumHelper.str2enum(Category.values, json['category']);
PS. I did not use types on purpose here. enum is not type in Dart and treating it as one makes things complicated. Class is used solely for caching purposes.

Generalising #CopsOnRoad's solution to work for any enum type,
enum Language { en, ar }
extension StringExtension on String {
T toEnum<T>(List<T> list) => list.firstWhere((d) => d.toString() == this);
}
String langCode = Language.en.toString();
langCode.toEnum(Language.values);

Simplified version:
import 'package:flutter/foundation.dart';
static Fruit? valueOf(String value) {
return Fruit.values.where((e) => describeEnum(e) == value).first;
}
Using the method describeEnum helps you to avoid the usage of the split to get the name of the element.

You can write getEnum like below, getEnum will go through enum values and returns the first enum that is equal to the desired string.
Sample getEnum(String name) => Sample.values.firstWhere(
(v) => v.name.toLowerCase() == name.toLowerCase(),
orElse: () => throw Exception('Enum value not found.'),
);
enum SampleEnum { first, second, third }
UPDATE
also, you can use this:
final SampleEnum nameEnum = SampleEnum.values.byName('second'); // SampleEnum.second
Usage:
void main() {
print(getEnum('first'));
}

In the latest version of Dart, enum can support custom fields and methods. So the most modern way to do this, is to write a custom field for name/label, and a static parser function.
For example:
enum Foo {
a('FIRST'),
b('SECOND'),
c('THIRD'),
unknown('UNKNOWN'); // make sure the last element ends in `;`
final String label; // define a private field
const Foo(this.label); // constructor
static Foo fromString(String label) { // static parser method
return values.firstWhere(
(v) => v.label == label,
orElse: () => Foo.unknown,
);
}
}
Sample Usage:
final foo = Foo.fromString('FIRST'); // will get `Foo.a`

There are a couple of enums packages which allowed me to get just the enum string rather than the type.value string (Apple, not Fruit.Apple).
https://pub.dartlang.org/packages/built_value (this is more up to date)
https://pub.dartlang.org/packages/enums
void main() {
print(MyEnum.nr1.index); // prints 0
print(MyEnum.nr1.toString()); // prints nr1
print(MyEnum.valueOf("nr1").index); // prints 0
print(MyEnum.values[1].toString()) // prints nr2
print(MyEnum.values.last.index) // prints 2
print(MyEnum.values.last.myValue); // prints 15
}

Here is the function that converts given string to enum type:
EnumType enumTypeFromString(String typeString) => EnumType.values
.firstWhere((type) => type.toString() == "EnumType." + typeString);
And here is how you convert given enum type to string:
String enumTypeToString(EnumType type) => type.toString().split(".")[1];

Generalizing on #Pedro Sousa's excellent solution, and using the built-in describeEnum function:
extension StringExtension on String {
T toEnum<T extends Object>(List<T> values) {
return values.singleWhere((v) => this.equalsIgnoreCase(describeEnum(v)));
}
}
Usage:
enum TestEnum { none, test1, test2 }
final testEnum = "test1".toEnum(TestEnum.values);
expect(testEnum, TestEnum.test1);

import 'package:collection/collection.dart';
enum Page {
login,
profile,
contact,
}
Widget page(String key){
Page? link = Page.values.firstWhereOrNull((e) => e.toString().split('.').last == key);
switch (link) {
case Page.login:
return LoginView();
case Page.profile:
return const ProfileView();
case Page.contact:
return const ContactView();
default:
return const Empty();
}
}

#Collin Jackson has a very good answer IMO. I had used a for-in loop to achieve a similar result prior to finding this question. I am definitely switching to using the firstWhere method.
Expanding on his answer this is what I did to deal with removing the type from the value strings:
enum Fruit { apple, banana }
class EnumUtil {
static T fromStringEnum<T>(Iterable<T> values, String stringType) {
return values.firstWhere(
(f)=> "${f.toString().substring(f.toString().indexOf('.')+1)}".toString()
== stringType, orElse: () => null);
}
}
main() {
Fruit result = EnumUtil.fromStringEnum(Fruit.values, "apple");
assert(result == Fruit.apple);
}
Maybe someone will find this useful...

I had the same problem in one of my projects and existing solutions were not very clean and it didn't support advanced features like json serialization/deserialization.
Flutter natively doesn't currently support enum with values, however, I managed to develop a helper package Vnum using class and reflectors implementation to overcome this issue.
Address to the repository:
https://github.com/AmirKamali/Flutter_Vnum
To answer your problem using Vnum, you could implement your code as below:
#VnumDefinition
class Visibility extends Vnum<String> {
static const VISIBLE = const Visibility.define("VISIBLE");
static const COLLAPSED = const Visibility.define("COLLAPSED");
static const HIDDEN = const Visibility.define("HIDDEN");
const Visibility.define(String fromValue) : super.define(fromValue);
factory Visibility(String value) => Vnum.fromValue(value,Visibility);
}
You can use it like :
var visibility = Visibility('COLLAPSED');
print(visibility.value);
There's more documentation in the github repo, hope it helps you out.

When migrating to null-safety, the Iterable.firstWhere method no longer accepts orElse: () => null. Here is the implementation considering the null-safety:
import 'package:collection/collection.dart';
String enumToString(Object o) => o.toString().split('.').last;
T? enumFromString<T>(String key, List<T> values) => values.firstWhereOrNull((v) => key == enumToString(v!));

enum Fruit { orange, apple }
// Waiting for Dart static extensions
// Issue https://github.com/dart-lang/language/issues/723
// So we will be able to Fruit.parse(...)
extension Fruits on Fruit {
static Fruit? parse(String raw) {
return Fruit.values
.firstWhere((v) => v.asString() == raw, orElse: null);
}
String asString() {
return this.toString().split(".").last;
}
}
...
final fruit = Fruits.parse("orange"); // To enum
final value = fruit.asString(); // To string

I think my approach is slightly different, but might be more convenient in some cases. Finally, we have parse and tryParse for enum types:
import 'dart:mirrors';
class Enum {
static T parse<T>(String value) {
final T result = (reflectType(T) as ClassMirror).getField(#values)
.reflectee.firstWhere((v)=>v.toString().split('.').last.toLowerCase() == value.toLowerCase()) as T;
return result;
}
static T tryParse<T>(String value, { T defaultValue }) {
T result = defaultValue;
try {
result = parse<T>(value);
} catch(e){
print(e);
}
return result;
}
}
EDIT: this approach is NOT working in the Flutter applications, by default mirrors are blocked in the Flutter because it causes the generated packages to be very large.

enum in Dart just has too many limitations. The extension method could add methods to the instances, but not static methods.
I really wanted to be able to do something like MyType.parse(myString), so eventually resolved to use manually defined classes instead of enums. With some wiring, it is almost functionally equivalent to enum but could be modified more easily.
class OrderType {
final String string;
const OrderType._(this.string);
static const delivery = OrderType._('delivery');
static const pickup = OrderType._('pickup');
static const values = [delivery, pickup];
static OrderType parse(String value) {
switch (value) {
case 'delivery':
return OrderType.delivery;
break;
case 'pickup':
return OrderType.pickup;
break;
default:
print('got error, invalid order type $value');
return null;
}
}
#override
String toString() {
return 'OrderType.$string';
}
}
// parse from string
final OrderType type = OrderType.parse('delivery');
assert(type == OrderType.delivery);
assert(type.string == 'delivery');

another variant, how it might be solved:
enum MyEnum {
value1,
value2,
}
extension MyEnumX on MyEnum {
String get asString {
switch (this) {
case MyEnum.value1:
return _keyValue1;
case MyEnum.value2:
return _keyValue2;
}
throw Exception("unsupported type");
}
MyEnum fromString(String string) {
switch (string) {
case _keyValue1:
return MyEnum.value1;
case _keyValue2:
return MyEnum.value2;
}
throw Exception("unsupported type");
}
}
const String _keyValue1 = "value1";
const String _keyValue2 = "value2";
void main() {
String string = MyEnum.value1.asString;
MyEnum myEnum = MyEnum.value1.fromString(string);
}

enum HttpMethod { Connect, Delete, Get, Head, Options, Patch, Post, Put, Trace }
HttpMethod httpMethodFromString({#required String httpMethodName}) {
assert(httpMethodName != null);
if (httpMethodName is! String || httpMethodName.isEmpty) {
return null;
}
return HttpMethod.values.firstWhere(
(e) => e.toString() == httpMethodName,
orElse: () => null,
);
}

You can do something like this:
extension LanguagePreferenceForString on String {
LanguagePreferenceEntity toLanguagePrerence() {
switch (this) {
case "english":
return LanguagePreferenceEntity.english;
case "turkish":
return LanguagePreferenceEntity.turkish;
default:
return LanguagePreferenceEntity.english;
}
}
}

Related

How to get an enum from a String?

Minimal reproducible code:
abstract class FooEnum extends Enum {
// Some abstract methods...
}
enum One implements FooEnum { a, b }
enum Two implements FooEnum { x, y }
FooEnum getFooEnum(String string) {
// Too much boiler plate code, how to do it in a better way?
if (string == 'One.a') return One.a;
else if (...) // other cases.
}
Right now I'm doing it manually (error prone). So, how can I get an enum from a String?
If you only want to use pure dart without flutter or any packages you could do this:
FooEnum? getFooEnum(String string) {
final classValue = string.split('.');
if (classValue.length != 2) {
return null;
}
try {
switch (classValue[0]) {
case 'One':
return One.values.byName(classValue[1]);
case 'Two':
return Two.values.byName(classValue[1]);
}
} on ArgumentError {
return null;
}
return null;
}
With the collection package you could do this:
FooEnum? getFooEnum(String string) {
return (One.values.firstWhereOrNull((e) => e.toString() == string) ??
Two.values.firstWhereOrNull((e) => e.toString() == string)) as FooEnum?;
}
I haven't looked into why the cast is needed, but it was a quick way to fix the problem that occures without it.

Dart explicit operators like C#?

In C# you can do an explicit operator as a static function in a class so that if you know how to convert from one type to another that normally wouldn't work you can have your class do it and it just works anywhere it expects the other type you can just put in the first type.
Does Dart have this?
This is not the same but you can implement it if you need it.
Errors can only be detected at runtime.
void main() {
final str1 = '123';
final int i = str1.castTo();
print(i);
final str2 = 'true';
final bool b = str2.castTo();
print(b);
}
extension Cast on String {
T castTo<T>() {
switch (T) {
case int:
return int.parse(this) as T;
case bool:
switch (this) {
case 'false':
return false as T;
case 'true':
return true as T;
}
}
throw StateError('Unable to convert to type: $T');
}
}
Output:
123
true

Need help understanding => symbol in Dart

I came across the following Dart code while reading about first where. I wanted to understand what exactly is happening in this code as this is some syntax I am not familiar with, particularly the "=>" symbols.
void main() {
final list = List<Book>.generate(10, (id) => Book(id));
Book findBook(int id) => list.firstWhere((book) => book.id == id);
print(findBook(2).name);
print(findBook(4).name);
print(findBook(6).name);
}
class Book {
final int id;
String get name => "Book$id";
Book(this.id);
}
This is what is called Syntaxic sugar. It does not provide a special functionality but makes it easier for developer to write and read code.
In the cas of the => symbol, it is a shortcut for a function containing only a return statement. So those two definitions are exactly the same:
String main() {
return "Hello World"
}
String main() => "Hello World";
Note however, how the second one is way more readable.
So in your case if we unwrap everything your code would become:
void main() {
final list = List<Book>.generate(10, (id) {
return Book(id);
});
Book findBook(int id) {
return list.firstWhere((book) {
return book.id == id;
});
}
print(findBook(2).name);
print(findBook(4).name);
print(findBook(6).name);
}
class Book {
final int id;
String get name {
return "Book$id";
}
Book(this.id);
}

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

How to do lazy evaluation in Dart?

Is there a native (language supported) lazy evaluation syntax? Something like lazy val in Scala.
I've gone through the docs, and could not find anything. There is only a chapter about "lazily loading a library", but it's not what I am asking.
Based on this research I incline to believe (please correct me if I'm wrong) that currently there is no such thing. But maybe you know of any plans or feature requests which will provide the functionality? Or maybe it was considered and rejected by the Dart team?
If indeed there is no native support for this, then what is the best practice (best syntax) for implementing lazy evaluation? An example would be appreciated.
Edit:
The benefits of the feature that I am looking for are mostly the same as in implementation in other languages: Scala's lazy val or C#'s Lazy<T> or Hack's __Memorize attribute:
concise syntax
delayed computation until the value is needed
cache the result (the by-need laziness)
don't break pure functional paradigm (explanation below)
A simple example:
class Fibonacci {
final int n;
int _res = null;
int get result {
if (null == _res) {
_res = _compute(this.n);
}
return _res;
}
Fibonacci(this.n);
int _compute(n) {
// ...
}
}
main(List<String> args) async {
print(new Fibonacci(5).result);
print(new Fibonacci(9).result);
}
The getter is very verbose and has a repetitive code. Moreover I can't make the constructor const because the caching variable _res has to be computed on demand. I imagine that if I had a Scala-like lazy feature then I would also have language support for having a constant constructor. That's thanks to the fact, that the lazy evaluated _res is referentially transparent, and would not be in the way.
class Fibonacci {
final int n;
int lazy result => _compute(this.n);
const Fibonacci(this.n); // notice the `const`
int _compute(n) {
// ...
}
}
main(List<String> args) async {
// now these makes more sense:
print(const Fibonacci(5).result);
print(const Fibonacci(9).result);
}
Update 2021
Lazy initialization is now part of dart from the release 2.12.
Simply add late modifier to the variable declaration
late MyClass obj = MyClass();
And this object will be initialized only when it is first used.
From the docs:
Dart 2.12 added the late modifier, which has two use cases:
Declaring a non-nullable variable that’s initialized after its
declaration.
Lazily initializing a variable.
Checkout the example here:
https://dartpad.dev/?id=50f143391193a2d0b8dc74a5b85e79e3&null_safety=true
class A {
String text = "Hello";
A() {
print("Lazily initialized");
}
sayHello() {
print(text);
}
}
class Runner {
late A a = A();
run() async {
await Future.delayed(Duration(seconds: 3));
print("First message");
a.sayHello();
}
}
Here class A will be initialized only after "First message" has been displayed.
update2
From #lrn s comment - using an Expando for caching makes it work with const:
class Lazy<T> {
static final _cache = new Expando();
final Function _func;
const Lazy(this._func);
T call() {
var result = _cache[this];
if (identical(this, result)) return null;
if (result != null) return result;
result = _func();
_cache[this] = (result == null) ? this : result;
return result;
}
}
defaultFunc() {
print("Default Function Called");
return 42;
}
main([args, function = const Lazy(defaultFunc)]) {
print(function());
print(function());
}
Try it in DartPad
update
A reusable Lazy<T> could look like below in Dart but that also doesn't work with const and can't be used in field initializers if the calculation needs to refer instance members (this.xxx).
void main() {
var sc = new SomeClass();
print('new');
print(sc.v);
}
class SomeClass {
var _v = new Lazy<int>(() {
print('x');
return 10;
});
int get v => _v();
}
class Lazy<T> {
final Function _func;
bool _isEvaluated = false;
Lazy(this._func);
T _value;
T call() {
if(!_isEvaluated) {
if(_func != null) {
_value = _func();
}
_isEvaluated = true;
}
return _value;
}
}
Try it in DartPad
original
Dart version of http://matt.might.net/articles/implementing-laziness/ using a closure to lazy evaluate:
void main() {
var x = () {
print ("foo");
return 10;
}();
print("bar");
print(x);
// will print foo, then bar then 10.
print('===');
// But, the following Scala program:
x = () {
print("foo");
return 10;
};
print ("bar");
print (x());
// will print bar, then foo, then 10, since it delays the computation of x until it’s actually needed.
}
Try it in DartPad
Update
int _val;
int get val => _val ??= 9;
Thanks #Nightscape
Old
I think this little snippet might help you...
int _val;
int get val => _val ?? _val = 9;

Resources