How to find an element in a dart list - dart

I have a dart list of objects, every of which contains book_id property, and I want to find an element of that list by the book_id field.

Use firstWhere method: https://api.flutter.dev/flutter/dart-core/Iterable/firstWhere.html
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);
}
/*
Output:
Book2
Book4
Book6
*/

Null-Safe approach (handling no-element error)
If the searched item might or might not be in the array, with null safety I personally prefer to use .where on lists - it filters through the array and returns a list.
var myListFiltered = myList.where((e) => e.id == id);
if (myListFiltered.length > 0) {
// Do stuff with myListFiltered.first
} else {
// Element is not found
}
The benefit of this approach is that you'll always get an array and there won't be an issue with element not being found or trying to return null through orElse callback:
The return type 'Null' isn't a 'FooBar', as required by the closure's context.
Refactoring the finder logic into a helper method
Since the task of finding an element by id in a list of objects is quite a common one, I like to extract that logic into helpers file, for example:
class Helpers {
static findById(list, String id) {
var findById = (obj) => obj.id == id;
var result = list.where(findById);
return result.length > 0 ? result.first : null;
}
}
And use it like this:
var obj = Helpers.findById(myList, id);
if (obj == null) return; //? Exn: element is not found

A lot has been said already, but I want to add my best way to do this. if it's possible that the item doesn't exist in the list, import the iterable extension and use firstWhereOrNull1 on the list.
import 'package:collection/src/iterable_extensions.dart';
myList.firstWhereOrNull((item) => item.id == id))

You should add orElse to avoid exeption:
list.firstWhere(
(book) => book.id == id,
orElse: () => null,
});

To avoid having to add orElse to every use of firstWhere, with Dart 2.7+, you can create a handy little extension to handle this:
extension IterableX<T> on Iterable<T> {
T safeFirstWhere(bool Function(T) test) {
final sublist = where(test);
return sublist.isEmpty ? null : sublist.first;
}
}
Which can then be used as such:
final books = List<Book>.generate(10, (id) => Book(id));
final matchingBooks = books.safeFirstWhere((book) => book.id == id);

void main(){
List lights=[
{"id":1,"color":"yellow"},
{"id":2,"color":"green"},
{"id":3,"color":"red"}
];
int searchId = 2;
var colr = lights.firstWhere((x) => x['id'] == searchId, orElse:() => {"color" : "Color Not found"})['color'];
print(colr);
}
The above code searches the obj list, using firstWhere. If not found returns text 'Color not found'

Related

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

Equivalent in Dart

