How does the null check operator work in Dart

2k views Asked by At

I'm using this repository to familiarize myself with Amazon's Cognito user system. In the file lib/screens/signup_screen.dart, starting at line 27 there is this piece of code:

          TextFormField(
            keyboardType: TextInputType.emailAddress,
            decoration: InputDecoration(labelText: "Email"),
            controller: _emailController,
            validator: (value) =>
                !validateEmail(value) ? "Email is Invalid" : null,
          ),

However, since we have null safety in Dart since version 2.x, this piece of code fails with the error message: The argument type 'String?' can't be assigned to the parameter type 'String'.

What I got from that is that value may not be equal to null and the code cannot guarantee that as it is. Please correct me if I'm wrong and I'm also hoping someone can explain to me why the code cannot guarantee null safety here. If the textfield is empty, value should be equal to "" instead of null.

Anyhow, I decided to use the ternary operator to fix this issue:

!validateEmail(value == null ? "" : value) ? ...

Which the IDE suggested I correct to:

!validateEmail(value ?? "") ? ...

Dart also suggested to insert a null check as another alternative:

!validateEmail(value!) ? ....

Up till now, I have just been using these fixes as a workaround to produce code fast without trying to understand what's actually going on.

So what's the difference between these methods? Does the ?? in the second method have an integrated == null check and is it exactly the same as writing value == null ? "" : value?

Also, what does the nullcheck value! do? Yes, it checks if value is equal to null, but what does it do for me when value is in fact equal to null?

3

There are 3 answers

2
esentis On BEST ANSWER

The ?? is a null-aware operator , which returns the expression on its left unless that expression’s value is null, in which case it evaluates and returns the expression on its right:

print(1 ?? 3); // <-- Prints 1.
print(null ?? 12); // <-- Prints 12.

Which is the same as doing a ternary operator but more readable.

The bang operator ! casts aways the nullability of the variable, meaning YOU explicitly say that the value CAN'T be null.

Of course, like any cast, using ! comes with a loss of static safety. The cast must be checked at runtime to preserve soundness and it may fail and throw an exception.

For further reading check out the documentation.

0
Ber On

You can start learning more about sound null safety in Dart here: Understanding Null Safety

The null assertion value! is equal to casting to a non-nullable type: value as String. It raises an exception if value is null.

The conditional ?? operator acts as you expect: value ?? "" is a shorthand equivalent for value != null ? value : ""

1
jamesdlin On

What I got from that is that value may not be equal to null and the code cannot guarantee that as it is.

value might be null, but validateEmail requires that its argument is not null.

can explain to me why the code cannot guarantee null safety here. If the textfield is empty, value should be equal to "" instead of null.

Because that's what TextFormField's constructor specifies. Its validator parameter is declared to be a FormFieldValidator<String>?, and FormFieldValidator<T> is defined to be a String? Function(T? value). In other words, the validator callback is declared to be a function that must accept null as an argument. The compiler does not have semantic knowledge to know whether TextFormField will actually invoke that callback with null instead of an empty string in practice.

So what's the difference between these methods? Does the ?? in the second method have an integrated == null check and is it exactly the same as writing value == null ? "" : value?

?? is one of Dart's null-aware operators; it automatically checks for null for you. As stated in the Dart Language Tour:

expr1 ?? expr2

If expr1 is non-null, returns its value; otherwise, evaluates and returns the value of expr2.

So yes, it is equivalent to expr1 == null ? expr2 : expr1 (except that ?? would evaluate expr1 only once, which matters if evaluation has side effects). You should prefer ?? anyway because it better conveys intent.

Also, what does the nullcheck value! do? Yes, it checks if value is equal to null, but what does it do for me when value is in fact equal to null?

value! will throw an exception (specifically a TypeError) if value turns out to be null at runtime. Do not use ! unless you can logically guarantee that value will not be null.