Introduction
Dart null safety is one of the most significant language enhancements in Dart 2.12 and further refined in Dart 3. With null safety, Dart developers can write more predictable and less error-prone code by distinguishing between nullable and non-nullable types at compile time. This tutorial walks you through the essential concepts of Dart null safety, shows you how to work with nullable and non-nullable variables, and explains sound null safety guarantees introduced in Dart 3.
What Is Dart Null Safety?
Null safety in Dart enforces that non-nullable variables cannot hold a null value. This eliminates a class of runtime errors known as “null reference” or “NoSuchMethodError: The method ‘…’ was called on null.” By opting into null safety, your code becomes more robust:
Non-nullable types (String, int, MyClass) must always contain a value.
Nullable types (String?, int?, MyClass?) can hold either a value or null.
Dart’s static analyzer checks these annotations at compile time, preventing you from inadvertently assigning or accessing null where it’s not allowed.
Non-nullable vs Nullable Types
In Dart null safety, you explicitly mark a type as nullable by appending a question mark (?):
void main() {
String nonNullable = 'Hello';
String? nullableText = null;
print(nonNullable.length);
print(nullableText?.length);
}Key points:
A String variable must be initialized with a non-null string.
A String? can be null; you must use the null-aware operators (?, ??, !) to work safely.
Using Null-aware Operators
Dart provides several operators to handle nullable types conveniently:
?. (null-aware access): Executes a property or method only if the object isn’t null.
?? (if-null operator): Provides a default when the left side is null.
! (null assertion): Asserts that a value isn’t null (throws at runtime if you’re wrong).
Example:
int? fetchCount() => null;
void displayCount() {
int count = fetchCount() ?? 0;
print('Count: $count');
}Always prefer ?? and ?. over ! to preserve soundness and avoid runtime exceptions.
Sound Null Safety in Dart 3
Dart 3 enforces sound null safety, meaning:
Nullability is part of the type system.
The compiler guarantees that non-nullable types never receive null.
Optimizations can assume no runtime null checks for non-nullable values.
With sound null safety:
Cross-module calls respect null annotations; you can’t override non-nullable parameters with nullable arguments.
Any code compiled with null safety is more performant and reliable.
Migrating to Null Safety
If you have existing Dart code, follow these steps to migrate:
Upgrade SDK constraints in your pubspec.yaml:
environment:
sdk: '>=2.12.0 <4.0.0'
Run the migration tool:
This tool suggests where to add ? or ! and inserts migration comments.
Review and test thoroughly. Address any analyzer warnings.
Opt in by removing the // @dart=2.9 flags and re-running your tests.
By migrating, you gain the full benefits of Dart’s type system and reduce possible runtime null errors.
Vibe Studio

Vibe Studio, powered by Steve’s advanced AI agents, is a revolutionary no-code, conversational platform that empowers users to quickly and efficiently create full-stack Flutter applications integrated seamlessly with Firebase backend services. Ideal for solo founders, startups, and agile engineering teams, Vibe Studio allows users to visually manage and deploy Flutter apps, greatly accelerating the development process. The intuitive conversational interface simplifies complex development tasks, making app creation accessible even for non-coders.
Conclusion
Understanding null safety in Dart 3 is crucial for writing stable, maintainable Flutter and server-side applications. By differentiating between nullable and non-nullable types, using null-aware operators, and leveraging sound null safety, you’ll eliminate a large category of runtime errors.