There is no perfect solution, because if a parameter is optional, it needs to have a default value, or be nullable (and have `null` as implicit default value).
A default value must be constant.
If you want to do a computation if no argument is passed, you need to be able to recognize that no argument was passed, which you can only do by recognizing the default value.
I'd use a nullable parameter. Then explicitly passing `null` is a way to trigger the default value too, which makes it easy to forward parameters from one function to another if necessary.
I don't see it as "ugly" to have a nullable type for something, because `null` means "no value provided". Turning that into a value later is reasonable, expectable, idiomatic and therefore readable. People seeing a nullable optional parameter will just think that passing `null` means the same as omitting an argument.
The next alternative would be using a recognizable non-`null` default value, say `A({String a = ""}) : a = a != "" ? a : Uuid().toString();`.
That requires there to be at least one value of the type which cannot be a valid argument. But at that point, you should probably be validating the inputs too, if some of them are not valid or reasonable.
I see no advantage over just using a nullable argument. Users still need to know that some argument values are significant, and now they can't rely on it being `null`, and forwarding parameters has to take extra care to use the same default values.
Then you could have two separate methods:
A({String this.a});
A.fresh() : a = UUid().toString();
Dart doesn't have overloading, so they need different names.
If the behavior is significantly different depending on whether the argument is there or not, like I'd consider "get" vs. "create", I'd prefer two methods.
If it's really just choosing a default value for the same operation, it's fine to use the same function.
Then there is the hacky option of hiding the nullability, but that only works for instance methods:
abstract class A {
factory A() = _A();
void something({String a});
}
class _A implements A {
void something({String? a}) {
a ??= UUid().toString();
// ...
}
}
If you really, really don't want people passing in `null`, even if they are allowed to provide no argument, this can at least prevent them from doing
that in typed code (it still works for dynamic calls).
And again, I don't see that as a goal.
Embrace that nullable and optional go hand in hand, and let `null` mean the same as no value every time.
/L