Please save us from having to do this to capture both undefined and null values. 馃槂 I know undefined isn't a thing in Dart but it is treating "unspecified" as being different than "null".
void main() {
Thing nullThing = Thing(text: null);
print(nullThing.text); // "default"
Thing undefinedThing = Thing();
print(undefinedThing.text); // "default"
}
class Thing{
String text;
Thing({
text: "default"
}) : this.text = text ?? "default";
}
Ideally:
class Thing{
String text;
Thing({
this.text: "default"
});
}
Why? Because when being passed Maps from a NoSQL database that may not have complete data, and then using it to create a new class instance, I have not found a good way to pass undefined
to the class constructor arguments, when myMap['notHere'] == null
I agree that it would be ideal if explicitly passing null
would be treated the same as not passing the parameter at all (which is not undefined
, but I can see the resemblance).
That is, foo({x = "default"}) { ...}
should implicitly be the same as foo({x}) { x ??= "default"; ...}
.
I hope to be able to make that change some time in the future.
If we introduce non-nullable types, it likely becomes much more important. That would also allow for required named arguments (a named argument with a non-nullable type and no default value).
Currently, I personally prefer to use ??
to initialize optional parameters, and only use default values for parameters which I would consider required (where I don't mind passing null
being an error, which is typically boolean named arguments only).\
I would also like to extend this request to @required
in meta
class Project{
final int id;
final String title;
final String iconUrl;
final List<String> tags;
final List<SocialLink> links;
final int taps;
Project({
@required this.id,
@required this.title,
@required this.iconUrl,
@required this.tags,
@required this.links,
@required this.taps,
}) : assert(id != null),
assert(title != null),
assert(iconUrl != null),
assert(tags != null),
assert(links != null);
}
could be
class Project{
final int id;
final String title;
final String iconUrl;
final List<String> tags;
final List<SocialLink> links;
final int taps;
Project({
@required this.id,
@required this.title,
@required this.iconUrl,
@required this.tags,
@required this.links,
@required this.taps,
});
}
null
to parameters results in suppression of the default values.Example references the FloatingActionButton FAB class constructor from Flutter package:
// from Flutter
class FloatingActionButton extends StatefulWidget {
const FloatingActionButton({
Key key,
this.child,
this.tooltip,
this.foregroundColor,
this.backgroundColor,
this.heroTag = const _DefaultHeroTag(),
this.elevation = 6.0,
this.highlightElevation = 12.0,
@required this.onPressed,
this.mini = false,
this.shape = const CircleBorder(),
this.clipBehavior = Clip.none,
this.materialTapTargetSize,
this.isExtended = false,
}) : assert(elevation != null && elevation >= 0.0),
assert(highlightElevation != null && highlightElevation >= 0.0),
assert(mini != null),
assert(shape != null),
assert(isExtended != null),
_sizeConstraints = mini ? _kMiniSizeConstraints : _kSizeConstraints,
super(key: key);
// remaining class definition...
}
The code above is imported and cannot be modified by end user.
The code below is a user trying to create a class that builds a FAB. Each argument of FAB constructor is constructed by a method. Each method can return a value or null
(null
would ideally then defers to FABs defaults).
class MyClass /* extends StatelessWidget */ {
bool getMini(){ /* may return a `bool` or `null` */ }
bool getExtended(){ /* may return a `bool` or `null */` }
double getElevation(){ /* may return a `double` or `null`*/ }
double getHighlightElevation(){ /* may return a `double` or `null`*/ }
Hero getHero(){ /* may return new `Hero` or `null`*/ }
Shape getShape(){ /* may return new `Shape` or `null`*/ }
Clip getClipBehavior(){ /* may return new `Clip` or `null`*/ }
build(_){
return FloatingActionButton(
mini = getMini(),
isExtended = getExtended(),
elevation = getElevation(),
highlightElevation = getHighlightElevation(),
heroTag = getHero(),
shape = getShape(),
clipBehavior = getClipBehavior(),
// remaining FAB arguments (child, onPressed...)
);
}
}
Desired/expected results:
null
is passed as argument to constructor, it ignores null
for default valuegetMini()
returns null
, passes it to the mini
argument of FloatingActionButton
constructor, the null
should be ignored and it's own internal default true
should be used.Actual results:
null
is passed as an argument, defaults values are ignored.getMini()
returns null
, passed to mini
argument of FloatingActionButton
constructor, mini
variable remains null
, ignoring default value and an error will be thrown.null
can't be passed to constructors, to get desired results one constructor must potentially be created for every iteration.Because the above code doesn't work as expected, it must be rewritten a different way. For simple situations, a ternary can be used for multiple constructors, but quickly gets unwieldy.
dart
build(_){
return FloatingActionButton(
dart
// mini = null,
// isExtended = null
// elevation = null
dart
mini = getMini(),
// isExtended = null
// elevation = null
dart
// mini = null,
isExtended = getExtended(),
// elevation = null
dart
mini = getMini(),
isExtended = getExtended(),
// elevation = null
dart
// mini = null,
// isExtended = null
elevation = getElevation(),
dart
mini = getMini(),
// isExtended = null
elevation = getElevation(),
dart
// isExtended = null
isExtended = getExtended(),
elevation = getElevation(),
dart
mini = getMini(),
isExtended = getExtended(),
elevation = getElevation(),
dart
// heroTag, shape, clipBehavior...
// remaining FAB arguments (child, onPressed...)
);
}
Although changes to the constructor would allow it to work since it's imported the end user cannot modify:
FloatingActionButton({this.mini = false});
to FloatingActionButton({bool mini}): thi.mini = mini ?? true;
can't be made as in another package.mini
is false
and a working implementation would be:dart
return FloatingActionButton(
mini = getMini() ?? false,
//...
)
note: Although using a Flutter class within example, the issue is not specific to Flutter.
I'm new to dart and this behavior was a big stumbling block for me, the error I was getting didn't suggest that null value was doing anything. It would be a huge benefit for me and many other beginners for this to be resolved!
@MeamonB I think this will become more clear if and when Dart gets non nullable types. I am personally waiting to see if that happens before I worry about this issue more. Just my opinion.
Same issue here, I've spend many hour trying why the default value didn't get set correctly until I realise if we pass null as a parameters it doesn't get his default value... Very annoying !
This would be very helpful when developing packages that interface with other packages and many other scenarios too but I'm just frustrated because I'm trying to make a package and I would really like this to be added.
I agree with everyone here. Here's a little proposal; keep the current system but add an operator like this : ??=
which would work on undefined and on null values.
class Person {
String firstName;
String lastName;
Person({this.firstName ??= 'John', this.lastname ??= 'Watson'});
}
Would be the same as :
class Person {
String firstName;
String lastName;
Person({
String firstName,
String lastName,
}) : this.firstName = firstName ?? 'John',
this.lastName = lastName ?? 'Watson';
}
Here's another proposal, just like dart optional values in array:
SnackBar(
content: Text(message),
duration: if (retryAction != null) Duration(days: 365),
)
This would really boost productivity instead of having to write the initialization twice
SnackBar( content: Text(message), duration: if (retryAction != null) Duration(days: 365), )
Or like in lists or maps :
SnackBar(
content: Text(message),
if (retryAction != null)
duration: Duration(days: 365),
)
Using if
for optional named parameters is definitely possible. My main worry about it is that it doesn't work for optional positional parameters, at least not the way they are currently defined.
That asymmetry would encourage using named parameters over positional parameters even in situations where positional parameters would otherwise be the better design choice.
If we also allow you to omit no-trailing optional positional parameters, so you an write foo(1,,3)
to call int foo(int x, [int y = 42, int z])
and get the default value for y
, then we can also allow writing foo(1, if (test) 2, 3)
to conditionally omit the parameter.
I think this may be fixed after Dart introduced non-nullable types?
This is sooooo hacky and makes me feel like I'm missing something critical, but it works.
ThemedSwitch({this.key,
@required value,
@required this.onChanged,
this.activeColor,
this.activeTrackColor,
this.inactiveThumbColor,
this.inactiveTrackColor,
this.activeThumbImage,
this.onActiveThumbImageError,
this.inactiveThumbImage,
this.onInactiveThumbImageError,
this.materialTapTargetSize,
this.dragStartBehavior,
this.mouseCursor,
this.focusColor,
this.hoverColor,
this.focusNode,
this.autofocus}) {
//Construct a default widget in order to fall back on default values when the optional params aren't passed
var defaultSwitch = Switch(value: value, onChanged: onChanged);
_switch = Switch(
value: value,
onChanged: onChanged,
activeColor: activeColor != null ? activeColor : defaultSwitch.activeColor,
);
}
Basically, to override the super's functionality without passing on the nulled optional arguments (ie the optional args that were not passed into the constructor), create one widget with the required fields and then use a conditional argument that falls back on the default values from that widget (see activeColor above)
Dart...please make it easier to extend the constructor!
Most helpful comment
I agree with everyone here. Here's a little proposal; keep the current system but add an operator like this :
??=
which would work on undefined and on null values.Would be the same as :