Consider two classes:
@freezed
abstract class Person with _$Person {
factory Person(String name, int age) = _Person;
}
@freezed
abstract class Company with _$Company {
factory Company(String name, Person director) = _Company;
}
Now say we want to change the director's name, then we would have to write:
Company company;
company = company.copyWith(
director: company.director.copyWith(name: 'New name'),
)
This is not ideal, especially on very nested objects.
Another solution would be to, on Company, generate methods to update director directly:
Company company;
company = company.$director.copyWith(name: 'New name')
Another syntax could be to use Dart's callable classes to achieve:
Company company;
company = company.copyWith.director(name: 'New name')
Which is more readable IMO (and remove that ugly $).
It may even scale with property-specific copy:
company = company.copyWith.name('New name');
company = company.copyWith.director.name('New name');
The benefits of the company.$director.copyWith() approach is that it is more flexible.
It allows:
company.$director(Person(...));
company.$director.copyWith(name: 'Name');
Whereas with company.copyWith.director, trying to achieve the same thing leads to weird names:
company.copyWith.director(Person(...))
company.copyWith.director.copyWith(name: 'Name')
I don't think it is sane to mixt both approaches either.
A third solution could be:
company.copy.director(Person(...))
company.copy.director.with(Person(...))
But this takes an extra reserved keyword slot.
Don't know if this should be part of this issue or if I should create a new one:
@freezed
abstract class Company with _$Company {
factory Company(String name, List<Person> ceos) = _Company;
}
Now say I wanna change the name of one Person in ceos.
Company company;
var ceos = company.ceos.toList();
ceos[1] = ceos[1].copyWith(name: 'NewName');
company = company.copyWith(ceos: ceos);
This is not very intuitive.
Yes, lists and maps are definitely in the scope of this feature. Maybe not in the first release though.
We can think of:
@freezed
abstract class TodoList with _$TodoList {
factory TodoList(List<Todo> todos) = _TodoList;
}
@freezed
abstract class Todo with _$Todo {
factory Todo(String name, bool checked) = _Todo;
}
Which would allow:
TodoList list;
list = list.$todos[0].checked(true);
list = list.$todos.where(...).checked(true);
list = list.$todos([]);
I probably won't work on collections in the first release of such feature.
In the meantime, we can use spread operator and if/for inside collections:
TodoList list;
list = list.todos([...list.todos, Todo()]);
list = list.todos([
for (final todo in list.todos)
todo.copyWith(checked: true),
]);
So it's not too bad.
What happens if director is nullable?
@smiLLe nothing special.
What's your concern?
Let's say director is null in my company object.
And director may have a Person assistant.
what happens if i'd call company.$director.$assistant.copyWith(age: 22);
$director would be nullable
So you'd have:
company.$director?.assistant.copyWith(age: 22);
@rrousselGit ok ... that was too obvious :) i need a coffee. right. now....
No worries, that's a fair question! 😄
Thinking about it, the previous issue of:
company.copyWith.director(Person(...))
company.copyWith.director.copyWith(name: 'Name')
This is actually not a problem.
We could instead have:
company.copyWith(director: Person(...))
company.copyWith.director(name: 'Name')
And this logic applies to deeper copies too:
company.copyWith.director(assistant: Assistant(...));
company.copyWith.director.assistant(age: 22);
I think I'll go with that syntax, as copyWith.director(name: 'Foo') free-up the $director slot for something else
$director may be needed for #60
Most helpful comment
I think I'll go with that syntax, as
copyWith.director(name: 'Foo')free-up the$directorslot for something else$directormay be needed for #60