Flutter: create an Object from Type or String - flutter

I want to create an Object from an object type defined in Type or from an object name defined in String. The following example uses a String to hold the object type. But I think this is not that elegant - even with Type, this if block would increase dramatically for a lot of object types...
I didn't find a better solution for this yet. How can I create that object dynamically from the specified object type?
if (model == 'Event') {
data = Event.fromMap(result);
}
else if (model == 'Content') {
data = Content.fromMap(result);
}
else if (...) {
// ...
}

This is other approach.
class Event{
Event.fromMap(_map){
print('This is an event');
print(_map);
}
}
class Content{
Content.fromMap(_map){
print('This is a content');
print(_map);
}
}
Map<String, Function> types = {
'Event' : (_map)=>Event.fromMap(_map),
'Content' : (_map)=>Content.fromMap(_map),
};
void main() {
var a = types['Event']({'test':'success_event'});
print(a.runtimeType);
var b = types['Content']({'test':'success_content'});
print(b.runtimeType);
}
Its a bit more scalable (since only depends on add the class constructor into the map).
The explanation:
class Event{
Event.fromMap(_map){
print('This is an event');
print(_map);
}
}
class Content{
Content.fromMap(_map){
print('This is a content');
print(_map);
}
}
Here we are creating the test classes. nothing important.
Map<String, Function> types = {
'Event' : (_map)=>Event.fromMap(_map),
'Content' : (_map)=>Content.fromMap(_map),
};
Here we are defining a Map. Why? Because it allows us to access some value through some key in constant time. In this case, the keys are the Strings 'Event', 'Content', but also can be types as you wanted. For simplicity, let them be Strings. The values are Function's, in this example only getting as parameter a _map (because the Class constructors in the example require one parameter _map). So, if you need more types, only add the type and the function encapsulating the constructor for that type.
void main() {
var a = types['Event']({'test':'success_event'});
print(a.runtimeType);
var b = types['Content']({'test':'success_content'});
print(b.runtimeType);
}
Finally you can instantiate the classes easily. Only with the type string and passing to the function the values you want (In this example a map with a key 'test').
In your example would be something like:
data = types[model](result);

Related

Is it possible to declare a Map type that requires all enum values to be present as keys?

How can I require the Dart compiler to warn me when I forget to include all members of an enum in a map? For example, in the following:
enum Size {
small,
medium,
large,
}
// This is a valid Dart code. Dart compiler doesn't require `Size.large` to be present.
final Map<Size, ButtonSize> sizeMap = {
Size.small: const MyClass(),
Size.medium: const MyClass(),
};
The Dart compiler isn't that restrictive. It doesn't require all enum values to be present in the Map, so I can't be sure that the following code will return an instance of MyClass. It might resolve to null:
final MyClass instance = sizeMap[Size.small]; // unsafe
I have to either do this:
final MyClass? instance = sizeMap[Size.small]; // `instance` might be `null`
or this:
final MyClass instance = sizeMap[Size.small] as MyClass; // `instance` might still be `null`, but we're pretending it's not.
Both solutions are far from perfect. The first one implies further null checks in the code, the second one smells because of typecasting.
Is there any way to declare the type of sizeMap so that all enum values must be present?
Instead of using a Map, you'd be better off with a function and a switch statement:
enum Size {
small,
medium,
large,
}
class MyClass {
const MyClass();
}
MyClass mapSize(Size size) {
switch (size) {
case Size.small:
return const MyClass();
case Size.medium:
return const MyClass();
}
}
the above code generates a compile-time error from not handling all enum values:
Error: A non-null value must be returned since the return type 'MyClass' doesn't allow null.
With null-safety, the above code cannot be executed until the above problem is fixed.
Additionally, dart analyze will produce a more descriptive warning:
Missing case clause for 'large'. Try adding a case clause for the missing constant, or adding a default clause. • missing_enum_constant_in_switch
If you want to use a Map because you want to allow it to be mutated, you could provide separate functions to set and get values:
var _smallValue = const MyClass();
var _mediumValue = const MyClass();
var _largeValue = const MyClass();
MyClass mapSize(Size size) {
switch (size) {
case Size.small:
return _smallValue;
case Size.medium:
return _mediumValue;
case Size.large:
return _largeValue;
}
}
void setMappedSize(Size size, MyClass value) {
switch (size) {
case Size.small:
_smallValue = value;
break;
case Size.medium:
_mediumValue = value;
break;
case Size.large:
_largeValue = value;
break;
}
}
If you find that to be too verbose, and you just want callers to avoid returning null, you could wrap a Map internally:
const _defaultValue = const MyClass();
final _sizeMap = <Size, MyClass>{};
MyClass mapSize(Size size) => _sizeMap[size] ?? _defaultValue;
void setMappedSize(Size size, MyClass value) => _sizeMap[size] = value;

