I have this class which takes some parameters by using the factory constructor, if instance is null, a new object will be created; if it's not null, the value of instance will be returned so we always receive the same object all the time (Singleton). This is how I used the singleton pattern before enabling null-safety features of dart.
class GuestUser extends User {
static GeustUser _instance;
factory GuestUser(
{required String firstName,
required String lastName,
required String email,
required Address address,
required PaymentInfo paymentInfo}) {
if (_instance == null) {
_instance =
GuestUser._(firstName, lastName, email, address, paymentInfo);
}
return _instance;
}
Now with null-safety enabled, I get this error:
The non-nullable variable '_instance' must be initialized.
Try adding an initializer expression.
Also if (_instance == null) is not needed anymore.
If I define the _instance like this: static late final GuestUser _instance; then I cannot use the if (_instance == null) to only create the _instance when needed. so I have to remove the if statement and create a new instance every time the factory constructor is called.
How can I solve this issue and create a singleton class with null-safety enabled?
I have this solution in mind to keep track of the instance with a boolean variable:
static late final GeustUser _instance;
static bool _isInstanceCreated = false;
factory GuestUser(
{required String firstName,
required String lastName,
required String email,
required Address address,
required PaymentInfo paymentInfo}) {
if (_isInstanceCreated == false) {
_instance =
GuestUser._(firstName, lastName, email, address, paymentInfo);
}
_isInsanceCreated = true;
return _instance;
}
But I want to know whether there is a way to do this without defining new variable and by using the features of the null-safety
Your singleton is a little strange since your are taking arguments which will only be used the first time it is called. It is not a great pattern since it can give some surprises if e.g. your want to use it for two different guest users.
In your example it makes sense to just use GuestUser? as the type for _instance. Non-nullable by default in Dart should not be seen as the use of null is bad and you should definitely use null where it makes sense (especially if we can prevent the introduction of bool variables to indicate if a variable has been set). In your example, _instance is null until it is initialized the first time.
Example where we use the ??= operator. The operator will check if _instance is null and in case of null, it will assign the variable to the object created by calling the constructor GuestUser._. It will hereafter return the value of _instance. If _instance does already have a value (not null), this value will just be returned without calling the GuestUser._ constructor.
class GuestUser extends User {
static GuestUser? _instance;
factory GuestUser(
{required String firstName,
required String lastName,
required String email,
required Address address,
required PaymentInfo paymentInfo}) =>
_instance ??=
GuestUser._(firstName, lastName, email, address, paymentInfo);
}
If you had a more traditional singleton, you could have made a static final variable where the instance is created in the definition. But this does not allow for parameters.
You can use the same traditional way to create a singleton. You only have to put the null-safety operator in the right place.
class MySingleton {
// make this nullable by adding '?'
static MySingleton? _instance;
MySingleton._() {
// initialization and stuff
}
factory MySingleton() {
if (_instance == null) {
_instance = new MySingleton._();
}
// since you are sure you will return non-null value, add '!' operator
return _instance!;
}
}
As #julemand101 mentioned, your singleton is actually strange, because generally you'd do something like:
class Foo {
static final instance = Foo._();
Foo._();
}
However, you can't instantiate it with the parameters. For that you can do this:
class Bar {
static Bar? instance;
Bar._(int i);
factory Bar.fromInt(int i) {
var bar = Bar._(i);
instance = bar;
return bar;
}
void display() => print('Bar instance');
}
void main() {
final nullableBar = Bar.instance;
final nonNullableBar = Bar.fromInt(0);
nullableBar!.display(); // Runtime error as Bar.instance is used before Bar.fromMap(0)
nonNullableBar.display(); // No error
}
What you are getting as it is part of Lazy Initialization, with this approach we create an instance at runtime, not at the time of initialization.
Lastly, your problem will be solved using the null aware operator, and better use factory constructor for initialization of an object.
class GuestUser {
static GuestUser? _instance;
GuestUser._(
String firstName,
String lastName,
String email,
String address,
String paymentInfo);
factory GuestUser(
{required String firstName,
required String lastName,
required String email,
required String address,
required String paymentInfo}) {
_instance ??=
GuestUser._(firstName, lastName, email, address, paymentInfo);
return _instance!;
}
///.. Rest code
}
Create object like
GuestUser user = GuestUser(firstName: "jitesh", lastName: "mohite", email: "jitesh#gmail.com", address: "Universe", paymentInfo: "As you like");
Related
Hello guys in tis application when i add delete or update student in emulator it says late initialization error field _id has not been initialized this is because it wrote in student.dart late int _id; i don't want to make a value for _id in order to be able to add a new value
class Student {
late int _id ;
late String _name;
late String _description;
late int _pass;
late String _date;
Student(this._name, this._description, this._pass, this._date);
Student.withId(
this._id, this._name, this._description, this._pass, this._date);
String get date => _date;
int get pass => _pass;
String get description => _description;
String get name => _name;
int get id => _id;
set date(String value) {
_date = value;
}
set pass(int value) {
if (value >= 1 && value <= 2) {
_pass = value;
}
}
set description(String value) {
if (value.length <= 255) {
_description = value;
}
}
set name(String value) {
if (value.length <= 255) {
_name = value;
}
}
Map<String, dynamic> toMap() {
var map = Map<String, dynamic>();
map["id"] = this._id;
map["name"] = this._name;
map["description"] = this._description;
map["pass"] = this._pass;
map["date"] = this._date;
return map;
}
Student.getMap(Map<String, dynamic> map) {
this._id = map["id"];
this._name = map["name"];
this._description = map["description"];
this._pass = map["pass"];
this._date = map["date"];
}
}
when i write late int _id ; it says late initialization error this is the entire code https://github.com/abdelrahman992-cpu/studentfinal
You need to be careful when marking a variable as being late. You must instantiate the variable with a value before you ever access it. In your case, the default constructor for Student doesn't instantiate _id with a value, so anywhere in your code afterward that tries to access _id is going to throw this error.
You can either give _id a default value to ensure it is instantiated:
Student(this._name, this._description, this._pass, this._date)
: _id = -1;
Student.withId(this._id, this._name, this._description, this._pass, this._date);
Or mark it as nullable:
int? _id;
late String _name;
late String _description;
late int _pass;
late String _date;
Having said all this, there is no reason for your fields to be marked as late. That keyword is reserved for situations where your fields cannot be logically made nullable but their values won't exist until some time after the object has been created.
For example, if the instance represents some data that exists in some asynchronous source:
class RemoteData {
final String name;
late final List<String> rows;
RemoteData(this.name);
// Must call fetch before using any instance of this class
Future<void> fetch() async {
rows = await SomeRemoteDataSource.getRows();
}
}
...
final data = RemoteData('foo');
// fetch hasn't been called on data so it's in a dangerous error-prone state
// print(data.rows); // Uncommenting this line will result in an error
await SomeRemoteDataSource.initialize();
await data.fetch();
// Now it's safe to use data because rows has been initialized
print(data.rows);
In general, the use of late is discouraged for the following reasons:
You almost never actually need it.
When you think you do need it, it's usually better to refactor your code so you don't need it anymore.
The reason is that Dart expects all non-nullable fields of a class to have a value by the time the constructor is finished. The late keyword disables that behavior for a field, allowing the class to be created without the field having a value. But there's a catch - if the field doesn't have a value by the time it's accessed for the first time, Dart will throw an error. This is an example of a race condition, and it should be avoided at all costs since you can easily shoot yourself in the foot even if you think you know what you're doing.
For example, if you created RemoteData and then tried to access rows without first calling fetch, the code would throw an error. And even sneakier, there's no guarantee that even fetch would properly instantiate rows, either. (What if the call to SomeRemoteDataSource.getRows() never completes or throws an error?) So the RemoteData class would be better off being refactored so that the call to SomeRemoteDataSource.getRows() happened before the object was created, and then the rows passed in along with the name to the constructor itself.
In your case, all of the fields are being instantiated in the constructor as is normal for a class's fields, so there's no reason whatsoever for the fields to be marked as late. As such, get rid of late since it can only cause harm to your code to leave it in.
I am getting following errors:
Instance members can't be accessed from a factory constructor. (Documentation) Try removing the reference to the instance member.
The argument type 'List<Map<String, dynamic>>?' can't be assigned to the parameter type 'List<Vaccination>'. (Documentation)
at line _convertVaccinations(json['vaccinations'] as List<dynamic>));
Code:
final String name;
final String? notes;
final String type;
final List<Vaccination> vaccination;
final String? referenceId;
Pet(this.name, {this.notes, required this.type, required this.vaccination, this.referenceId});
factory Pet.fromJson(Map<String, dynamic> json) =>
Pet(
json['name'] as String,
notes: json['notes'] as String,
type: json['types'] as String,
referenceId: json['referenceId'] as String,
vaccination:
_convertVaccinations(json['vaccinations'] as List<dynamic>));
List<Map<String, dynamic>>? _convertVaccinations(List<dynamic>? vaccinations) {
if (vaccinations == null) {
return null;
} else {
final vaccinationMap = <Map<String, dynamic>>[];
for (var element in vaccinations) {
vaccinationMap.add(element.toJson);
}
return vaccinationMap;
}
}
Factory and instance member error:
Well it is because factory Pet.fromJson(...) is a factory constructor, and the class method _convertVaccinations(...) is an instance member.
If you make _convertVaccinations(...) static, it will accept the use of it in the factory constructor.
Argument error:
vaccination is of type List<Vaccination> and the method _convertVaccination(...) returns either null or List<Map<String, dynamic>>
In other words, you cannot assign null to List<T> unless it says List<T>? and the class Vaccination is not a Map<String, dynamic>
Maybe you want to do something like final List<Vaccination>? vaccinations; OR return <Vaccination>[] instead of null if vaccinations == null.
So you'd probably want to do write _convertVaccinations(...) as:
static List<Vaccination>? _convertVaccination(List<dynamic>? vaccinations) {
return vaccinations?.map((e) => Vaccination.fromJson(e as Map<String,dynamic>).toList();
}
or:
static List<Vaccination> _convertVaccination(List<dynamic>? vaccinations) {
return vaccinations?.map((e) => Vaccination.fromJson(e as Map<String,dynamic>).toList() ?? <Vaccination>[];
}
Side note: Maybe you have more methods that you haven't presented here. Because it looks a bit wonky when your Pet.fromJson(...) use a element.toJson down the call stack.
I am new to Flutter.
I am creating a named constructor to work with flutter Models. But for some reason it is showing an error:
class ImageModel {
int id;
String url;
String title;
// constructor
ImageModel(this.id, this.url, this.title);
// named constructor
ImageModel.fromJSON(Map<String, dynamic> parsedJson) {
id = parsedJson['id'];
url = parsedJson['url'];
title = parsedJson['title'];
}
}
Error:
Non-nullable instance field 'url' must be initialized.
Try adding an initializer expression, or add a field initializer
in this constructor, or mark it 'late'.dartnot_initialized_non_nullable_instance_field
I read the documentation, and found this solution, not sure why this is required at this place. I know its use case, but should not this work without this ?
class ImageModel {
late int id; // refactor
late String url; // refactor
late String title; // refactor
.
.
.
You have used incorrect syntax for the named constructor.
Instead of
ImageModel.fromJSON(Map<String, dynamic> parsedJson) {
id = parsedJson['id'];
url = parsedJson['url'];
title = parsedJson['title'];
}
it must be
ImageModel.fromJSON(Map<String, dynamic> parsedJson) :
id = parsedJson['id'],
url = parsedJson['url'],
title = parsedJson['title'];
The object is initialized after colon(:) in named constructor and curly brackets({}) are then used if you want to perform some task after initialization of object. Since you directly used {} after named constructor, it created an empty object for you and hence all parameters were null which you were trying to initialize in the function body. That's why this issue was solved after using 'late' keyword.
do you like this way
factory ImageModel.fromJson(Map<String, dynamic> json) {
return ImageModel(
json["id"],
json["url"],
json["title"],
);
}
And i prefer
class ImageModel {
int id;
String url;
String title;
// constructor
ImageModel({
required this.id,
required this.url,
required this.title,
});
factory ImageModel.fromJson(Map<String, dynamic> json) {
return ImageModel(
id: json["id"],
url: json["url"],
title: json["title"],
);
}
}
The Dart compiler complains because of its "null safety" feature. This means, that variable types like int or String must be initialised. This is been checked for at compile time. You can either add required in the constructor
ImageModel({
required this.id,
required this.url,
required this.title,
});
so that you cannot call the constructor without initialising the fields or let the compiler know that you will take care of the initialisation later by adding the late keyword (as you did). Of course you can also initialise the variables with some default values, if there are such
int id = 0;
String url = "https://www.example.com/default.jpg";
String title = "Default Text";
but that seems to be more of a corner case.
I have a question, I started learning dart/flutter, and when passing data from one screen to another I access the data like this
final orderData = ModalRoute.of(context).settings.arguments;
OrderData class looks like this
class OrderItem {
final String id;
final String name;
final String date;
final String address;
final String recordNumber;
OrderItem({
#required this.id,
#required this.name,
#required this.date,
#required this.address,
#required this.recordNumber,
});
Map<String, dynamic> toMap() {
return {
'id': id,
'name': name,
'date': date,
'address': address,
'recordNumber': recordNumber,
};
}
factory OrderItem.fromMap(Map<String, dynamic> map) {
if (map == null) return null;
return OrderItem(
id: map['id'],
name: map['name'],
date: map['date'],
address: map['address'],
recordNumber: map['recordNumber'],
);
}
String toJson() => json.encode(toMap());
factory OrderItem.fromJson(String source) => OrderItem.fromMap(json.decode(source));
#override
String toString() {
return 'OrderItem(id: $id, name: $name, date: $date, address: $address, recordNumber: $recordNumber)';
}
#override
bool operator ==(Object o) {
if (identical(this, o)) return true;
return o is OrderItem &&
o.id == id &&
o.name == name &&
o.date == date &&
o.address == address &&
o.recordNumber == recordNumber;
}
#override
int get hashCode {
return id.hashCode ^
name.hashCode ^
date.hashCode ^
address.hashCode ^
recordNumber.hashCode;
}
}
My question is two fold(this is what vs code plugin generates for class). First is how can I access the data in the instance of the class(do I need for each specific getter), and second can somebody explain what toMap(), fromMap(), toJson(), fromJson(), bool operator ==(Object o), and hashCode getter do.
Looks like you're trying to use Flutter navigation methods.
Have a look at this blog post that explains how it works.
In summary, to "push" state to be used in the next screen, do something like this:
final arguments = OrderItem(
id = 'id',
name = 'name',
date = 'date',
address = 'address',
recordNumber = '10',
);
Navigator.pushNamed(context, NamedPagePassed.route, arguments: arguments);
You can later access it like this:
final OrderItem args = ModalRoute.of(context).settings.arguments;
print('The id of the order is ${args.id}');
You seem to have a whole lot of generated code in your class. The fromJson, toJson, fromMap and toMap methods are used for serialization (i.e. turn a Dart object into something that can be "transferred" to/from another language/network/etc).
The == operator and hashCode are used to check if an object instance is equal to another (which is very common in Flutter as Flutter wants to know if your UI state has been modified). hashCode allows a fast way to check that two objects are definitely not equal (you can know for sure that two objects are not equal if their hash-codes are different... if the hash-codes are equal, the objects may or may not be equal, but the probability they are NOT equal will be low because hash functions try to avoid "collisions", which is when two different objects have the same hash-code).
Hash-code and == are normally implemented together to give your class "identity". Just google around and you'll see how this all works.
I would recommend you don't use code generation from your IDE like this. Instead, get familiar with how Dart builders work, then use a codegen library that will automatically create these methods for you every time you compile (so changes to the data model are immediately reflected in the implementation of all these generated methods).
My recommendation is to use freezed for that.
Your code will be much more maintainable this way.
In the following model class, what purpose does overriding the toString() method achieve?
class User {
final int id;
String name, email, token;
User(this.id, this.name, this.email, this.token);
User.fromJson(dynamic json) : this.id = json['id'] {
this.name = json['name'];
this.email = json['email'];
this.token = json['token'];
}
dynamic toJson() => {'id': id, 'name': name, 'email': email, 'token': token};
#override
String toString() {
return toJson().toString();
}
}
The purpose of the toString() method is to provide a literal representation of whatever object it is called on, or to convert an object to a string (example converting primitive types to strings). For a user defined class, it can vary depending on your use case. You might want to print out the object to a console. A simple print command on the object won't give you any useful information. However, you can use the toString() method and convert it to a more understandable version. Example
#override
String toString() {
return "($someProperty,$someOtherProperty)";
}
This will return whatever the properties of that object were set at that time. Another (although not good) purpose could be to compare two objects of the same class. Similar to the above example, you can say that two objects of the class would be equal if the output of the toString() method of both objects is the same. Again, it totally depends on your use case.