Introducing Null Safety in Dart

Stevanus Wijaya
Tunaiku Tech
Published in
4 min readMar 31, 2021

Null is one of the common errors that we often see in programming. Tony Hoare, a British Computer Scientist who invented this null reference, refers this as the “billion dollar mistake”. This has led to innumerable errors, vulnerabilities, and system crashes, which have probably caused a billion dollars of pains and damages in the last dozen years.

Photo by Polina Zimmerman on Pexels

Dart is not an exception for this error. Although Dart is null safety language, it doesn’t guarantee the variable you have is not null. You can see many thousands of issues caused by nulls in Dart code.

Introduce Null Safety

Before null safety version in Dart, the default of the variable in Dart is nullable. Now, starting from Dart 2.12, Dart introduces sound null safety. Null safety makes all the types in the code is non-nullable by default unless you say it can. This will help us to turn from getting runtime null deference into edit-time analysis errors.

Before Null Safety:

Before Null Safety hierarchy in Dart

After null safety:

After Null Safety hierarchy in Dart

The example is like this:

void main() {
// With null safety these variables can not be null
int accountID = 10;
String name = "John";

// You can define nullable variable by adding ? in type
int? favouriteNumber;
String? favouriteColor;
}

You can try using null safety feature in DartPad. Also you can try switching using null safety and not using it to see the difference between them.

Null Safety Principles

In Dart null safety, there are three core design principles:

  • Non-nullable by default
    With this null safety, by default, the variable is non-nullable unless we say it can be null. This approach is based on the research that found the non-null was the common choice in API (Application Programming Interface) .
  • Incrementally adoptable
    Dart provide the tools to easily migrate from non-null safety into null-safety code. This tools help you to choose what you want and when to migrate your code. With this, we can migrate incrementally, mixing null safe and non-null safe code in the same project. You can learn more about migration using dart tools example like in Flutter in here
  • Fully sound
    Sound null safety mean that the if the variable cannot be null the system will determine that it can never be null. This will enable the compiler optimizations to make your application have smaller binaries, faster execution, and also fewer bugs caused by null referencing.

Working with Null Safety

With null-safety, now you can use some features that help you to handle both non-nullable & nullable variable scenario.

required
Before null safety, we usually use named parameters, especially with classes or functions that take many arguments. However, by default, the variable can be null and this can lead to many problems if the variable is supposed to be not null. We can use annotation @required , but it only helps us to give the warning and doesn’t guarantee the variable cannot be null. Now, with the required syntax we can safely say that the parameters’ value must be passed or it will give an error.

class Book {
String title;
String category;

Book({required this.title, required this.category});
}

late
It is great to have non-nullable variable by default for types. However, how about if we want to have non-nullable objects that will be initialized later? For instance, in this scenario, we can use late keyword to handle this.

class Person {
late id;
String name;

Person({required this.name}) {
id = generateId();
}
}

Handle non-nullable variable
Non-nullable variables give us a better & safe process when we are developing our code. But in the real application, we usually have both non-nullable variables and nullable variables. For example, maybe we have some classes that accepts nullable values but we need to process them in the other function that accepts non-nullable value. How do we handle this? Let’s see the following example.

Imagine that we have person class that might have a favourite book category. And we have functions that give the user some recommendation books based on their favourite category.

Person class:

class Person {
String name;
String? favouriteCategory;

Person({required this.name, this.favouriteCategory});
}

Set book recommendation functions:

void setBookRecommendation(String favouriteCategory) {
//..
}

There are some ways to handle this scenario:

Firstly, we can just add null checking before calling the function like this:

String? favouriteCategory = person.favouriteCategory;
if(favouriteCategory != null) {
setBookRecommendation(favouriteCategory);
}

Other alternative is that we can just assign a default value when the variable value is null:

String? favouriteCategory = person.favouriteCategory;
setBookRecommendation(favouriteCategory ?? “general”);

But, how about if we know and guarantee that our code will call setBookRecommendation method when the value is not null? For this scenario, we can use ! to let know the compiler that for this variable we guarantee the variable must not be null like making a contract. If you break the contract (variable is null), the system will give the error and let you know that you break the contract.

String? favouriteCategory = person.favouriteCategory;
setBookRecommendation(favouriteCategory!);

That’s it about null safety in Dart. Hopefully you can understand the null-safety system and start using it to make your code safer and better. Cheers!

Reference

--

--