I've just started exploring Dart language and I wanted to test an existing code that I wrote in Java as given:
public interface Condition {
Condition FALSE = facts->false;
Boolean evaluate(Fact<?> fact);
default Condition and(Condition other) {
return fact-> this.evaluate(fact) && other.evaluate(fact);
}
default Condition or(Condition other) {
return fact-> this.evaluate(fact) || other.evaluate(fact);
}
}
And the caller calls it as:
#Test
public void testCondition() {
String str = "A String";
Condition a = fact -> !str.isBlank();
Condition b = fact -> str.contains("A");
a.and(b);
}
A complete test class that makes use of this is :
public class AnonymousLoopTest {
#Test
public void test() {
RulesEngine rulesEngine = new InferenceRuleEngine();
List<Name> names = NamesFactory.fetchNames();
Rules rules = new Rules();
Facts facts = new Facts();
AtomicReference<Integer> countRef = new AtomicReference<>(1);
names.forEach(personName -> {
facts.put("name-" + countRef.get(), personName);
countRef.set(countRef.get()+1);
Condition condition = fact -> !personName.name().isEmpty();
//Hack the comparator logic of DefaultRule/BasicRule in order to override its internal logic as below.
//This is needed to register our Rule with Rules which uses a Set<Rule> to register new Rules
//with the comparator logic written in BasicRule.
Rule nameRule = new RuleBuilder((o1, o2) -> personName.name().compareTo(o1.getName()))
.when(condition).then(action -> System.out.println("In Action:" + personName)).build();
rules.register(nameRule);
});
rulesEngine.fire(rules, facts);
}
}
record Name(Integer id, String name){}
class NamesFactory{
static List<Name> fetchNames(){
return List.of(new Name(10, "Sara"), new Name(20, "Zara"), new Name(30, ""),new Name(40, "Lara"));
}
}
The condition is used by when() method.
In the given example the blank name will be filtered out. The other three names will be printed.
I was trying to write and equivalent in Dart but I'm just stuck. What is the way to write this code in Dart?
That looks like something I'd do by:
typedef Condition = bool Function(Fact);
bool falseCondition(Fact _) => false;
extension ConditionComposition on Condition {
Condition operator &(Condition other) => (Fact fact) => this(fact) && other(fact);
Condition operator |(Condition other) => (Fact fact) => this(fact) || other(fact);
Condition operator ~() => (Fact fact) => !this(fact);
}
If you insist on having a wrapper class for the function objects, I'd do it as:
class Condition {
static const Condition falseCondition = Condition(_kFalse);
final bool Function(Fact) _test;
const Condition(bool test(Fact fact)) : _test = test;
bool evaluate(Fact fact) => _test(fact);
Condition operator &(Condition other) => Condition((fact) =>
this.evaluate(fact) && other.evaluate(fact));
Condition operator |(Condition other) => Condition((fact) =>
this.evaluate(fact) || other.evaluate(fact));
static bool _kFalse(_) => false;
}
but a class seems overkill for something which really is just a simple function. Dart has first class functions.
You can use the former version as:
test("test Condition", () {
var str = "A String";
Condition a = (fact) => str.isNotEmpty();
Condition b = (fact) => str.contains("A");
var both = a & b;
expect(both(someDefaultFact), true);
}

Safe way to access List index

I'm newbee to Dart.
I have troubles to find an easy to read way to "safely" access a List element at any index
final List<String> myList = <String>[]
myList.add("something")
// ...
String myGetter(int index) {
// "heavy" way
if (index < myList.length) {
return myList[index]
}
return null;
}
If I go with regular [index] or elementAt(index) and index is out of boundaries, it throws a RandeError
Is there a method that returns null when the index cannot be reached?
Sorry if double posted, but I try to find the info without any success, + not sure if there is an (un)official slack / discord to ask this kind of "easy" questions
Dart lists do not allow invalid indices. There is no built-in way to get a null when trying. Not in the platform libraries.
You can create your own helper function (like you already do):
T? tryGet<T>(List<T> list, int index) =>
index < 0 || index >= list.length ? null : list[index];
(Remember to check for negative indices too).
As suggested, you can also add it as an extension method:
extension ListGetExtension<T> on List<T> {
T? tryGet(int index) =>
index < 0 || index >= this.length ? null : this[index];
}
which may make it more pleasant to work with.
(I recommend against doing something bad and then catching the error, at least when you can easily check up-front whether it's bad or not).
You can defined an extension method to catch the RangeError and return null:
void main() {
print([1, 2].get(3)); // display null
}
extension SafeLookup<E> on List<E> {
E get(int index) {
try {
return this[index];
} on RangeError {
return null;
}
}
}
You can try this
void main() {
List<int> teste = [1, 2, 3, 4];
print(teste.get(1));
}
extension ListExtension<E> on List<E> {
dynamic get(int value) {
return this.contains(value) ? value : null;
}
}
According to the documentation:
throws a RangeError if index is out of bounds.
So you can use the try-catch block:
String myGetter(int index) {
try {
return myList[index];
}
on RangeError {
// Called when the index is out of bounds
return null;
}
}
If you want to be extra cautious I guess you could put a generic catch at the end (to catch all kinds of throws that are not RangeError), but in a simple getter like this I think that would not be necessary:
[...]
}catch (e) {
// No specified type, handles all other types of error/exceptions
return null;
}
[...]

Limit a generic type argument only to be a int, double or custom class

I trying make the following code but T only can be int, double or a custom class. I couldn't find how to restrict the type in Dart or something that work like where from C#. How can I do that in Dart?
class Array3dG<T> extends ListBase<T> {
List<T> l = List<T>();
Array3dG(List<T> list) {
l = list;
}
set length(int newLength) { l.length = newLength; }
int get length => l.length;
T operator [](int index) => l[index];
void operator []=(int index, T value) { l[index] = value; }
}
There is no way to constrain the type variable at compile-time. You can only have one bound on a type variable, and the only bound satisfying both int and your custom class is Object.
As suggested by #Mattia, you can check at run-time and throw in the constructor if the type parameter is not one of the ones you supprt:
Array3dG(this.list) {
if (this is! Array3dG<int> &&
this is! Array3dG<double> &&
this is! Array3dG<MyClass>) {
throw ArgumentError('Unsupported element type $T');
}
}
This prevents creating an instance of something wrong, but doesn't catch it at compile-time.
Another option is to have factory methods instead of constructors:
class Array3dG<T> {
List<T> list;
Array3dG._(this.list);
static Array3dG<int> fromInt(List<int> list) => Array3dG<int>._(list);
static Array3dG<int> fromDouble(List<double> list) => Array3dG<double>._(list);
static Array3dG<MyClass> fromMyClass(List<MyClass> list) => Array3dG<MyClass>._(list);
...
}
which you then use as Array3dG.fromInt(listOfInt). It looks like a named constructor, but it is just a static factory method (so no using new in front).
You can check at runtime the type with the is keyword:
Array3dG(List<T> list) {
if (list is List<int>) {
//Handle int
}
else if (list is List<double>) {
//Handle double
}
else if (list is List<MyClass>) {
//Handle MyClass
}
else {
throw ArgumentError('Unsupported $T type');
}
}
Note that if you are handling int and double in the same way you can just check for num
You can check the progress of the Union types here: https://github.com/dart-lang/sdk/issues/4938

Enum from String

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

Resources