Flutter Dart | How to return different object from class Constructor - flutter

In Dart, is it possible for a constructor to cancel object creation and return a different object instead?
Use case:
Users contains a static map that maps ids to User objects.
When a User is initialized, I want the User constructor to check if User with id is already created, if so: return existing User object, else create a new User object
Example (of-course not working):
class Users {
static const Map<String, User> users = {};
}
class User {
final String id;
final String firstName;
User({required id, required firstName}) {
// If user with id already exists, return that object
if (Users.users.containsKey(id) {
return Users.users[id];
}
// Else, initialize object and save it in Users.users
this.id = id;
this.firstName = firstName;
Users.users[id] = this;
}
}
Question: IS there any way to get the above pseudo code to work?

As mentioned by jamesdlin you should use a factory constructor. Here's what is mentioned in the documentation:
Use the factory keyword when implementing a constructor that doesn’t
always create a new instance of its class.
And in your case this is exactly what you want. Now here's a code sample that does what you want:
Code sample
class Users {
// Also your Map cannot be const if you want to edit it.
static Map<String, User> users = {};
}
class User {
final String id;
final String firstName;
/// Base private constructor required to create the User object.
User._({required this.id, required this.firstName});
/// Factory used to create a new User if the id is available otherwise return the User
/// associated with the id.
factory User({required String id, required String firstName}) {
// If user with id already exists, return that object
if (Users.users.containsKey(id)) {
// Force casting as non nullable as we already checked that the key exists
return Users.users[id]!;
}
// Else, initialize object and save it in Users.users
final newUser = User._(id: id, firstName: firstName);
Users.users[id] = newUser;
return newUser;
}
}
Try the full code on DartPad

You can create a function in class to handle things you want. Here's what you can implement.
class Player {
final String name;
final String color;
Player(this.name, this.color);
Player.fromPlayer(Player another) :
color = another.color,
name = another.name;
}

If this is for caching purposes or you are not creating multiple instances of the Users class, I would suggest using a pattern where static is responsible for a list of class instances. Sometimes this helps to significantly reduce the amount of code:
class User {
static final Map<String, User> users = {};
final String id, firstName;
User._({required this.id, required this.firstName});
factory User({required String id, required String firstName}) => users[id] ??= User._(id: id, firstName: firstName);
}

Related

Flutter how to convert List<Object> for using ObjectBox

I have a model like this code
class Folder {
int id;
String title;
Color? color;
List<Note> notes;
final user = ToOne<User>();
String? get dbNotes => json.encode(notes);
set dbNotes(String? value) {
notes = json.decode(value!);
}
// ...
}
and I have this error
Cannot use the default constructor of 'Folder': don't know how to initialize param notes - no such property.
How can I convert my List<Object> to store in ObjectBox?
You can define a Note entity and create a To-One relation to the Folder class just like you did with Folder -> User.
Please do this:
you define a default constructor and initialize the variable notes
Folder(){ notes = <Notes>[]; }

A value of type 'xxx?' can't be returned from a function with return type 'xxx' because 'xxx?' is nullable and 'xxx' isn't

I am learning factory method in dart. However I encounter a problem, which says
A value of type 'Person?' can't be returned from a function with return type 'Person' because 'Person?' is nullable and 'Person' isn't.
3-1.dart:13
Here is my code:
void main(List<String> args) {
Person a = new Person("zarkli");
a.say("hello");
}
class Person{
final name;
static final Map<String, Person> _cache = new Map<String,Person>();
factory Person(String name){
if (_cache.containsKey(name)){
return _cache[name]; // error
} else {
final person = new Person.newPerson(name);
_cache[name] = person;
return person;
}
}
Person.newPerson(this.name);
void say(String content){
print("$name: $content");
}
}
Some maps allow null as a value. For those maps, a lookup using this operator cannot distinguish between a key not being in the map, and the key being there with a null value.
This is from the get operator documentation for map. When you use containsKey it could be the case that the key exists but its value is null.
You can use a local variable to store _cache[name] and check if it's null or not. Dart then can promote that local variable and not give you any errors. So your factory will look like this:
factory Person(String name) {
final cached = _cache[name];
if (cached != null) {
return cached;
} else {
final person = Person.newPerson(name);
_cache[name] = person;
return person;
}
}

storing a nested data structure in ObjectBox with type conversion method (.toObjectBox)

I guess the best (only?) way is to create a separate box for the nested structure and then to create a relation from parent (ONE) to children (MANY).
However, my issue is how to implement a .toObjectBox() method to convert my domain data model into the ObjectBox data model. Is there anything alike Either from the dartz package (returns either a left or a right side object), which transports 2 objects simultaneously?
So with the ObjectBox Entities
#Entity()
class Customer {
int id;
#Backlink('customer')
final orders = ToMany<Order>();
}
#Entity()
class Order {
int id;
final customer = ToOne<Customer>();
}
I would store the parent (customer) with the nested data objected (order) in the 2 related boxes
Customer customer = domainData.toObjectBox; // but how to get the order out?
customer.orders.add(Order('Order 1')); // shouldn't this be Order(1)?
final customerId = store.box<Customer>().put(customer);
This is how I typically implement the toObjectBox method. By here, you see that this does not work out because I would have to split parent and nested child apart. While I guess I could achieve this with some spaghetti, I wonder if there is a smart way of doing so since I guess this should be a common pattern (though I haven't found any Q&A on it).
#Entity()
Customer {
int id;
List<Order> orders;
Customer({required this.id, required this.orders});
CustomerObox toObjectBox() {
return CustomerObox(
id: id,
// orders: orders.map((x) => x.toObjectBox()).toList()
);
}
=== UPDATE =====================================
I have meanwhile tried to create a return structure myself, and now I am in the process to get this working.
class Pair<T1, T2> {
final T1 parent;
final T2 child;
Pair({required this.parent, required this.child});
}
#Entity()
class Customer {
int id;
List<Order> orders;
Customer({required this.id, required this.orders});
static Pair<CustomerObox, List<OrderObox>> toObjectBox(Customer cust) {
Pair<CustomerObox, List<OrderObox>>(
parent: CustomerObox(
id: cust.id,
),
child: cust.orders.map((o) => o.toObjectBox()).toList()
);
}
}
So I implemented toObjectBox as shown in the update of my question above.
Inside my local datasource implementation (I use the layers: presentation > business logic (bloc) > domain (models, contracts etc) > repository > datasource) I implemented the following methods
#override
Future<void> storeCustomer(Customer customer) async {
// Pair(T1 parent, T2 child) with T2 being List<OrderObox>
final Pair customerObox = Customer.toObjectBox(customer);
final CustomerObox customerParent = customerObox.parent;
final List<OrderObox> customerChildren = customerObox.child;
for (final child in customerChildren)
customerParent.records.add(child);
// puts both, parent and children:
final id = _sBox.put(customerParent);
}
#override
Future<List<Customer>> getAllCustomers() async {
final List<Customer> parents = [];
final List<CustomerObox> parentsObox = await _sBox.getAll();
for (final p in parentsObox) {
final List<CustomerObox> childrenObox = p.records;
parents.add(SessionJournalDto.fromObjectBox(p));
for (final c in childrenObox)
parents.last.records.add(Order.fromObjectBox(c));
}
return parents;
}

Creating and using Singleton in flutter dart

I am very new to flutter and dart and trying to use singleton instance for global state(?).
which is company info that gets from backend server.
When flutter app starts, send request to the server and get a response and build a singleton instance based on the response.
So I created class
class Company {
static final Company _instance = new Company._internal();
factory Company() {
return _instance;
}
#protected
String name;
#protected
String intro;
String get companyName => name;
String get companyIntro => intro;
void setCompany(String name, String intro) {
name = name;
intro = intro;
}
Company._internal();
}
in main.dart
// companyResult is the response from server
final String companyName = companyResult["name"];
final String companyIntro = companyResult["intro"];
// create singleton instance
var company = Company();
// set company info
company.setCompany(companyName, companyIntro);
// cheking
print(company.companyName)
prints null
What am I doing wrong?
Singletons are better avoided, I would recommend that you use Provider instead and inject a simple object reference on your widget tree, so you can grab that reference whenever you want.
The reason your example prints null is because you are wrongly referencing your variables on setCompany(), the variables name and intro are all the same variable, you are changing the variables internal to the function, not the class variables, in order to fix it change it to:
void setCompany(String name, String intro) {
this.name = name;
this.intro = intro;
}
Also, I would suggest you name your variables _name and _intro, as there's no sense in having a get for a variable that's no private.

How to return an object or struct from a method in MQL?

struct Person {
string FirstName;
string LastName;
};
class Builder {
public:
Person Builder::Build() {
Person person;
person.FirstName = "FirstName";
person.LastName = "LastName";
return person;
};
};
When I compile this it gives me the below error:
'return' - structure have objects and cannot be copied.
I just need to create a struct or class object and return it, I don't want to do any copying.
I tried using & and * combinations but didn't work. I tried with a class instead of struct and it didn't work either.
I also tried with class as shown below:
class Person {
public:
string FirstName;
string LastName;
};
class Builder {
public:
Person* Build() {
Person person;
person.FirstName = "FirstName";
person.LastName = "LastName";
return &person;
};
};
int OnInit()
{
Builder builder;
Person* person = builder.Build();
string firstName = person.FirstName;
return(INIT_SUCCEEDED);
}
And it gives me invalid pointer access when accessing person.FirstName in the OnInit() method at runtime.
Found the answer but how to avoid memory leak? how to destruct the object and its pointer after use?
class cPerson {
public:
string FirstName;
string LastName;
};
class cBuilder {
public:
cPerson* Build() {
cPerson* person = new cPerson();
person.FirstName = "firstname";
return person;
};
};
cBuilder builder;
cPerson* person = builder.Build();
string age = person.FirstName;
You can delete the object by delete(person); and if you are unsure the object is not a null, it is better to check if(CheckPointer(object)==POINTER_DYNAMIC)delete(object);
Overall you should have all such objects as variables with corresponding variable names, or keep them all in a collection and destroy the whole collection at end. You may also create global variable of object (before OnInit, not inside any function) and it is to be deleted at end of program.
Regarding the initial question - you cannot have string inside a struct, only primitives
you should NOT delete static objects - you will get "delete invalid pointer"-message from compiler in print area... therefore the check should be done as Daniel Kniaz answered... but as so as you are creating the object inside the wrapper - you'd better have a check for deletion & delete it in the Destructor of its wrapper (though I doubt, that you really should use here another class for CPerson creation - yuo can create its object in its - CPerson's - Constructor)
First you need a default constructor and copy constructor in your code. second you need to initialize the struct to default value, the struct only allocate space in the memory and not initialize it, so you can end up with cases where a long variable has some weird value like -1823834393939, so always set your struct to default values.
then when you return a class or struct from a function. the copy constructor will be called that copy the values. so if you don't want to return the exact object you've created in your class you don't need to return a reference
struct Person
{
string FirstName;
string LastName;
Person()
{
FirstName = "";
LastName = "";
}
Person(Person &that)
{
FirstName = that.FirstName;
LastName = that.LastName;
}
};
class Builder
{
public:
Person Builder::Build(string argFirstname, string argLastName)
{
Person person;
person.FirstName = argFirstname;
person.LastName = argLastName;
return person;
};
};
void OnStart()
{
Builder b;
Person p = b.Build("Mohammad Hossein", "amri");
Print(p.FirstName + " " + p.LastName);
}
the output will be