Dart Class with ":" vs Dart Class without [duplicate] - flutter

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

Can this class be reconfigured to accept named parameters in Flutter?

Background code
I have a class called Result that I'm using to pass on some error codes and error messages to an error screen.
class Result<T>{
Result._();
factory Result.loading(T msg) = LoadingState<T>;
factory Result.success(T value) = SuccessState<T>;
factory Result.error(T title, T msg, T errorcode) = ErrorState<T>;
}
class ErrorState<T> extends Result<T> {
final T title;
final T msg;
final T errorcode;
ErrorState(this.title, this.msg, this.errorcode) : super._();
//final T msg;
}
This class is called as follows:
return Result.error("Error","Status code not 200", 1);
My problem
Functionally it all works great.
The problem is I see myself in the future having to refer back to my class Result code to remember what each field represents which may become even more problematic if I want to add in more fields down the line
I'd rather convert this so that the fields are named
My question
Can this class be converted so that it is called like this (with a name describing the parameter):
return Result.error(title:"Error", msg:"Status code not 200", errorcode:1);
If you want to change Result.error to accept named parameters without breaking existing call sites, you can't do that. Parameters can be either positional or named, but not both.
If you're okay with breaking existing call sites, then you can just make them required named parameters. Since you're using redirecting constructors, you either will need to:
Change the signature of the redirectee constructor (in your case, ErrorState) to exactly match that of Result.error:
factory Result.error(
{required T title, required T msg, required T errorcode}) = ErrorState<T>;
...
ErrorState({required this.title, required this.msg, required this.errorcode})
: super._();
Or change Result.error to be a non-redirecting constructor:
factory Result.error(
{required T title, required T msg, required T errorcode}) {
return ErrorState<T>(title, msg, errorcode);
}
(If you do want to avoid breaking existing callers, you could add a separate named constructor (or static method) that uses named parameters, and you could optionally deprecate the old constructor.)
As an aside, it does not make sense that Result and ErrorState are generic classes. Do you really expect title and msg to not be Strings? Does it really make sense for the type of title, msg, and errorcode to be the same? (With the example you've shown, you'll end up with Result<Object>, which defeats the point of using a generic class.)

What does " : " after Board.fromDatabase(DataSnapshot snapshot) mean. I want to undestand how " : " works here and what is it. If possible by exmaple [duplicate]

