How to set associated constant values of enum members in Dart? - flutter

The only thing I can find to set the enum value is this:
Add methods or values to enum in dart
However, I find it a bit tedious.
Is there a better way?
In C# I can just simply do something like this:
enum ErrorCode
{
None = 0,
Unknown = 1,
ConnectionLost = 100,
OutlierReading = 200
}

Here is the simple exapmle
enum ErrorCode {
None,
Unknown,
ConnectionLost,
OutlierReading,
}
extension ErrorCodeExtention on ErrorCode {
static final values = {
ErrorCode.None: 0,
ErrorCode.Unknown: 1,
ErrorCode.ConnectionLost: 100,
ErrorCode.OutlierReading: 200,
};
int? get value => values[this];
}

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 ErrorCode {
None(0),
Unknown(1),
ConnectionLost(100),
OutlierReading(200);
final int value;
const ErrorCode(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 ErrorCode is an enum declaration with four values ErrorCode.None, ErrorCode.Unknown, and so on, and we have ErrorCode.None.value == 0 and ErrorCode.Unknown.value == 1, and so on. 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.

Related

Dynamic Type Casting in Dart/Flutter

I am in the middle of writing a library to dynamically serialise/deserialise any object in Dart/Flutter (Similar in idea to Pydantic for Python). However, I am finding it impossible to implement the last component, dynamic type casting. This is required in order convert types from JSON, such as List to List (or similar). The types are retrieved from objects using reflection.
The below is the desired implementation (though as far as I understand this is not possible in Dart).
Map<String, Type> dynamicTypes = {"key": int };
// Regular casting would be "1" as int
int value = "1" as dynamicTypes["key"];
Is there some workaround which makes this possible to implement? Or have I reached a dead end with this (hence no other dynamic serialisation/deserialisation package already exists).
Conducting more research into this issue, it seems in Dart's current implementation this is impossible due to runtime reflection being disabled as referenced here in the official docs.
There are ongoing discussions about the support for this and the associated package dart:mirrors here on GitHub, but so far though there is some desire for such functionality it is highly unlikely to ever be implemented.
As a result, the only options are:
Use code generation libraries to generate methods.
Manual serialisation/deserialisation methods.
Implement classes with complex types such as lists and maps to be dynamic, enabling (all be it limited) automatic serialisation/deserialisation.
Your question does not specify how exactly dynamicTypes is built, or how its key is derived. So there is perhaps a detail to this that is not clear to me.
But what about something like this?
class Caster<T> {
final T Function(String) fromString;
Caster(this.fromString);
}
void main() {
Map<String, Caster> dynamicTypes = { "key": Caster<int>((s) => int.parse(s)) };
int v = dynamicTypes['key']!.fromString('1');
print(v);
}
Or, if you have the value as a dynamic rather than a string:
class Caster<T> {
T cast(dynamic value) => value as T;
}
void main() {
Map<String, Caster> dynamicTypes = { "key": Caster<int>() };
dynamic a = 1;
int v = dynamicTypes['key']!.cast(a);
print(v);
}
Or even more succinctly:
void main() {
dynamic a = 1;
int v = a;
print(v);
}

Dart assert not working when use List.length

I want to check List length when initialize class.
I tried but it did not work as expected.
My code is here.
#immutable
class TestClass {
const TestClass(this.values)
: assert(values.length >= 0 && values.length <= 4);
final List<int> values;
}
const a = TestClass(<int>[1, 1, 1]); // assert
How can I get it to behave as expected?
Edit 1
Now the assertion no longer works.
Edit 2
Edit 3
// OK
final ok = TestClass(const <int>[1]);
// NG
final ng = TestClass(const <int>[1, 1, 1, 1, 1]);
Edit 4
#immutable
class TestClass2 {
const TestClass2(
this.value,
) : assert(value.length == 3);
final String value;
}
final a = TestClass2('abcde');
I think T.length doesn't work in assert.
Edit 5
Edit 6
I don't have enough reputation to add a comment, so I'll add it as an answer.
Apparently, this is working as expected. Found this issue on the Dart SDK repository which is similar. In a comment someone referenced the following from the language specification:
You cannot get the length of a list in a constant expression. The language specification says so here:
\item An expression of the form \code{$e$.length} is potentially constant
if $e$ is a potentially constant expression.
It is further constant if $e$ is a constant expression that
evaluates to an instance of \code{String}.
So it must be rejected at compile-time. This is one of many situations where it's tempting to make Dart constant expressions just a little bit more expressive, but it is quite costly (in so many ways) so it's not very likely to happen. One issue is that you can write a class MyList extends List<String> {...} and make the length getter run arbitrary code, and still have a const constructor in MyList, and constant expressions will not run arbitrary code, so you'd need to find the right borderline between the lists where .length is OK and the lists where it isn't, and the type system wouldn't be able to prevent the latter from occurring where a List or List<T> for some T is specified.
Not sure if there's a way around this, but I hope this helps a bit.
#immutable
class TestClass {
const TestClass(this.values) : assert(values.length == 0 || values.length > 4);
final List<int> values;
}
I edited my answer you will get a warning if values ​​variable has no elements or has more than 4 elements

Is there a dart function annotation that makes the type checker do type narrowing or condition assertions

Is there a construct that communicates to the type checker a function's post-condition?
For example, in typescript it is possible to say
function assertIsNumber(value: any): asserts value is number {
if (typeof value !== 'number') {
throw new TypeError();
}
}
I would like to be able to do something like the following in dart:
class SomeClass {
int? value;
_checkPreconditions() {
if(value == null) {
throw MyPreconditionError()
}
// ...
}
somefunc() {
_checkPreconditions();
// here we know `value` is a non-null int.
final sum = value + 5;
}
}
I understand I could coerce the value to non-null sum = value! + 5, but I would prefer to allow the function to inform the type checker if possible.
It looks like the type system of Dart is not so powerful. The only thing that looks (from first glance) possible is to create a custom code analyzer package (or search for one that already exists).
Dart annotations don't actually do anything. They provide hints to tools such as the Dart analyzer (usually so that it can generate additional warnings), but they cannot change program behavior. Even if you could convince the analyzer to treat some variables as different types, you still wouldn't be able to compile and run your code.
Annotations can be used by code generation tools, so one possibility might be to generate a statement such as final value = this.value!; automatically. However, that would be a lot of trouble to go through (and would mean that code then would need to use this.value = 42; for assignments and would prevent your code from being analyzed directly).

Multi if statement in class parameters setting

I know that in the latest version of dart we can use if else statements inside the build method. Does anyone know if we can use also if else statement when we setting class parameters? I know I can do inline statement there but inline is a bit hard to read when there are multiple conditions
const int i = 0;
class Person {
// NewClass n = NewClass(a: i == 0 ? 'a' : 'b'); //<- inline statement working
NewClass n = NewClass(a: if(i == 0) 'a' else 'b'); //<- if statement doesn't
}
class NewClass {
final String a;
const NewClass({this.a});
}
Edit:
Basically in my case I've got an TextField widget where I set its's type parameter from enum (Type.text, Type.numeric...) According to this parameter I want to set The textField parameters (textCapitalization, maxLength and so on)
As per your comment, you are already creating an enum for specifying the type of the fields.
enum Type {text, numeric}
Now for specifying the properties of that particular type, you can add an extension on this enum, as shown below:
extension TextFieldProperties on Type {
int get maxLength {
if (this == Type.text) {
return 10;
}
return 12;
}
}
So in your field class you already have a type defined, you can use that type variable to get the properties of that particular type of field.
Type type = Type.text;
print(type.maxLength); // Will print 10
type = Type.numeric;
print(type.maxLength); // Will print 12
Note: It will work only in Dart 2.7 and above
You want the conditional expression (?:), not the conditional statement or literal entry (if), as you have already discovered.
The reason if doesn't work is that if only works as a statement or as a collection literal entry. It doesn't work in arbitrary expressions.
The reason for the distinction is that the if syntax allows you to omit the else branch. That only makes sense in places where "nothing" is a valid alternative. For a statement, "doing nothing" is fine. For a collection, "adding nothing" is also fine.
In an expression context, you must evaluate to a value or throw. There is no reasonable default that we can use instead of "nothing", so an if is not allowed instead of an expression.
Doesn't work because this syntax doesn't exist in Dart. The only way to do what you would like to do is to use the ternary operator.
If you try it in the DartPad you will get an error.
I suggest you to use a function to return the right value.

libspotify sp_search_type

When executing sp_search_create, there is a parameter for sp_search_type which is defined
typedef enum sp_search_type {
SP_SEARCH_STANDARD = 0,
SP_SEARCH_SUGGEST = 1,
} sp_search_type;
I don't see any change in the result. Is there a suggested way to handle this differently from a normal search ? For instance if we are implementing auto complete or am I missing something here.
Thanks,