Sign in
Log inSign up
Dealing with nulls in Dart

Dealing with nulls in Dart

Bartosz Wiśniewski's photo
Bartosz Wiśniewski
·Jan 15, 2020

Despite its young age and influence from other programming languages, Dart doesn’t really provide a null safety. Even classic Optional from Java doesn’t exist in Dart, even though is so simple to implement. A lot of people coming from Kotlin or Swift may see this as a huge disadvantage.

But that may change since couple days ago Dart 2.7 was released and with it — comes the preview of null safety. It drastically changes how types interact with nulls. It’s still an early preview so don’t expect too much, but the idea will probably remain the same. You can track the progress of this feature and read the docs behind it.

So let’s explore all the tricks Dart has to make your life easier when dealing with nulls!

Before we start: you can find the code executable here on DartPad

Null coalescing operator

Null coalescing operator or how Dart docs are calling it — if null operator is a double question mark ?? that checks if the object is non-null if it is — it returns its value, otherwise, it evaluates and returns the value of the second expression.

It is commonly used when applying default values or when merging objects. I will take hello world as an example:

void sayHello({ String to }) {
  String secondPart = to ?? "world";
  print("Hello " + secondPart);
}

sayHello(); // -> Hello world
sayHello(to: "Dart"); // -> Hello Dart

?? operator helped us prevent ArgumentError by supplying a default string. We can even make it better with the assignment version of that operator.

void sayHello({ String to }) {
  to ??= "world";
  print("Hello " + to);
}

But of course, this example is a bit stupid since we can just put the default value of the parameter…

void sayHello({ String to = "world" }) {
  print("Hello " + to);
}

Conditional member access

?? is useful, but the most common error we get with null is NoSuchMethodError. We get that because we are trying to access a member on null. To avoid that we can use ?. for accessing members, which will check if the object is null for us. If the object is null then the expression will also return null.

void mail() {
  RegExp emailDomainRegExp = new RegExp('hello@(.+)');

  // Thanks to our conditional member access - error won't be thrown if our RegExp won't match
  String email = "";
  String domain = emailDomainRegExp.matchAsPrefix(email)?.group(1);
  String domain2 = emailDomainRegExp.matchAsPrefix("itwillnotmatch")?.group(1);

  print("domain: $domain, domain2: $domain2");
}

In case our email won’t match we would get an error, but thanks to ?. on it we will just get null, the same as if there was no group(1).

Of course, we can still stack ?. like normal member access, but we should be aware that if we want to avoid errors we need to use them on every call.

void main() {
  String a;

  // Safe
  print(a?.toLowerCase()?.toUpperCase());

  // Will throw NoSuchMethodError
  print(a?.toLowerCase().toUpperCase());
}

Null-aware spread operator

The last trick is so unique that it will probably become the classic job interview question.

void main() {
  var listA = [1, 2, 3, 4, 5];
  var listB;

  // Without spread operator with null check, we would get an error
  print([...listA, ...?listB]); // -> [1, 2, 3, 4, 5]

  // It does not filter nulls
  var listC = [6, 7, null, 9];
  print([...listA, ...?listC]); // -> [1, 2, 3, 4, 5, 6, 7, null, 9]
}

It just doesn’t spread if the object is null.

Meme

Null safety

All the features I mentioned above don’t protect you against nulls, but rather make it convenient to handle. This changes with null safety, which will change how types work. That can be previewed in Dart 2.7, but don’t even try to use it normally yet.

So TLDR: int will become non-nullable type and new int? will be nullable, which will require a check before accessing the data. You will have to opt-in for that behavior and libraries/dependencies can opt-in separately. You can check the examples on this special DartPad. You can also run Dart 2.7 with --enable-experiment=non-nullable to actually compile it.

Summary

Dart at this moment might not have the most secure typing system, but it is good enough. The tricks I just showed you will probably spare you many if (x != null) lines and that’s the point of this post — to avoid writing Dart like Java from 2011.

Dart is also constantly evolving and with null safety on the horizon, it seems to be a great future for Kotlin and Swift developers to move to Dart. Happy coding!