I have the following basic class with its constructor in Dart:
class ChartData {
String? name;
Color? color;
Duration? duration;
ChartData(
String name, List rgbo, Duration duration) {
this.name = name;
this.color = Color.fromRGBO(rgbo[0], rgbo[1], rgbo[2], rgbo[3]);
this.duration = duration;
}
}
How can I make it so that the constructor arguments are required, and the class arguments are non-nullable thus don't need any null safety? I'm aware of the keyword required, but from what I understand it works only for initializing formal class constructors.
How could we use initializing formal for this class, especially regarding constructing the color class argument?
First of all, you should use an initializer list to initialize the fields, not do assignments in the constructor body. Dart is like C++ in that regard, not Java.
class ChartData {
final String name;
final Color color;
final Duration duration;
ChartData(String name, List<int> rgbo, Duration duration)
: this.name = name,
this.color = Color.fromRGBO(rgbo[0], rgbo[1], rgbo[2], rgbo[3]),
this.duration = duration;
}
This change allows your fields to be final and non-nullable, because now they are initialized before they can ever be read. Your arguments are required. They already were, but they still are.
If you want to use initializing formals, and you do, you can replace an initializer list entry of the form this.name = name (or name = name, because the this is already optional) with a parameter of the form this.name:
class ChartData {
final String name;
final Color color;
final Duration duration;
ChartData(this.name, List<int> rgbo, this.duration)
: color = Color.fromRGBO(rgbo[0], rgbo[1], rgbo[2], rgbo[3]);
}
The color parameter cannot be an initializing formal because it doesn't store the argument directly into the field. Just keep that as an initializer list entry instead.
This works, the fields are final and non-nullable, the parameters are required and non-nullable, and you use initializing formals where possible.
You asked about required. That modifier works with named parameters, and your parameters are positional.
If you wanted them to be named instead, you could write it as:
class ChartData {
final String name;
final Color color;
final Duration duration;
ChartData(
{required this.name, required List<int> rgbo, required this.duration})
: color = Color.fromRGBO(rgbo[0], rgbo[1], rgbo[2], rgbo[3]);
}
The {...} surrounding the parameters makes them named. Required named parameters need a required in front, named parameters default to being optional.
Whether you like required named parameters or not is a matter of taste. Some hate writing and reading the extra name, others prefer it because they find it easier to read.
Either version works.
To set the arguments to non-null, you must add required to each argument in a constructor.
If you want to initialize the arguments, you could call the class and set the values. Also you can initialize in some initState()
For example:
class ChartData {
String name;
Color color;
Duration duration;
ChartData({
required this.name,
required this.color,
required this.duration
});
}
class OtherClass extends StatelessWidget {
//initialize
final chartData = ChartData(
name: "name1",
color: Color.fromRGBO(38, 38, 38, 0.4),
duration: const Duration(seconds:15));
#override
Widget build(BuildContext context) {
return Container();
}
}
Related
why there is error while am trying to construct my constructor i want to have a named paramters but not required ones how i can fix this
class Task {
int id;
String title;
String description;
**Task({this.id , this.title , this.description}); // Giving me Errors here**
Map<String , dynamic> toMap()
{
return {
'id' : id,
'title' : title,
'description' : description,
};
}
#override
String toString() {
return 'Task{id: $id, name: $title, age: $description}';
}
}
If you don't want the arguments to be required, you have to allow them to be null by adding a question mark to their type, so Dart will know that they don't need to be initialized. Like this:
int? id;
String? title;
String? description;
Another solution is to give them a default value in the constructor, but be careful to assign them values that won't conflict with the rest of your code:
Task({this.id=-1, this.title='Title' , this.description = 'Description'});
Choose the approach that suits you best: you can also use a mix of the two solutions, like making some properties nullable and giving a default value to the others.
Based on my following code, I want to have a constructor of the class Hero that takes a Stats class as an optional parameter that has a default value based on its constructor (the one that set its health and attack fields to 100 and 10 by an optional named parameter) instead of null.
void main() {
Hero hero = Hero("Foo");
print('${hero.name} : HP ${hero.stats.health}');
}
class Stats {
Stats({this.health = 100, this.attack = 10});
double health;
double attack;
}
class Hero {
// error: The default value of an optional parameter must be constant
Hero(this.name,[this.stats = Stats()]);
String name;
Stats stats;
}
More things i've tried:
class Hero {
// error: Can't have a const constructor for a class with non-final fields
Hero(this.name,[this.stats = const Stats()]);
String name;
Stats stats;
}
class Hero {
// error: stats initialized as null
Hero(this.name,[this.stats]);
String name;
Stats stats = Stats();
}
This following code works but it doesn't have stats as an optional parameter:
class Hero {
Hero(this.name);
String name;
Stats stats = Stats();
}
(Credits to #jamesdlin for linking to his answer in the comments)
In general, if there isn't a const constructor available, you instead
can resort to using a null default value (or some other appropriate
sentinel value) and then setting the desired value later:
class Foo {
Bar bar;
Foo({Bar bar}) : bar = bar ?? Bar();
}
(Note that explicitly passing null as an argument will do something
different with this approach than if you had set the default value
directly. That is, Foo(bar: null) with this approach will initialize
bar to Bar(), whereas with a normal default value it would initialize
bar to null. In some cases, however, this approach's behavior might be
more desirable.)
I am trying to create a base class for my models but I am struggling with the error The name 'cls' isn't a type so it can't be used as a type argument.. So, how can I pass the object's constructor to the Hive.box method?
import 'package:hive/hive.dart';
class AppModel {
#HiveField(0)
int id;
#HiveField(1)
DateTime createdAt;
#HiveField(2)
DateTime updatedAt;
save() async {
final Type cls = this.runtimeType;
// The name 'cls' isn't a type so it can't be used as a type argument.
final Box box = await Hive.openBox<cls>(cls.toString());
await box.put(this.id, this);
return this;
}
}
#HiveType(typeId: 0)
class UserModel extends AppModel {
#HiveField(3)
String email;
#HiveField(4)
String displayName;
}
void main() {
final UserModel user = UserModel()
..email = 'user#domain.com'
..displayName = 'john doe';
user.save().then(() {
print('saved');
});
}
Dart does not have a way to refer to the dynamic type of this (a "self type").
The way such things are often handled is to have a self-type as type argument, so:
class AppModel<T extends AppModel> {
save() async {
final Box box = await Hive.openBox<T>(T.toString());
await box.put(this.id, this as T);
return this;
}
...
and then ensure that each subclass tells the superclass what type it is:
class UserModel extends AppModel<UserModel> {
...
}
(or, if you expect to subclass UserModel eventually:
class UserModel<T extends UserModel> extends AppModel<T> {
...
}
so that a subclass can still pass its type through).
You are also talking about constructors, and for that there is no easy solution.
Dart's type parameters are types, not classes. You cannot access static members or constructors from a type variable, and there is also no other way to pass a class around.
The only way you can have something call a constructor that it doesn't refer to statically, is to wrap the constructor call in a function and pass that function.
(I can't see how you need the constructor here).
I am trying to figure out how to pass a null argument in the constructor, but I am getting this error:
Don't explicitly initialize variables to null
class Dog {
final id int;
final String name;
final int age;
Dog({this.id=null, this.name, this.age});
}
I don't want to pass an id to the constructor. I want to call the constructor like this:
var dog = Dog(
name: 'Rex',
age: 15,
);
How do I accomplish this?
By not explicitly assigning to null
class Dog {
final id int;
final String name;
final int age;
Dog({this.id, this.name, this.age});
}
Remember, be default value of id is set to null. So if the consumer doesn't pass a value for id it will continue to have null and so will name
If you want to make any parameter mandatory then you should mark that with #required
If you don't set variable the default value will be null.
I encountered a problem when writing the constructor in dart. I have a class with two final variables, initialize them in the constructor, the following is wrong, because the final variable has no setter method:
class Person{
final String name;
final int age;
// Error
Person(String name, int age) {
this.name = name;
this.age = age;
}
}
but this is correct, why
class Person{
final String name;
final int age;
// Correct
Person(String name, int age): this.name = name, this.age = age;
}
When the constructor body is executed, final fields are already sealed.
The constructor initializer list is executed before the constructor initializers of the super classes.
The constructor bodies are executed afterwards. Constructor body allows arbitrary code to be executed like reading from fields. This is why at this point the initialization of final fields has to be completed already, otherwise it would be possible to read from a not yet initialized final field.
The constructor initializer list is the supported window where final fields can be initialized. It does not allow reading from this (explicit or implicit) and is therefore safe.
This is just a measure to ensure object initialization always happens in predictable manner.