How to create generic class of type T, where T is Type variable?

I'm trying to create list of generic typed class from mixed-type list.
It works, but the problem is each Data instance type is dynamic.
class Data<T> {
final T value;
const Data(this.value);
}
final List<dynamic> bases = [...];
final List<Data> data = bases.map((b) {
return Data(b);
}).toList();
Attempt #2
class Data<T> {
final T value;
const Data(this.value);
}
final List<dynamic> bases = [...];
final List<Data> data = bases.map((b) {
final Type T = b.runtimeType;
return Data<T>(b);
}).toList(); // List<Data<dynamic>>
But it fails, due to: The name 'T' isn't a type so it can't be used as a type argument. Try correcting the name to an existing type, or defining a type named
Generic type parameters must be known statically (i.e., at compile-time). If you start off with a heterogeneous List<dynamic>, then the static type of each element is dynamic, and therefore when you construct a Data object from that element, it will be Data<dynamic>.
If your heterogeneous list has a limited number of types, you could do:
var data = <Data<dynamic>>[];
for (var b in bases) {
if (b is Foo) {
var d = Data(b); // `d` is of type `Data<Foo>`
data.add(d);
} else if (b is Bar) {
var d = Data(b); // `d` is of type `Data<Bar>`
data.add(d);
} else {
throw UnimplementedError('Unrecognized type: ${b.runtimeType}');
}
}
(Note that if you do data.add(Data(b)) in the above, the Data object will be constructed as Data<dynamic> because the generic type parameter will be inferred from data (of type List<Data<dynamic>>) instead of from b.)
Otherwise what you want isn't really possible.

Why does the dart "is" operator behave differently for local variables vs. class fields? [duplicate]

