Dart Fundamentals: The Language Behind Flutter · Dart Basics: Variables, Null Safety, Functions and Collections
Sound Null Safety
In Dart, a String can never be null; a String? can. Learn nullable types, the ?. ?? and ??= operators, flow analysis that promotes types after checks, late variables, and why the ! operator should be rare.
What you'll build
A user profile formatter that deals safely with optional data (missing nickname, missing phone number) without a single null crash.
The idea
Dart has sound null safety: types are non-nullable by default.
String namecan never holdnull.String? nicknamecan be aStringornull, and the compiler forces you to handle the null case before using it as aString.
Tools for nullable values:
| Syntax | Meaning |
|---|---|
a?.length |
null if a is null, otherwise a.length |
a ?? 'default' |
a if not null, else the default |
a ??= 'value' |
assign only if a is null |
if (a != null) { a.length } |
type promotion: inside the check a is a String |
a! |
"trust me, it's not null"; throws if it is |
late String token; |
non-nullable, but assigned later (checked at runtime) |
"Sound" means that if the type says non-null, it really is non-null at runtime, so the compiler can optimise and you can't get a null-reference error from those values.
The code
class Profile {
final String name;
final String? nickname;
final String? phone;
Profile(this.name, {this.nickname, this.phone});
}
String displayName(Profile p) => p.nickname ?? p.name;
String contactLine(Profile p) {
final phone = p.phone;
if (phone == null) return '${p.name}: no phone';
return '${p.name}: ${phone.replaceAll(' ', '')}'; // promoted to String inside the check
}
late final String greeting; // non-nullable, initialised later
void main() {
final people = [
Profile('Ana Silva', nickname: 'Ana', phone: '+351 912 345 678'),
Profile('Benjamin Okafor'),
];
for (final p in people) {
print('${displayName(p)} -> ${contactLine(p)}');
print(' nickname length: ${p.nickname?.length ?? 0}');
}
String? theme;
theme ??= 'dark'; // only assigns when null
theme ??= 'light'; // ignored: already set
print('theme: $theme');
greeting = 'Welcome back';
print(greeting);
String? missing;
try {
print(missing!.length); // ! throws if the value is null
} on TypeError {
print('the ! operator threw a TypeError: the value was null');
}
}
Output
Ana -> Ana Silva: +351912345678 nickname length: 3 Benjamin Okafor -> Benjamin Okafor: no phone nickname length: 0 theme: dark Welcome back the ! operator threw a TypeError: the value was null
Run this on your computer — see “Get Started” for setup.
Common mistakes
- Sprinkling
!to silence errors. Each one is a potential crash; prefer??,?.or anifcheck. - Expecting promotion on fields. Promotion works on local variables (and private final fields); copy a field to a local first, as
contactLinedoes. - Overusing
late. If the value is never assigned before use, you get a runtime error instead of a compile-time one. - Making everything nullable "just in case". Non-null types give you guarantees; use
?only when absence is meaningful.
Key points
- Types are non-nullable by default;
T?allows null. ?.,??,??=handle nulls concisely;if (x != null)promotesx.!asserts non-null and throws if wrong: use rarely.latedefers initialisation of non-nullable variables.
Further reading: Sound null safety, Understanding null safety.
Practice questions
What does 'sound null safety' mean in Dart?
Answer
Types are non-nullable unless marked with ?, and the compiler guarantees that a non-nullable variable can never contain null at runtime, so null-reference errors can only come from nullable values you explicitly handle (or from ! / late misuse).
Why doesn't type promotion work on a nullable class field?
Answer
Another piece of code (or a getter override) could change the field between the null check and its use, so the compiler can't prove it's still non-null. Copy it into a local variable first (promotion also works on private final fields).
Open in the app to mark lessons complete, take notes and track your progress.