Difference between class constructor syntax in Dart - dart

I have a class that I am creating that looks like this:
class Movie {
final String title, posterPath, overview;
Movie(this.title, this.posterPath, this.overview);
Movie.fromJson(Map json) {
title = json["title"];
posterPath = json["poster_path"];
overview = json['overview';
}
}
I am getting a warning that says that "The final variables 'overview', 'posterPath', & '1' more must be initialized. There are also warnings around each variable saying 'title' can't be used as a setter because it is final.
When I write the constructor using this syntax, the warnings go away:
Movie.fromJson(Map json)
: title = json["title"],
posterPath = json["poster_path"],
overview = json['overview'];
What exactly is going on here?

Dart objects must be fully initialized before anyone gets a reference to the new object. Since the body of a constructor can access this, the object needs to be initialized before entering the constructor body.
To do that, generative Dart constructors have an initializer list, looking similiar to C++, where you can initialize fields, including final fields, but you cannot access the object itself yet. The syntax:
Movie.fromJson(Map json)
: title = json["title"],
posterPath = json["poster_path"],
overview = json['overview'];
uses an initializer list (the list of assignments after the :) to initialize the final instance variables title, posterPath and overview.
The first constructor uses an "initializing formal" this.title to directly put the parameter into the field.
The constructor
Movie(this.title, this.posterPath, this.overview);
is effectively a shorthand for:
Movie(String title, String posterPath, String overview)
: this.title = title, this.posterPath = posterPath, this.overview = overview;
Your constructor can combine all of these and a body:
Movie(this.title, this.posterPath, String overview)
: this.overview = overview ?? "Default Overview!" {
if (title == null) throw ArgumentError.notNull("title");
}
(A const constructor cannot have a body, but it can have an initializer list with some restrictions on the allowed expressions to ensure that they can be evaluated at compile-time).

Dart separates properties initialization from the constructor body.
A constructor has 3 parts :
the name/parameters definition
properties initialization/super call/asserts
A body, similar to a function immediately run on construction
Both the initialization and body parts are optional.
final variables must be initialized on the first 2 parts. They cannot be initialized inside the body.
A full constructor will look like the following :
MyClass(int value)
: assert(value > 0),
property = value,
super();
{
print("Hello World");
}
The main purpose of this initializer part is for body-less constructors which allows const constructors, a dart specific feature. See How does the const constructor actually work? for more details on these.

I just found some documentation around this, & it seams that the second version with the : is what's called the "initializer list" which allows you to initialize instance variables before the constructor body runs.
There is more detail around this in the documentation here.

Related

Parameter issue on Dart

can somebody explain why
class SaveGlove {
final String serialNumber;
final String productionDate;
SaveGlove(this.serialNumber, this.productionDate);
SaveGlove.fromJson(Map<String, Object?> json)
: this(
serialNumber: json['serialNumber']! as String,
productionDate: json['prductionDate']! as String,
);
Map<String, Object?> toJson() {
return {
'serialNumber': serialNumber,
'prductionDate': productionDate,
};
}
}
doesn't work but when I change parameter in constructor like that:
SaveGlove({required this.serialNumber, required this.productionDate});
it works?
Your original code doesn't work because your constructor is declared to take two positional arguments,
SaveGlove(this.serialNumber, this.productionDate);
but you are calling it with named arguments:
: this(
serialNumber: json['serialNumber']! as String,
productionDate: json['prductionDate']! as String,
);
(This is a redirecting generative constructor, which tries to redirect to the SaveGlove constructor by passing it two named arguments.)
That doesn't work, positional parameters need positional arguments.
If you had written the redirecting constructor as:
: this(
json['serialNumber']! as String,
json['prductionDate']! as String,
);
then it would have worked.
Changing the constructor to take two named parameters also makes the redirection be valid.
It is because Null Safety is added in Dart. In simple words, Null Safety means a variable cannot contain a ‘null’ value unless you initialized with null to that variable. All the runtime null-dereference errors will now be shown in compile time with null safety.
So as you are initializing variables serialNumber & productionDate you have two options:
Either make it compulsory to provide those variables values from constructor by adding required keyword.
class SaveGlove{
String serialNumber;
final String productionDate;
SaveGlove({required this.serialNumber, required this.productionDate});
}
Or declare those variables as nullable, i.e which can accept null values. So you don't need the required keyword:
class SaveGlove{
String? serialNumber;
String? productionDate;
SaveGlove({this.serialNumber,this.productionDate});
}

Can default values be specified once (in the presence of non-nullable instance variables)?

Consider the following class. It is what we may call a "two-dimensional enum". We could have used
an enum with four states, but since the four states have a clear meaning, we choose the more
direct approach of storing two Boolean flags.
class FlowerType {
bool has_scent = false;
bool has_thorns = false;
FlowerType({this.has_scent = false, this.has_thorns = false});
}
The class has one constructor with two named optional parameters.
This constructor also acts as a default constructor, and since the two bools are non-nullable, they need to be specified (again) in the constructor.
Can default values be specified once?
Declaring two final static variables for this purpose is one option (though a pretty lousy one). Here I'm wondering whether I'm missing some basic fact about constructors.
I'd remove the default values from the fields:
class FlowerType {
final bool hasScent;
final bool hasThorns;
FlowerType({this.hasScent = false, this.hasThorns = false});
}
The values written as field initializers will always be overridden by the constructor anyway, and they prevent the fields from being final.
You could consider alternatives like multiple constructors:
FlowerType.plain() : this(hasScent: false, hasThorns: false);
FlowerType.thorny() : this(hasScent: false, hasThorns: true);
FlowerType.scented() : this(hasScent: true, hasThorns: false);
FlowerType.thornyAndScented() : this(hasScent: true, hasThorns: true);
(I assume there will be more fields and constructor parameters, otherwise you only ever need four instance of the class).
An alternative (albeit a less efficient one) is to make the members non-nullable but to use nullable parameters:
class FlowerType {
bool has_scent = false;
bool has_thorns = false;
FlowerType({bool? has_scent, bool? has_thorns}) {
this.has_scent = has_scent ?? this.has_scent;
this.has_thorns = has_thorns ?? this.has_thorns;
}
}
Drawbacks:
Less efficient (the member would be initialized, the argument would be tested, and the member would be reassigned).
Members could not be final.
Members would receive their intended value later in the construction process. It wouldn't matter in this example, though.

Get the name of a Dart class as a Type or String

Problem I need to solve
Is there a way to get the class name of a dart class as a String or a Type object..?
class MyClass {
}
var myClass = MyClass();
I know the property, runtimeType which return the type of the object as a Type object. But is there a similar function for classes?
print(myClass.runtimeType.toString());
What I currently do is creating an object of the class and use runtimeType.
String type = MyClass().runtimeType.toString();
Note: In python there is a variable called __name__ in every class, which does what I need.
My intention
My final goal is to create dart objects using previously saved class names. In this issue they have proposed a method using Maps.
The thing is that I have lots of classes and that method looks messy in my situation.
What I currently do is, save the object type by:
var saving = myClass.runtimeType.toString();
And when loading:
if (saving == MyClass().runtimeType.toString()) {
return MyClass();
}
From your experiences and opinions, can you propose a better solution?
You can use:
var runtimeTypeName = (MyClass).toString();
or for generics:
var runtimeTypeName = T.toString();
The class type can be used as a Type:
Type myType = MyClass;

FLUTTER How to get variable based on passed string name?

I have stored variables in a class with their code names.
Suppose I want to get XVG from that class, I want to do
String getIconsURL(String symbol) {
var list = new URLsList();
//symbol = 'XVG'
return list.(symbol);
}
class URLsList{
var XVG = 'some url';
var BTC = 'some url';
}
Can someone help me achieve this or provide me with a better solution?
Dart when used in flutter doesn't support reflection.
If it's text that you want to have directly in your code for some reason, I'd advise using a text replace (using your favourite tool or using intellij's find + replace with regex) to change it into a map, i.e.
final Map<String, String> whee = {
'XVG': 'url 1',
'BTC': 'url 2',
};
Another alternative is saving it as a JSON file in your assets, and then loading it and reading it when the app opens, or even downloading it from a server on first run / when needed (in case the URLs need updating more often than you plan on updating the app). Hardcoding a bunch of data like that isn't necessarily always a good idea.
EDIT: how to use.
final Map<String, String> whee = .....
String getIconsURL(String symbol) {
//symbol = 'XVG'
return whee[symbol];
}
If you define it in a class make sure you set it to static as well so it doesn't make another each time the class is instantiated.
Also, if you want to iterate through them you have the option of using entries, keys, or values - see the Map Class documentation
I'd just implement a getProperty(String name) method or the [] operator like:
class URLsList{
var XVG = 'some url';
var BTC = 'some url';
String get operator [](String key) {
switch(key) {
case 'XVG': return XVG;
case 'BTC': return BTC;
}
}
}
String getIconsURL(String symbol) {
var list = new URLsList();
return list[symbol];
}
You can also use reflectable package that enables you to use reflection-like code by code generation.
Assuming that the class is being created from a JSON Object, you can always use objectName.toJSON() and then use the variable names are array indices to do your computations.

Inconsistent error reporting from Dart Editor regarding final fields

Given the following class, Dart Editor (build 5549) gives me some conflicting feedback (per the comments in the constructor body):
class Example {
final int foo;
Example() :
foo = 0
{
foo = 1; // 'cannot assign value to final variable "foo"'
this.foo = 2; // ok
}
}
Even more confusingly, it will happily generate equivalent (working) javascript for both lines. The situation seems to be the same with methods as it is with the constructor; this especially leads me to believe that it was intended to be disallowed in both cases.
The Dart Style Guide suggests using public final fields instead of private fields with public getters. I like this in theory, but non-trivial member construction can't really go into the initializer list.
Am I missing a valid reason for the former to be reported as an error while the latter is not? Or should I be filing a bug right now?
This is surely a bug in the JavaScript generator if you run the following in the Dart VM:
main() {
new Example();
}
class Example {
final int foo;
Example() : foo = 0 {
foo = 1; // this fails in the dart vm
this.foo = 2; // this also fails in the dart vm
}
}
then it refuses to execute both the line foo = 1 and this.foo = 2. This is consistent with the spec which requires (if I read it correctly) that final fields to be final in the constructor body.

Resources