This code is from flutter gallery and i'm trying to understanding and adapting it. I would know what this syntax means:
class DemoItem<T> {
DemoItem({
this.valueName,
this.hintName,
this.valueSurname,
this.hintSurname,
this.builder,
this.valueToString
}) : textController = new TextEditingController(text: valueToString(valueName));
Especially i would know what means the colon after the constructor and if there is a way to define another TextEditingController, in addition to the one already defined.
The part after : is called "initializer list". It is a ,-separated list of expressions that can access constructor parameters and can assign to instance fields, even final instance fields. This is handy to initialize final fields with calculated values.
The initializer list is also used to call other constructors like : ..., super('foo').
Since Dart version 2.0 the initializer list also supports assert(...) which is handy to check parameter values.
The initializer list can't read from this because the super constructors need to be completed before access to this is valid, but it can assign to this.xxx.
Pointing out as mentioned in the comments by user693336:
This also means the initializer list is executed before the constructor body. Also the initializer lists of all superclasses are executed before any of the constructor bodies are executed.
Example (copied from https://github.com/dart-lang/language/issues/1394):
class C {
final int x;
final int y;
C(this.x) : y = x + 1;
}
To elaborate on other answers and to complete the syntax, it is also possible to have a real body for the constructor along with initializer code
NonNegativePoint(this.x, this.y) : assert(x >= 0), assert(y >= 0) {
print('I just made a NonNegativePoint: ($x, $y)');
}
^ Here the assertions happen before the execution of the body
Another use case is to assign values to final fields before body executes
final num x;
final num y;
Point.fromJson(Map<String, num> json) : x = json['x'], y = json['y'] {
print('In Point.fromJson(): ($x, $y)');
}

A way to read a String as dart code inside flutter?

I want to build a method to dynamically save attributes on a specific object
given the attribute name and the value to save I call the "save()" function to update the global targetObj
var targetObj = targetClass();
save(String attribute, String value){
targetObj.attribute = value;
print(targetObj.attribute);
}
But I'm getting the following error:
Class 'targetClass' has no instance setter 'attribute='.
Receiver: Instance of 'targetClass'
Tried calling: attribute="Foo"
The only thing that I can think of is that "attribute" due to being type String results in an error.
That lead me to think if there is a way to read a String as code, something like eval for php.
As #Randal mentioned, you cannot create class..method at runtime. Still, you can try something like this.
A certain class
class Foo {
dynamic bar1;
dynamic bar2;
// ...
}
Your save method
save(Foo fooObject, String attribute, dynamic value) {
if ("bar1" == attribute) fooObject.bar1 = value;
else if ("bar2" == attribute) fooObject.bar2 == value;
// ...
}
Dart (and thus flutter) does not have a way to compile and execute code at runtime (other than dart:mirrors, which is deprecated). You can build additional code that derives from other code using the various builder mechanisms, although it can be rather complicated to implement (and use!).

why does my indexOf output -1 in flutter?

class Task{
String name;
bool isDone;
Task({this.name, this.isDone = false});
}
class TaskData{
List<Task> tasks = [
Task(name: 'Buy milk'),
Task(name: 'Buy eggs'),
Task(name: 'Buy bread')
];
}
void main(){
print(TaskData().tasks.indexOf(Task(name: 'Buy milk')));
}
why does it print -1 ?
Flutter(Dart) can not compare objects directly as we compare variables like int, strings and other.
We have to do it manually by checking each and every variables are same or not then we can say that both object are same or not.
However, we can compare class object as other variable by using Equatable package.
Add Equatable package in pubspec.yaml file and then change your class as below. now you will get desire outputs.
class Task extends Equatable {
String name;
bool isDone;
Task({this.name, this.isDone = false});
#override
List<Object> get props => [name, isDone];
}
Your program prints -1 becuase the Task argument you sent to indexOf() does not exist in tasks. The following explanation is dart-related more than it is flutter-related.
By default, instance of an object in Dart is considered equal to another instance only if those are the same instance. Here, you created two different instances, perhaps with the same properties but they are actually independent of each other. In this case both tasks are logically equal but there is no way Dart could know that. Your options are:
Use a const constructor
Const constructors in Dart let you create instances that are compile-time constants. If you use such a constructor, Dart will know for sure that after you create a Task its properties can never change. Thus, it will be comfortable making the assumption that Task(name: 'Buy milk') will always have the same properties of another Task(name: 'Buy milk') no matter what (They are equal). Under the hood, Dart uses the same instance for both. To implement this you'll have to change your code as follows:
class Task {
final String name;
final bool isDone;
const Task({this.name, this.isDone = false});
}
Override the hashCode and the == operator
If you don't want Task to be immutable, you should override the current primitive equality check of Task. from Dart's documentation:
The default hash code implementation provides an identity hash—two
objects generally only have the same hash code if they are the exact
same object. Likewise, the default behavior for == is identity.
If you are overriding ==, it implies you may have different objects
that are considered “equal” by your class. Any two objects that are
equal must have the same hash code. Otherwise, maps and other
hash-based collections will fail to recognize that the two objects are
equivalent.
The default hashCode represents the identity of an instance, but we want it to represent its properties in this case. Note that hashCode should not end up being the same for instances with different properties. It generally does not matter how it's implemented as long as this principle is preserved. I'll use the example provided here to give a possible implementation:
#override
int get hashCode {
int result = 17;
result = 37 * result + name.hashCode;
result = 37 * result + isDone.hashCode;
return result;
}
Now override the == operator to reflect the changes:
#override
bool operator ==(dynamic other) {
if (other is! Task) return false;
Task task = other;
return (task.name == name &&
task.isDone == isDone);
}
}
Note that Dart genrally recommends to avoid defining custom equality for mutable classes, and, following this design rule, the first solution I've listed is the easiest to implement.

Flutter required keyword

I don't really understand how required works. For example I've seen this code:
class Test{
final String x;
Test({
required this.x
});
factory Test.initial(){
return Test(x: "");
}
}
But what should required do here? Seems like it makes an optional parameter a non optional parameter.
Update
As of Dart 2.12, the required keyword replaces the #required meta annotation. For detailed info look into the official FAQ. The following answer has been updated to reflect both this and null safety.
Parameters required by default
The parameters of a class constructor or function are required by default.
class Test {
final String x;
Test(this.x);
}
You're not allowed to do this:
final value = Test();
// 1 positional argument(s) expected, but 0 found.
You must do this:
final value = Test('hello');
Optional named parameters
If you surround a parameter with curly braces, though, in addition to becoming a named parameter, it also becomes optional.
Since it's optional, the property must either be nullable like this:
class Test {
final String? x;
Test({this.x});
}
Or it has to have a default value like this:
class Test {
final String? x;
Test({this.x = ''});
}
So now this is ok:
final value = Test();
And so is this:
final value = Test(x: 'hello');
Required named parameters
Sometimes you don't want to allow a parameter to be null and there is no natural default variable. In that case you can add the required keyword in front of the parameter name:
class Test {
final String x;
Test({required this.x});
}
This is not ok anymore:
final value = Test();
// The named parameter 'x' is required, but there's no corresponding argument.
But this is still fine:
final value = Test(x: 'hello');
Dart 2.12 (null safety):
Beginning with Dart 2.12, the #required annotation is now replaced by the required keyword. You should mark your field required if it is mandatory for others to pass some value to it.
For example:
class Foo {
final int a; // Mandatory? Use 'required'
final int b; // Not mandatory? Don't use 'required'
Foo({
required this.a, // Marked 'required'
this.b = 1,
});
}
Usage:
Foo(); // Error: 'a' is required
Foo(a: 0); // Good
Foo(a: 0, b: 1); // Good
#required is an annotation that will create a warning for you to remember that the named parameter is necessary for the class to work as expected.
It will not create compile errors, at least for what I know.
#required bounds you to pass #required marked arguments while creating object of Class. For example, while showing a dialog, you'd mark context as required since, you cannot show dialog without having a valid context. But, you should not overuse it.
Short answer: Named parameters are optional by default in Dart. We prefer them to positional params for ease of use. In this case, the named parameters also might be expected to hold some value all the time (non-nullable) - from initialization itself. Hence, the double effort.
He could use default value initialization of the parameters instead of 'required', if the values were compile-time constants, and that doesn't seem to be the case here.
Positional parameters can be required or optional, which we pass in order when calling. The following is an example of required positional parameters' usage:
class Object{
String name;
int value;
Object(this.name, this.value=100); //auto type inference
}
final one = Object("Name here", 50); // All parameters are needed to call.
Named parameters are another type of optional parameters. Flutter APIs use named parameters and in our UI code, it is preferred to use named parameters instead of positional parameters. Reason being readability and clarity when reading code or calling the constructors later on at several parts of the code. You would have seen this as the case with all Widgets, Styles. For if it were to be positional it would be difficult to keep track of them upon calling with the sheer amount of methods that would be in use, and dynamic type inference could also be at work.
void display({required String name, int value1, int value2=100}) {...;} //named params
display(value1: 50, name: "Calculated name");
NOTE:
If exists, required positional parameters have to come first. Either named or optional positional params can follow(NOT BOTH).
String say(String from, String msg, [String? device]) { //req. pos params and opt pos params.
var result = '$from says $msg';
if (device != null) {
result = '$result with a $device';
}
return result;
}
assert(say('Bob', 'Howdy') == 'Bob says Howdy');
Remove
required
in constructor.
Instead write
final String? x;
So, it becomes as:
class Test{
final String? x;
Test({
this.x
});
factory Test.initial(){
return Test(x: "");
}
}