Null Safety: Living Without Surprises
Most programming languages have a famous problem: the "null" value. Null is a special value that means "nothing here." If your code tries to use nothing as if it were real data, the program crashes. The inventor of null once called it his "billion-dollar mistake."
Dart solves this with null safety. In plain terms: Dart makes you promise, in advance, whether a variable is allowed to be empty. If you promise it never is, Dart checks your work and stops you at compile time—the moment before the program runs—if you break that promise.
Nullable and non-nullable types
A type describes what kind of data a variable holds, like text (String) or whole numbers (int). By default, Dart types are non-nullable: a String must always hold actual text, never nothing.
To allow emptiness, add a question mark:
String name = 'Ada'; // can never be null
String? nickname; // nullable: holds null for nowHere nickname is a String?—a string or null. The question mark is the whole secret.
The null-aware operators
Dart gives you short tools for working with possibly-empty values.
String? maybeName;
print(maybeName ?? 'guest'); // if null, use 'guest'
maybeName ??= 'Ada'; // assign only if still null
int length = maybeName?.length ?? 0; // safe access, with fallback
print(maybeName!.length); // force: "I know it's not null!"??means "or else": use the left value, but if it's null, use the right one.??=assigns a value only when the variable is currently null.?.calls a method only if the value exists; otherwise the whole expression becomes null.!is the "trust me" operator. It turns a nullable value into a certain one. If you're wrong, the program crashes—so use it only when you genuinely know.
Why this matters
Without null safety, an empty value can sneak deep into your program and explode far from where it started. With null safety, Dart points at the exact line where null might appear, before you ever run the code. The compiler becomes a helpful reviewer that never gets tired.
A small worked example
void main() {
String? email; // no email yet
print('contact: ${email ?? 'unknown'}');
email = 'ada@example.com'; // now it has a value
print('contact: $email');
}Run it and you'll see contact: unknown, then the real address. No crashes, no surprises.
Exercise
What does nickname ?? 'none' evaluate to when nickname holds null?
Show answer
It evaluates to 'none', because ?? returns the value on the right when the value on the left is null.
Exercise
Why is ! considered risky?
Show answer
Because it tells Dart the value is definitely not null. If that's false at runtime, the program crashes immediately.
Spotted a mistake, or code that does not work?