This question already has answers here:
"The operator can’t be unconditionally invoked because the receiver can be null" error after migrating to Dart null-safety
(3 answers)
Closed 12 months ago.
I have migrated my Dart code to NNBD / Null Safety. Some of it looks like this:
class Foo {
String? _a;
void foo() {
if (_a != null) {
_a += 'a';
}
}
}
class Bar {
Bar() {
_a = 'a';
}
String _a;
}
This causes two analysis errors. For _a += 'a';:
An expression whose value can be 'null' must be null-checked before it can be dereferenced.
Try checking that the value isn't 'null' before dereferencing it.
For Bar() {:
Non-nullable instance field '_a' must be initialized.
Try adding an initializer expression, or add a field initializer in this constructor, or mark it 'late'.
In both cases I have already done exactly what the error suggests! What's up with that?
I'm using Dart 2.12.0-133.2.beta (Tue Dec 15).
Edit: I found this page which says:
The analyzer can’t model the flow of your whole application, so it can’t predict the values of global variables or class fields.
But that doesn't make sense to me - there's only one possible flow control path from if (_a != null) to _a += 'a'; in this case - there's no async code and Dart is single-threaded - so it doesn't matter that _a isn't local.
And the error message for Bar() explicitly states the possibility of initialising the field in the constructor.
The problem is that class fields can be overridden even if it is marked as final. The following example illustrates the problem:
class A {
final String? text = 'hello';
String? getText() {
if (text != null) {
return text;
} else {
return 'WAS NULL!';
}
}
}
class B extends A {
bool first = true;
#override
String? get text {
if (first) {
first = false;
return 'world';
} else {
return null;
}
}
}
void main() {
print(A().getText()); // hello
print(B().getText()); // null
}
The B class overrides the text final field so it returns a value the first time it is asked but returns null after this. You cannot write your A class in such a way that you can prevent this form of overrides from being allowed.
So we cannot change the return value of getText from String? to String even if it looks like we checks the text field for null before returning it.
An expression whose value can be 'null' must be null-checked before it can be dereferenced. Try checking that the value isn't 'null' before dereferencing it.
It seems like this really does only work for local variables. This code has no errors:
class Foo {
String? _a;
void foo() {
final a = _a;
if (a != null) {
a += 'a';
_a = a;
}
}
}
It kind of sucks though. My code is now filled with code that just copies class members to local variables and back again. :-/
Non-nullable instance field '_a' must be initialized. Try adding an initializer expression, or add a field initializer in this constructor, or mark it 'late'.
Ah so it turns out a "field initializer" is actually like this:
class Bar {
Bar() : _a = 'a';
String _a;
}
There are few ways to deal with this situation. I've given a detailed answer here so I'm only writing the solutions from it:
Use local variable (Recommended)
void foo() {
var a = this.a; // <-- Local variable
if (a != null) {
a += 'a';
this.a = a;
}
}
Use ??
void foo() {
var a = (this.a ?? '') + 'a';
this.a = a;
}
Use Bang operator (!)
You should only use this solution when you're 100% sure that the variable (a) is not null at the time you're using it.
void foo() {
a = a! + 'a'; // <-- Bang operator
}
To answer your second question:
Non-nullable fields should always be initialized. There are generally three ways of initializing them:
In the declaration:
class Bar {
String a = 'a';
}
In the initializing formal
class Bar {
String a;
Bar({required this.a});
}
In the initializer list:
class Bar {
String a;
Bar(String b) : a = b;
}
You can create your classes in null-safety like this
class JobDoc {
File? docCam1;
File? docCam2;
File? docBarcode;
File? docSignature;
JobDoc({this.docCam1, this.docCam2, this.docBarcode, this.docSignature});
JobDoc.fromJson(Map<String, dynamic> json) {
docCam1 = json['docCam1'] ?? null;
docCam2 = json['docCam2'] ?? null;
docBarcode = json['docBarcode'] ?? null;
docSignature = json['docSignature'] ?? null;
}
}

How to create null safe block in flutter?

How do I null check or create a null safe block in Flutter?
Here is an example:
class Dog {
final List<String>? breeds;
Dog(this.breeds);
}
void handleDog(Dog dog) {
printBreeds(dog.breeds); //Error: The argument type 'List<String>?' can't be assigned to the parameter type 'List<String>'.
}
void printBreeds(List<String> breeds) {
breeds.forEach((breed) {
print(breed);
});
}
If you try to surround it with an if case you get the same error:
void handleDog(Dog dog){
if(dog.breeds != null) {
printBreeds(dog.breeds); //Error: The argument type 'List<String>?' can't be assigned to the parameter type 'List<String>'.
}
}
If you create a new property and then null check it it works, but it becomes bothersome to create new properties each time you want to null check:
void handleDog(Dog dog) {
final List<String>? breeds = dog.breeds;
if (breeds != null) {
printBreeds(breeds); // OK!
}
}
Is there a better way to do this?
Like the ?.let{} syntax in kotlin?
To get something similar to Kotlins .let{} i created the following generic extension :
extension NullSafeBlock<T> on T? {
void let(Function(T it) runnable) {
final instance = this;
if (instance != null) {
runnable(instance);
}
}
}
And it can be used like this:
void handleDog(Dog dog) {
dog.breeds?.let((it) => printBreeds(it));
}
"it" inside the let function will never be null at runtime.
Thanks to all the suggestions, but they were all some variation of moving the null check further down the code execution cain, which was not what i was looking for.
Yes, you'll have create a local variable just like you did to handle those things because if you don't create a local variable then if there is a class which is extending the Dog class can override breeds which will then become nullable even after you had checked it in the first place.
The other solution you can try is changing the List<String> to nullable in printBreeds method.
void handleDog(Dog dog) {
printBreeds(dog.breeds);
}
void printBreeds(List<String>? breeds) {
breeds?.forEach((breed) {
print(breed);
});
}
This error is right //Error: The argument type 'List<String>?' can't be assigned to the parameter type 'List<String>'.
as null type list is passing to function which says it accepts a non-null list
By below way breeds can be accessible
void printBreeds(List<String>? breeds) {
breeds?.forEach((breed) {
print(breed);
});
}
Also, if we don't want nullable operation every time, we can handle it while calling
Example:
class Dog {
final List<String>? breeds;
Dog(this.breeds);
}
void handleDog(Dog dog) {
print("handleDog");
printBreeds(dog.breeds!);
}
// This method only called if breeds not null
void printBreeds(List<String> breeds) {
print("printBreeds");
breeds.forEach((breed) {
print(breed);
});
}
void main() {
var dog = Dog(null);
handleDog(dog);
}
Output:
printBreeds

Under which conditions I should prefer computed properties over stored properties?

I saw this piece of code today, and was wondering why you would not instead use simple static stored properties?
This is the code that I am curious about:
class ApiKeys {
// movie keys
class var HomePage: String { get { return "homepage" } }
class var Id: String { get { return "id" } }
class var Overview: String { get { return "overview" } }
class var PosterPath: String { get { return "poster_path" } }
class var ReleaseDate: String { get { return "release_date" } }
class var Runtime: String { get { return "runtime" } }
class var Tagline: String { get { return "tagline" } }
class var Title: String { get { return "title" } }
class var Rating: String { get { return "vote_average" } }
// query params
class var ApiKey: String { get { return "api_key" } }
class var Query: String { get { return "query" } }
}
And this is how I would have written the same code:
class ApiKeys {
static let homePage = "homepage"
static let id = "id"
static let overview = "overview"
static let posterPath = "poster_path"
static let releaseDate = "release_date"
static let runtime = "runtime"
static let tagline = "tagline"
static let title = "title"
static let rating = "vote_average"
//Query Params
static let ApiKey = "api_key"
static let query = "query"
}
There won't ever be any need to override the variables, so use of static should be okay. Am I missing something? Is there any advantage or reason to use the first method over the second?
For what it's worth, I wouldn't be inclined to use computed or stored properties at all. Rather than defining this to be a class, this seems like a textbook case for an enum:
enum ApiKey: String {
// movie keys
case HomePage = "homepage"
case Id = "id"
case Overview = "overview"
case PosterPath = "poster_path"
case ReleaseDate = "release_date"
case Runtime = "runtime"
case Tagline = "tagline"
case Title = "title"
case Rating = "vote_average"
// query params
case ApiKey = "api_key"
case Query = "query"
}
This more accurately captures the notion that a "key" can be one of those values.
And you'd use it like so:
if key == ApiKey.HomePage.rawValue {
...
}
Or
if ApiKey(rawValue: key) == .HomePage {
...
}
In answer to your original question, “when should I prefer computed properties”, the answer is that you generally use them to retrieve a value computed from other properties and, optionally, if you want to set other (possibly private) properties and values indirectly. There's little benefit to using computed properties if you're just going to return some static, unchanging string.
A class var can be overridden by a subclass while a static constant can't. That's the first difference I can think about.
Computed properties can be used to dynamically change the value of the property at runtime if necessary, just like and overridden getter can in Objective-C. You can't do that with a static let constant.
Possibly somewhat off-topic: but one possibly contrived usage scenario where static stored properties cannot be used is if you define non-blueprinted static computed properties with default implementations in an extension to some "constants" protocol. Classes/structs/etc that conform to such a protocol can be allowed to access type constrained generics, where these generics are the the only context in which the protocol constants are accessible (limit the accessibility to the constants) where they are guaranteed to be constants (since they can also be used directly from the concrete types that conform that protocol, but these can "override" the "constants" with new values).
protocol HasAccessToConstants {
/* since we don't blueprint 'theAnswer', the default
implementation below will always be used for objects
conforming to this protocol when used in a generic
context (even if they attempt to "override" these
"constants" with implementations of their own, these
custom ones can only be accessed for concrete-types). */
}
extension HasAccessToConstants {
static var theAnswer: Int { return 42 }
/* for protocols: we may implement a default
implementation only for computed properties */
}
class Foo : HasAccessToConstants {
/* Even if the developer implements its own "constant"
implementation, this will not be used for accessing
Foo type in a generic context. */
static var theAnswer: Int { return 9 }
}
func onlyForObjectsWithAccessToConstants<T: HasAccessToConstants>(obj: T) {
// do something with obj ...
// make use of constants available to the type of obj
print("Constants available to the type of this object (e.g. '\(T.theAnswer)')")
}
onlyForObjectsWithAccessToConstants(Foo())
/* Constants available to the type of this object (e.g. '42') */
// not really "constants" as they can be "overridden" for concrete types
print(Foo.theAnswer) // 9 (since concrete type)
Again, contrived, and included for the technical discussion, as I can't really see in what scenario this would be more useful than other, better alternatives.