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.
Related
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);
}
I'm a beginner learning dart from the book dart apprentice and I reached where they were discussing constructors in dart classes, the book was implying that constructors create instances of the class which I understood but I needed more info about constructors. So I googled and some results repeated what was already in the book about it being used to create instances of a class while others also showed that it's used to instantiate class properties, but my problem is with the other answer which I found that they are used to instantiate properties of a class, but my question is: I instantiate all class properties when I create the class by declaring the property variables, like this:
class UserClass{
userClassProperty = "";
anotherUserClassProperty = ""; }
why is the constructor also needed to instantiate class properties?
Often, values are unique to every class instance.
Consider the following example:
class Point {
final int x;
final int y;
const Point(this.x, this.y);
double get distanceToOrigin => sqrt(x * x + y * y);
}
If the x and y values were defined inside the class, it would be pretty useless. Instead, different Point objects can be instantiated with different values, which means the same code can be used for different situations.
Ok, so constructors instantiate or start a class by collecting all the data the class needs to start to start working. Constructors are so important that the dart compiler provides one even if you don't explicitly create one. For example, you create a class for mammals like this :
class Mammal{
String name = "cat";
int numberOfLegs = 2;
}
Although you don't explicitly add a constructor the dart compiler adds a default constructor like this :
class Mammal{
Mammal(); //This is added by dart during the class instantiation by default.
String name = "cat";
int numberOfLegs = 2;
}
Yeah, that's how crucial constructors are to the dart compiler.
And on the topic of why are they necessary even when you declare all the properties by yourself in the class, as hacker1024 said it would make the class pretty useless, as the point of the existence of classes is to create variants but with different properties. Not adding a constructor to your class and defining all the properties in the class would mean that your class doesn't take property arguments which in turn also means that different variants of your class can't be created. Again this goes directly against the point of the existence of dart classes. For example, you have a class like this :
class Mammals{
Strig name = "Human";
int numberOfLegs = 2;
bool hasFur = false;
}
final cat = Mammal();
final human = Mammal();
print(cat.numberOfLegs); //Prints 2
//2
print(human.numberOfLegs); //Also prints 2
//2
print(cat.hasFur);
// false
Yeah, this class is problematic. Cats with 2 legs? You would agree with me that that's not how things are in reality. And also the class is pretty useless in the sense that it's not modular, no matter which kind of mammal we create be it a cat, a sheep or even a cow the name property is going to be the default one we set, that is "Human". When we create a class to simulate mammals we want to be able to define what kind of properties it has, not use some fixed values. So you want to create a class which has a constructor like this :
class Mammals{
Mammals(String name,int noOfLegs, bool hasFur){
this.name = name;
this.noOfLegs = noOfLegs;
this.hasFur = hasFur;
}
String name = "";
int noOfLegs = 0;
bool hasFur = False;
}
final cat = Mammal("Cat", 4, True); //Now you can pass in the properties ou want.
final human = Mammal("Human", 2, false);
print(cat.name); //This prints the customized name of the object cat instead of some fixed value
//Cat
print(human.name); //This prints the customized name of the object human
Now we have two instances of the class with separate property values.
Although this adds a little more code, the modularity benefit is worth it.
https://dart.dev/guides/language/effective-dart/design#avoid-public-late-final-fields-without-initializers
AVOID public late final fields without initializers.
Unlike other final fields, a late final field without an initializer
does define a setter. If that field is public, then the setter is
public. This is rarely what you want. Fields are usually marked late
so that they can be initialized internally at some point in the
instance’s lifetime, often inside the constructor body.
Unless you do want users to call the setter, it’s better to pick one
of the following solutions:
Don’t use late. Use late, but initialize the late field at its
declaration. Use late, but make the late field private and define a
public getter for it.
The above explanation is abstract and I have no concrete image of what kind of risk this rule envisions.
I would be grateful if you could give me a hint as to how to think.
The risk is that you accidental try to assign a value twice, which will result in an error.
late final String a;
void someMethod() {
a = "a";
a = "b";
}
The above code compiles perfectly fine and is valid code because of the late but leads to a crash.
As for the suggested solutions
Don’t use late.
Use late, but initialize the late field at its declaration.
final String a = "b";
// or
late final String a = "b";
void someMethod() {
a = "a";
a = "b";
}
This makes it that the above code doesn't even compile, making it sure that the crash doesn't happen.
Late modifier means a variable's value is not known during declaration but will definitely get initialized and not null when it's accessed.
When we declare a variable as final, it means that it will only assigned once. Therefore, all the final fields has to be initialized either at declaration or inside the class constructor.
Given above facts, for one to declare a late final variable as public would probably a mistake. One should either pass the value to final field as class constructor parameter, or declare the late final field as private and initialize it internally.
Let see the example why late final field should not be made public
class Coffee {
late final String temperature; // public field as there's no prefix underscore, e.g. _temperature
// based on description from the guide, a setter will be created automatically for public late final
set temperature(String val) => temperature = val;
// again, do you think you would want above?
// as it's final field, it means it should only be initialized once!
// The creation of the setter for late final (public field) does not make too much sense
// Therefore usage late final as public field is rarely what you will want
}
Not sure if it's true, but I think this advice is to enforce better architecture. If field is final then you can set it value only once and almost always you need to initialize it from within the class it belongs to.
But if setter for final field is public then people might attempt to set it value from elsewhere AFTER it is already initialized, and this will lead to an error.
UPD: two sections above there is this advice, that basically summarizes my point:
Objects shouldn’t generally expose more state than they need to.
...and setter is not needed after final field has been initialized.
i'm coming from mainly JS/TS world (NestJS/Angular) and recently i start to building Flutter apps..
i have 2 main questions
there is any difference when instantiate object with or without new keyword?
i saw examples in flutter when people use new Row(children: [Text('Foo'), Text('Bar'),],) instead of just Row(...)
if there is a difference which one is better to use?
inside of my Dart classes in flutter app, i can both use this.property and property again there is any difference and if so which one is better and why?
example:
class Person {
final String name;
final int age;
Person(this.name, this.age);
getNameAge() => '${this.name} is ${this.age}';
getNameAge2() => '$name is $age';
}
both looks the same to me
void main() {
final p = Person('dan', 22);
final p2 = new Person('ben', 20);
print(p.getNameAge()); // dan is 22
print(p2.getNameAge2()); // ben is 20
}
The new keyword is optional in Dart and I think the general consensus is, today, to not use it.
The use of this is useful if you have multiple variables with the same name but in different scope. E.g. (this is just an example. You would not make a setA method in Dart but use properties):
class A {
int a;
A(this.a);
void setA(int a) {
this.a = a;
}
}
Here we use this to distinguish between the argument a and the class variable a. But if you don't have variables with the same name (but in different scope), the use of this is optional. In some projects, you still use this to make it more clear that you are referring to a class variable even if it is not needed.
class X extends Y {
X(int a, int b) : super(a,b);
}
Can someone give me an explanation about the syntax meaning of the colon :?
This feature in Dart is called "initializer list".
It allows you to initialize fields of your class, make assertions and call the super constructor.
This means that it is not the same as the constructor body. As I said, you can only initialize variables and only access static members. You cannot call any (non-static) methods.
The benefit is that you can also initialize final variables, which you cannot do in the constructor body. You also have access to all parameters that are passed to the constructor, which you do not have when initializing the parameters directly in the parentheses.
Additionally, you can use class fields on the left-hand of an assignment with the same name as a parameter on the right-hand side that refers to a parameter. Dart will automatically use the class field on the left-hand side.
Here is an example:
class X {
final int number;
X(number) : number = number ?? 0;
}
The code above assigns the parameter named number to the final field this.number if it is non-null and otherwise it assigns 0. This means that the left-hand number of the assignment actually refers to this.number. Now, you can even make an assertion that will never fail (and is redundant because of that, but I want to explain how everything works together):
class X {
final int number;
X(number): number = number ?? 0, assert(number != null);
}
Learn more.
It's ok to access non static member in initializer list.
class Point {
num x, y;
Point(this.x, this.y);
Point.origin(): this.x = 10, this.y = 10;
}
main() {
Point p = Point.origin();
print(p.x); // 10
}