Null Safety in Dart
Sound null safety, mandatory since Dart 3, means every type in Dart's type system is non-nullable by default. Writing String name = 'Alice'; guarantees name can never be null anywhere in the program — not just 'probably won't be,' but statically, provably cannot be, because the compiler verifies it. To allow null, you must opt in explicitly by adding a ? to the type: String? name; declares a variable that can hold either a String or null. This flips the default from most older languages, where every reference type could silently be null unless you added extra validation, and it eliminates the infamous 'null pointer exception' as a runtime surprise for any variable that wasn't explicitly marked nullable.
Cricket analogy: A non-nullable String name is like a team sheet that guarantees exactly eleven named players will walk out, no blank slot is ever legal, whereas String? is like a squad list that explicitly allows a 'TBD' twelfth-man slot.
Null-Aware Operators
Dart provides a family of operators for working with nullable values concisely. The null-aware access operator ?. calls a method or reads a property only if the receiver isn't null, short-circuiting to null otherwise — user?.email returns null instead of throwing if user is null. The if-null operator ?? provides a fallback: name ?? 'Guest' evaluates to name if it's non-null, or 'Guest' otherwise. The null-aware assignment operator ??= only assigns if the variable is currently null: count ??= 0; sets count to 0 only the first time, leaving any existing value untouched on subsequent calls. Together these three operators cover the vast majority of everyday null-handling without ever needing an explicit if (x != null) check.
Cricket analogy: The ?. operator is like a scorer who only logs a boundary if the ball actually crossed the rope, otherwise silently recording nothing rather than crashing the scoreboard trying to log a non-existent boundary.
The Null Assertion Operator (!)
The null assertion operator ! tells the compiler 'trust me, this value is not null right now,' converting a nullable type to its non-nullable counterpart at that point in the code. Writing user!.email asserts that user is definitely non-null and lets you access .email directly; if you're wrong and user actually is null, Dart throws a runtime exception immediately at that line. Because ! bypasses the compiler's static guarantee and replaces it with a runtime promise, it should be used only when you have genuine, verifiable certainty a value is non-null — for example, right after an explicit null check elsewhere in the same function, not as a routine way to silence the analyzer.
Cricket analogy: Using ! is like a captain reviewing a decision without watching the replay first, betting confidently that the call is correct; if the gamble is wrong, the review is burned immediately and there's no recovery.
The late Keyword
Sometimes a non-nullable field genuinely can't be initialized at declaration time but will definitely be set before it's ever used — for example, a field populated inside initState() in a Flutter StatefulWidget, or a value injected by a dependency-injection framework after construction. The late keyword lets you declare such a field as non-nullable while deferring its initialization: late String userName; compiles fine with no immediate value, and Dart only checks at first access that it has actually been assigned. If you read a late variable before it's been set, Dart throws a LateInitializationError at that point, which is a clear, deliberate signal rather than a silent null — but it does mean late shifts a compile-time guarantee into a runtime one, so it's a deliberate trade-off, not a way to avoid thinking about initialization order.
Cricket analogy: A late field is like a stadium announcing the player of the match award before the presentation ceremony has actually happened, the slot is reserved and guaranteed to be filled, just not filled yet at the moment it's declared.
class UserProfile {
String? nickname; // nullable: may or may not be set
late String userId; // non-nullable, but set later
UserProfile();
void init(String id) {
userId = id; // must be set before first read
}
String displayName() {
return nickname ?? 'Anonymous'; // ?? fallback
}
}
void main() {
UserProfile? profile; // starts as null
profile = UserProfile()..init('u-1001');
print(profile?.displayName()); // ?. safe access
print(profile!.userId); // ! assert non-null
int? retryCount;
retryCount ??= 0; // assigns 0 only if currently null
print('Retries: $retryCount');
}Overusing ! to silence 'possibly null' analyzer warnings is a common beginner mistake — every ! is a promise the compiler can no longer verify, and a wrong promise throws a runtime exception exactly where you asserted it. Prefer ?., ??, or an explicit if (value != null) check (which Dart's type promotion recognizes) wherever possible.
- Since Dart 3, sound null safety is mandatory: every type is non-nullable by default unless explicitly suffixed with
?. ?.safely accesses a member only if the receiver is non-null, short-circuiting to null otherwise.??supplies a fallback value when the left-hand expression is null;??=assigns only if the variable is currently null.!asserts a nullable value is non-null right now, throwing a runtime exception immediately if that assertion is wrong.latedeclares a non-nullable variable whose initialization is deferred, throwing aLateInitializationErrorif read before being set.- Overusing
!defeats the purpose of null safety by converting a compile-time guarantee back into a runtime risk.
Practice what you learned
1. In Dart 3, what is the default nullability of a declared type like `String`?
2. What does the `?.` operator do when the receiver is null?
3. What happens if you use `!` on a value that turns out to actually be null?
4. What is the purpose of the `late` keyword?
5. What does `count ??= 0;` do if `count` already holds the value 5?
Was this page helpful?
You May Also Like
Dart Variables and Types
How Dart's var, final, and const declarations work, an overview of Dart's built-in types, and how type inference lets Dart stay statically typed while looking concise.
Your First Dart Program
Write, structure, and run a minimal Dart program from the command line, understand the required main() entry point, and try Dart instantly in the browser with DartPad.
What Is Dart?
An introduction to Dart, the open-source, client-optimized language Google created for building fast, structured apps across mobile, web, desktop, and server targets.
Related Reading
Related Study Notes in Programming
Browse all study notesApache Spark Study Notes
Programming · 30 topics
ProgrammingApache Flink Study Notes
Programming · 30 topics
ProgrammingHadoop Study Notes
Programming · 30 topics
ProgrammingSnowflake Study Notes
Programming · 30 topics
ProgrammingApache Airflow Study Notes
Programming · 30 topics
Programmingdbt (Data Build Tool) Study Notes
Programming · 30 topics