freezed
freezed copied to clipboard
Non-constant default values
Thank you for the 0.7.0 update, the default values made my code a little bit simpler :)
One thing that I still have to work around are non-constant default values. My type has a String
property called id
that - if left empty - should have a random UUID value by default. I now work around this by adding a second factory method that redirects to the generated one without the id
parameter.
It would be nice if the @Default
annotation could also take in a lambda to generate the default value.
Dart only supports constant default values.
Are you looking for @late
instead?
Ah, I see what you're speaking of.
But the syntax using @Default
would be horrible as we can't use () => something
inside decorators.
What about such syntax?
@freezed
abstract class SuperLate with _$SuperLate {
factory SuperLate([int value]) = _SuperLate;
@late
@override
int get value => super.value ?? Random().nextInt(9999);
}
Yes, that would be great! The late
feature isn't enough because it doesn't allow the user to supply a custom value.
We'll probably need an alternate syntax for unions too:
@freezed
abstract class SuperLate with _$SuperLate {
factory SuperLate([@MyDefault() int value]) = _SuperLate;
factory SuperLate.emoty() = _SuperLate;
}
class MyDefault implements Default<T> {
const MyDefault();
T get defaultValue => Random().nextInt(9999);
}
Kinda verbose though.
It matched Json converter template (in a sense that is good). Depending if this is an instance of Default() or JsonConverter(), will handle generation.
If this gets done, does this mean, we could have default non literals also ?
Hi @rrousselGit. I write as
Ah, I see what you're speaking of. But the syntax using
@Default
would be horrible as we can't use() => something
inside decorators.What about such syntax?
@freezed abstract class SuperLate with _$SuperLate { factory SuperLate([int value]) = _SuperLate; @late @override int get value => super.value ?? Random().nextInt(9999); }
but get error
This is not implemented. Just a thought
@rrousselGit Could this thought came to reality? I got into the same problem when I want to set empty list to a some field, but I can't get it generified in the @Default
, and generator come broken when I tried to implement it writing block body, returning full-signatured constructor call with defaults written in this call. Like this:
factory RepositoryListDataModel({
@Default(false) bool fetchCompleted,
String query,
List<T> data
}) = {
return _RepositoryListDataModel<T>(query: query, data: data ?? <T>[]);
}
Another way to implement this would be to allow a function as Default
's argument :
Other _defaultOther() => Other();
@freezed
abstract class Example with _$Example {
factory Example(@Default(_defaultOther) Other value) = _Example;
}
The problem is, people will most likely want to initialize some parameters based on other parameters
Yeah, probably the most common scenario.
But it may solve issues with json serializable :
For example :
@freezed
abstract class Other with _$Other {
const factory Other() = _Other;
factory Example.fromJson(Map<String, dynamic> json) => _$ExampleFromJson(json);
}
@freezed
abstract class Example with _$Example {
const factory Example(@Default(const Other()) Other value) = _Example;
factory Example.fromJson(Map<String, dynamic> json) => _$ExampleFromJson(json);
}
That produces this error :
Error with
@JsonKey
onvalue
.defaultValue
is_$_Other
, it must be a literal.
I'm clueless on how to achieve this right now.
Another common use case that doesn't need other properties :
Example({
DateTime timestamp,
}) : timestamp = timestamp ?? DateTime.now().add(Duration(days: -30));
The nearest approach I found (though the value isn't initialized at instantiation, but at first read):
factory Example({
@JsonKey(name: 'timestamp') DateTime optionalTimestamp,
}) = _Example;
@late
DateTime get timestamp =>
optionalTimestamp ??
DateTime.now().add(Duration(days: -30));
@rrousselGit
The problem is, people will most likely want to initialize some parameters based on other parameters
I understand your concern, but I feel like it's a way to eagerly address future problems, since there hasn't been such feature request until now, as far as I could evaluate.
I must add that I cannot really find a good use of such functionality. Default values that depend on different parameters are something other than just default values. I cannot find a good reason to make it easy embracing bad practices.
On the other hand, I cannot see how this would play with json_serializable's default value.
@aloisdeniel If we got to that point, why not creating a dummy private constructor and a second one with body, which allows us to define a default value?
const factory Example._(DateTime timestamp) = _Example;
factory Example({
DateTime timestamp,
}) => Example._(timestamp ?? DateTime.now().add(Duration(days: -30)));
Notice that the parameter is not optional in the dummy constructor. This is a very important detail. By making it optional, freezed would not generate assert(timestamp != null)
.
Notice that the parameter is not optional in the dummy constructor. This is a very important detail. By making it optional, freezed would not generate assert(timestamp != null).
Not if you mark the parameter as required
.
@hcbpassos Yes absolutely, it is another valid solution, but I wanted to keep the const
behaviour. :)
Any progress on this? I would like to see this functionality because in a truly null safe app I need default values when I read from the database. It would be awesome to have these default constructors.
@aloisdeniel
Yes absolutely, it is another valid solution, but I wanted to keep the const behaviour. :)
I'm not sure I understand. Do you want a const constructor with non-const default values?
I have kind of the same issue, don't focus on the logic i was just trying to test forms with riverpod state notifier and freeezed,
but I'm not able to continue because DateTime doesn't have a const constructor so I'm thinking is there a possibility to do it or i just need to make a workaround for it?
Im also using Formz package for fields
@freezed
class LoginState with _$LoginState {
factory LoginState({
@Default(Email.pure()) Email email,
@Default(Password.pure()) Password password,
@Default(DateTime()) Date date,
@Default(FormzStatus.pure) FormzStatus status,
}) = _LoginState;
}
Is there another proposed solution to use another freezed class as a default value? Like a default value for a subclass?
Yeah, probably the most common scenario.
But it may solve issues with json serializable :
For example :
@freezed abstract class Other with _$Other { const factory Other() = _Other; factory Example.fromJson(Map<String, dynamic> json) => _$ExampleFromJson(json); } @freezed abstract class Example with _$Example { const factory Example(@Default(const Other()) Other value) = _Example; factory Example.fromJson(Map<String, dynamic> json) => _$ExampleFromJson(json); }
That produces this error :
Error with
@JsonKey
onvalue
.defaultValue
is_$_Other
, it must be a literal.I'm clueless on how to achieve this right now.
I have the same problem when i want to put an model default value:
@freezed
class User with _$User {
const factory User(
{required String id,
required String email,
required String username,
@Default(Profile.empty) Profile profile,}) = _User;
factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
static const empty = User(
id: '',
email: '[email protected]',
username: 'DummyName',);
}
Where Profile
is the model we need:
[SEVERE] json_serializable:json_serializable on lib/features/user/domain/user.entity.dart:
Error with `@JsonKey` on `profile`. `defaultValue` is `_$_Profile`, it must be a literal.
package:thesis_cancer/features/user/domain/user.entity.freezed.dart:297:17
╷
297 │ final Profile profile;
│ ^^^^^^^
╵
[INFO] Running build completed, took 4.6s
[INFO] Caching finalized dependency graph...
[INFO] Caching finalized dependency graph completed, took 80ms
[SEVERE] Failed after 4.7s
pub finished with exit code 1
What about cases with default list value? Like this:
class Account with _$Account {
factory Account({
String name,
@Default([]) List<String> photos, //<--- this is not working
}) = _Account;
}
I just want to init empty list and don't want to use null. How to do that?
@rodion-m you can't use implicit-cast for your default value, instead give your empty list a proper type:
@Default(<String>[]) List<String> photos,
@rodion-m you can't use implicit-cast for your default value, instead give your empty list a proper type:
@Default(<String>[]) List<String> photos,
Thank you, it works!
@Default(Set<String>.unmodifiable(<String>[])) Set<String> photos;
any idea how i can map the above code on a Set? with this code, i do get The constructor being called isn't a const constructor...
@Default(Set<String>.unmodifiable(<String>[])) Set<String> photos;
any idea how i can map the above code on a Set? with this code, i do get The constructor being called isn't a const constructor...
Instead do:
@Default(<String>{})
@rrousselGit Hey. Do you have plans in the near future to solve this? It's been a few months now
No. Feel free to make a pull request
@rrousselGit As soon as I am able to wrap my head around the Dart build system, I'd love to. BTW your efforts, and that of the community, is well appreciated. I'd love to donate to this project to keep it alive even if all I can afford is a few bucks :D
Thank you for the 0.7.0 update, the default values made my code a little bit simpler :)
One thing that I still have to work around are non-constant default values. My type has a
String
property calledid
that - if left empty - should have a random UUID value by default. I now work around this by adding a second factory method that redirects to the generated one without theid
parameter.It would be nice if the
@Default
annotation could also take in a lambda to generate the default value.
Hey @sdebruyn, could you please provide a code example for your workaround? I have a similar use case where I need a random UUID as default value but didn't find a workaround with freezed yet.