Sound null safety — currently in beta — is coming to the Dart language!
When you opt into null safety, types in your code are non-nullable by default, meaning that values can’t be null unless you say they can be. With null safety, your runtime null-dereference errors turn into edit-time analysis errors.
You can try null safety in your normal development environment, migrate your existing code to use null safety, or you can practice using null safety in the web app DartPad with Null Safety, shown in the following screenshot.
Null safety principles
Dart null safety support is based on the following three core design principles:
-
Non-nullable by default. Unless you explicitly tell Dart that a variable can be null, it’s considered non-nullable. This default was chosen after research found that non-null was by far the most common choice in APIs.
-
Incrementally adoptable. You choose what to migrate to null safety, and when. You can migrate incrementally, mixing null-safe and non-null-safe code in the same project. We provide tools to help you with the migration.
-
Fully sound. Dart’s null safety is sound, which enables compiler optimizations. If the type system determines that something isn’t null, then that thing can never be null. Once you migrate your whole project and its dependencies to null safety, you reap the full benefits of soundness —- not only fewer bugs, but smaller binaries and faster execution.
A tour of the null safety feature
New operators and keywords related to null safety
include ?
, !
, and late
.
If you’ve used Kotlin, TypeScript, or C#,
the syntax for null safety might look familiar.
That’s by design: the Dart language aims to be unsurprising.
Creating variables
When creating a variable,
you can use ?
and late
to inform Dart of the variable’s nullability.
Here are some examples of declaring non-nullable variables (assuming you’ve opted into null safety):
// In null-safe Dart, none of these can ever be null.
var i = 42; // Inferred to be an int.
String name = getFileName();
final b = Foo();
If the variable can have the value null
,
add ?
to its type declaration:
int? aNullableInt = null;
If you know that a non-nullable variable will be
initialized to a non-null value before it’s used,
but the Dart analyzer doesn’t agree,
insert late
before the variable’s type:
class IntProvider { late int aRealInt; IntProvider() { aRealInt = calculate(); } }
The late
keyword has two effects:
- The analyzer doesn’t require you to immediately initialize
a
late
variable to a non-null value. - The runtime lazily initializes the
late
variable. For example, if a non-nullable instance variable must be calculated, adding thelate
modifier delays the calculation until the first use of the instance variable.
Using variables and expressions
With null safety, the Dart analyzer generates errors when it finds a nullable value where a non-null value is required. That isn’t as bad as it sounds: the analyzer can often recognize when a variable or expression inside a function has a nullable type but can’t have a null value.
When using a nullable variable or expression,
be sure to handle null values.
For example, you can use an if
statement, the ??
operator,
or the ?.
operator to handle possible null values.
Here’s an example of using the ??
operator
to avoid setting a non-nullable variable to null:
int value = aNullableInt ?? 0; // 0 if it's null; otherwise, the integer
Here’s similar code, but with an if
statement that checks for null:
int definitelyInt(int? aNullableInt) {
if (aNullableInt == null) {
return 0;
}
return aNullableInt; // Can't be null!
}
If you’re sure that an expression with a nullable type isn’t null,
you can add !
to make Dart treat it as non-nullable:
int? aNullableInt = 2;
int value = aNullableInt!; // `aNullableInt!` is an int.
// This throws if aNullableInt is null.
If you need to change the type of a nullable variable —
beyond what the !
operator can do —
you can use the typecast operator (as
).
The following example uses as
to convert a num?
to an int
:
return maybeNum() as int;
Once you opt into null safety,
you can’t use the member access operator (.
)
if the operand might be null.
Instead, you can use the null-aware version of that operator (?.
):
double? d;
print(d?.floor()); // Uses `?.` instead of `.` to invoke `floor()`.
Understanding list, set, and map types
Lists, sets, and maps are commonly used collection types in Dart programs, so you need to know how they interact with null safety. Here are some examples of how Dart code uses these collection types:
- Flutter layout widgets such as
Column
often have achildren
property that’s aList
ofWidget
objects. - The Veggie Seasons sample uses a
Set
ofVeggieCategory
to store a user’s food preferences. - The GitHub Dataviz sample has a
fromJson()
method that creates an object from JSON data that’s supplied in aMap<String, dynamic>
.
List and set types
When you’re declaring the type of a list or set, think about what can be null. The following table shows the possibilities for a list of strings if you opt into null safety.
Type | Can the list be null? |
Can an item (string) be null? |
Description |
---|---|---|---|
List<String> |
No | No | A non-null list that contains non-null strings |
List<String>? |
Yes | No | A list that might be null and that contains non-null strings |
List<String?> |
No | Yes | A non-null list that contains strings that might be null |
List<String?>? |
Yes | Yes | A list that might be null and that contains strings that might be null |
When a literal creates a list or set,
then instead of a type like in the table above,
you typically see a type annotation on the literal.
For example, here’s the code you might use to create
a variable (nameList
) of type List<String?>
and
a variable (nameSet
) of type Set<String?>
:
var nameList = <String?>['Andrew', 'Anjan', 'Anya'];
var nameSet = <String?>{'Andrew', 'Anjan', 'Anya'};
Map types
Map types behave mostly like you’d expect, with one exception: the returned value of a lookup can be null. Null is the value for a key that isn’t present in the map.
As an example, look at the following code.
What do you think the value and type of uhOh
are?
var myMap = <String, int>{'one': 1};
var uhOh = myMap['two'];
The answer is that uhOh
is null and has type int?
.
Like lists and sets, maps can have a variety of types:
Type | Can the map be null? |
Can an item (int) be null? |
---|---|---|
Map<String, int> |
No | No* |
Map<String, int>? |
Yes | No* |
Map<String, int?> |
No | Yes |
Map<String, int?>? |
Yes | Yes |
* Even when all the int values in the map are non-null, when you use an invalid key to do a map lookup, the returned value is null.
Because map lookups can return null, you can’t assign them to non-nullable variables:
// Assigning a lookup result to a non-nullable
// variable causes an error.
int value = <String, int>{'one': 1}['one']; // ERROR
One workaround is to change the type of the variable to be nullable:
int? value = <String, int>{'one': 1}['one']; // OK
Another way to fix the problem —
if you’re sure the lookup succeeds —
is to add a !
:
int value = <String, int>{'one': 1}['one']!; // OK
A safer approach is to use the lookup value only if it’s not null.
You can test its value using
an if
statement or the ??
operator.
Here’s an example of using the value 0
if the lookup returns a null value:
var aMap = <String, int>{'one': 1};
...
int value = aMap['one'] ?? 0;
Enabling null safety
Null safety is currently a beta feature. We recommend requiring and using the most recent beta channel release of the Dart or Flutter SDK. To find the most recent releases, see the beta channel section of the Dart SDK archive, or the Beta channel section of the Flutter SDK archive.
Set the SDK constraints
to require a language version that has null safety support.
For example, your pubspec.yaml
file might have the following constraints:
environment:
sdk: ">=2.12.0-0 <3.0.0"
If you find any issues with null safety please give us feedback.
Known issues
Some parts of the Dart ecosystem still need additional work to migrate to null safety.
The Dart team is currently aware of the following issues:
- Migration of the pub.dev packages owned by the Dart team is nearly complete, but a few are still missing. See pub.dev for null-safe packages from the Dart team.
Where to learn more
For more information about null safety, see the following resources: