With Dart development, the issue of compliance versus guidelines is coming up for me frequently as I am learning about null safety.
This morning, I read through a section of Effective Dart that solved a problem for me with null variables.
The relevant section is below.
AVOID late
variables if you need to check whether they are initialized.
Dart offers no way to tell if a late
variable has been initialized or assigned to. If you access it, it either immediately runs the initializer (if it has one) or throws an exception. Sometimes you have some state that’s lazily initialized where late
might be a good fit, but you also need to be able to tell if the initialization has happened yet.
Although you could detect initialization by storing the state in a late
variable and having a separate boolean field that tracks whether the variable has been set, that’s redundant because Dart internally maintains the initialized status of the late
variable. Instead, it’s usually clearer to make the variable non-late
and nullable. Then you can see if the variable has been initialized by checking for null
.
Of course, if null
is a valid initialized value for the variable, then it probably does make sense to have a separate boolean field.
CONSIDER copying a nullable field to a local variable to enable type promotion.
Checking that a nullable variable is not equal to null
promotes the variable to a non-nullable type. That lets you access members on the variable and pass it to functions expecting a non-nullable type. Unfortunately, promotion is only sound for local variables and parameters, so fields and top-level variables aren’t promoted.
One pattern to work around this is to copy the field’s value to a local variable. Null checks on that variable do promote, so you can safely treat it as non-nullable.
class UploadException {
final Response? response;
UploadException([this.response]);
@override
String toString() {
var response = this.response;
if (response != null) {
return 'Could not complete upload to ${response.url} '
'(error code ${response.errorCode}): ${response.reason}.';
}
return 'Could not upload (no response).';
}
}
There’s a nice convention of providing good and bad examples. The good examples are green. The bad examples are red.