On this page26 sections
- Prerequisites
- Connection With Previous Lectures
- Learning Objectives
- 1. What Is a Null-Aware Operator?
- 2. The If-Null Operator ??
- 3. ?? Produces a Non-Nullable Value
- 4. Chaining Several ??
- 5. The If-Null Assignment ??=
- 6. ??= Leaves Existing Values Alone
- 7. Null-Aware Access ?.
- 8. Calling Methods with ?.
- 9. The Result of ?. Is Nullable
- 10. Chaining After ?.
- 11. Combining ?. and ??
- 12. Brackets and ??
- 13. More Null-Aware Operators — Coming Later
- 14. A Real-World Example
- Code Examples
- Common Beginner Mistakes
- Best Practices
- Mini Challenge
- Practice Questions
- Practice Answers
- Quick Revision
- Key Takeaways
- What's Next?
Lecture 14 — Null-Aware Operators
| Category | 3 — Null Safety |
| Lecture | 014 of 140 |
| Estimated video length | 12 min |
| Target | Dart 3.12.2 |
| Level | Beginner |
A nullable value cannot be used directly — Dart stops you before you touch "nothing". The null-aware operators
??,??=, and?.let you use it anyway, safely, in one short expression.
Contents
Prerequisites · Connection · Objectives · Lesson · Code Examples · Common Mistakes · Best Practices · Mini Challenge · Practice Questions · Answers · Takeaways
Every error message in this lecture is real. Each one was produced by running the code shown on Dart 3.12.2, so you will recognize it when you meet it yourself.
Prerequisites
Students should already understand, from Lectures 004–013:
- variables,
var,final, and string interpolation Stringmethods and properties such aslength,trim(), andtoUpperCase()int.tryParseanddouble.tryParse, which returnnullfor invalid text- what
nullis, and why null safety exists - nullable types such as
String?andint?, and why you cannot call a method on a nullable value
Connection With Previous Lectures
In Lecture 013 you met nullable types. A String? may hold text — or null. Because it
might be null, Dart refuses to let you use it like a normal String:
String? nickname = 'Zed';
void main() {
// print(nickname.length); // ✗
}Error: Property 'length' cannot be accessed on 'String?' because it is potentially null.
Try accessing using ?. instead.That keeps the program safe, but it leaves us stuck. A missing nickname should not stop the
program — we want to say "use 'Guest' instead", or "give me the length, if there is one".
Notice the hint in the error: ?.. That is one of the operators this lecture teaches.
Learning Objectives
By the end of this lecture you will be able to:
- Explain what a null-aware operator does
- Supply a default value with the if-null operator
?? - Assign a value only when a variable is
nullwith??= - Read a property or call a method safely with
?. - Chain several calls after
?.and predict where the chain stops - Combine
?.and??to turn a nullable value into a non-nullable one - Use brackets to avoid precedence mistakes with
?? - Recognize the error messages Dart produces for common null-aware mistakes
1. What Is a Null-Aware Operator?
A null-aware operator is an operator that checks for null for you, and does something
sensible when it finds it — instead of crashing or refusing to compile.
Analogy — think of a spare key under the doormat. If your main key is in your pocket, you use it. If it is missing, you use the spare. You never stand locked outside.
| Operator | Name | Meaning |
|---|---|---|
a ?? b | if-null | use a; if a is null, use b |
a ??= b | if-null assignment | if a is null, store b in it |
a?.b | null-aware access | use b on a; if a is null, the result is null |
A note on the examples. Nullable values usually come from outside the program — text a user typed, data from a file. To imitate that, this lecture declares those variables above
main(), or gets them fromtryParse. Insidemain(), Dart can see the value you just stored and would warn that the null check is unnecessary. How Dart tracks this is part of Lecture 016 — Null Safety in Real Code.
2. The If-Null Operator ??
?? gives you a fallback value. If the left side has a value, you get it. If the left
side is null, you get the right side.
String? nickname = 'Zed';
String? middleName;
void main() {
print(nickname ?? 'Guest');
print(middleName ?? 'none');
}Zed
nonenickname holds 'Zed', so the fallback is ignored. middleName was never given a value,
so it is null, and ?? uses 'none' instead.
3. ?? Produces a Non-Nullable Value
When the fallback is not null, the result of ?? can never be null. That means you can
store it in a normal, non-nullable variable:
void main() {
int marks = int.tryParse('abc') ?? 0;
print(marks + 5);
}5int.tryParse('abc') returns null, so marks becomes 0. Because marks is a plain
int, arithmetic works — no error about a value that is "potentially null".
4. Chaining Several ??
You can line up several fallbacks. Dart checks them from left to right and uses the first one
that is not null:
String? nickname;
String? firstName = 'Sara';
void main() {
print(nickname ?? firstName ?? 'Guest');
}Saranickname is null, so Dart moves on. firstName holds 'Sara', so the search stops there
and 'Guest' is never reached.
5. The If-Null Assignment ??=
??= combines ?? with assignment. It stores the right side in the variable only if the
variable is currently null:
String? city;
void main() {
print(city);
city ??= 'Lahore';
print(city);
}null
Lahorecity ??= 'Lahore' is a short way of saying city = city ?? 'Lahore'. It is perfect for
filling in a missing value with a default.
6. ??= Leaves Existing Values Alone
If the variable already holds a value, ??= does nothing:
void main() {
int? score = int.tryParse('85');
score ??= 0;
print(score);
}85'85' is valid text, so score already holds 85. The 0 is never stored. Compare this
with a plain =, which would replace 85 with 0.
7. Null-Aware Access ?.
?. is a careful version of the dot. If the value on the left is not null, it works exactly
like .. If the value is null, Dart skips the property and the result is simply null:
String? nickname = 'Zed';
String? middleName;
void main() {
print(nickname?.length);
print(middleName?.length);
}3
null'Zed' has 3 characters. middleName is null, so there is no length to read — the
expression gives null instead of an error.
8. Calling Methods with ?.
?. works for methods too, not only properties:
String? nickname = 'zed';
String? middleName;
void main() {
print(nickname?.toUpperCase());
print(middleName?.toUpperCase());
}ZED
nullThe method runs only when there is a string to call it on.
9. The Result of ?. Is Nullable
Because ?. can produce null, its result is always a nullable type. nickname?.length
is an int?, not an int:
String? nickname = 'Zed';
void main() {
int? letters = nickname?.length;
print(letters);
}3Trying to store it in a plain int is rejected — see
Common Beginner Mistakes. Section 11 shows how to get a plain
int out of it.
10. Chaining After ?.
You can continue a chain after ?.. If the value is null, Dart stops at the ?. and
skips the rest of the chain — so the later calls can use an ordinary dot:
String? nickname = ' zed ';
String? middleName;
void main() {
print(nickname?.trim().toUpperCase());
print(middleName?.trim().toUpperCase());
}ZED
nullFor nickname, trim() removes the spaces and toUpperCase() runs on the result. For
middleName, Dart sees null at ?. and stops; neither trim() nor toUpperCase() runs.
This is called short-circuiting. One ?. at the point where null can appear is enough.
11. Combining ?. and ??
The two operators work beautifully together. ?. reads safely and may give null; ?? then
replaces that null with a default:
String? nickname = 'Zed';
String? middleName;
void main() {
int nicknameLength = nickname?.length ?? 0;
int middleLength = middleName?.length ?? 0;
print(nicknameLength);
print(middleLength);
}3
0Read middleName?.length ?? 0 as: "the length of middleName if there is one, otherwise
0". The result is a plain int.
12. Brackets and ??
?? has a low precedence: Dart works out most other operators, such as +, before it.
So 10 + bonus ?? 0 means (10 + bonus) ?? 0 — which tries to add 10 to a value that
may be null. Put brackets around the ?? part:
void main() {
int? bonus = int.tryParse('abc');
int total = 10 + (bonus ?? 0);
print(total);
}10The full precedence table is the topic of Lecture 021 — Operator Precedence & Custom Operators.
13. More Null-Aware Operators — Coming Later
Dart has a few more operators with the same ? idea, each built on a feature you have not
met yet:
?[]reads an item from a list or map that may benull— Lists start in Lecture 039.?..is the null-aware cascade — Lecture 020 — Increment, Conditional & Cascade Operators.- The null-aware spread
...?and null-aware elements fill a collection only with values that exist — Lecture 046 — Collection Features.
When you meet them, you will recognize the pattern: "if it's null, skip it."
14. A Real-World Example
A sign-up form where the user may leave fields empty, or type invalid text:
String? enteredName = ' ali ';
String? enteredCity;
String enteredAge = 'twenty';
void main() {
String name = enteredName?.trim().toUpperCase() ?? 'GUEST';
enteredCity ??= 'Unknown';
int age = int.tryParse(enteredAge) ?? 0;
print('Name: $name');
print('City: $enteredCity');
print('Age: $age');
}Name: ALI
City: Unknown
Age: 0?. cleans the name only if it exists, ?? supplies 'GUEST' otherwise, ??= fills in the
missing city, and tryParse with ?? turns bad text into a safe 0. Every value printed is
non-nullable — the program never crashes.
Code Examples
Example 1 — A Default Name
String? username;
void main() {
print('Welcome, ${username ?? 'Guest'}!');
}Welcome, Guest!Example 2 — Filling a Missing Value
String? favouriteColour;
void main() {
favouriteColour ??= 'Blue';
favouriteColour ??= 'Green';
print(favouriteColour);
}BlueExample 3 — Length of a Nullable String
String? message = 'Hello';
String? emptyNote;
void main() {
print(message?.length);
print(emptyNote?.length);
}5
nullExample 4 — A Method Chain
String? email = ' SARA@MAIL.COM ';
void main() {
print(email?.trim().toLowerCase());
}sara@mail.comExample 5 — ?. with a Fallback
String? title;
void main() {
String heading = title?.toUpperCase() ?? 'UNTITLED';
print(heading);
}UNTITLEDExample 6 — Several Fallbacks
String? phone;
String? email;
String? address = 'Street 5, Karachi';
void main() {
print('Contact: ${phone ?? email ?? address ?? 'none'}');
}Contact: Street 5, KarachiHow it works
phoneandemailarenull, so Dart keeps moving right.addressholds text, so the search stops there.'none'is used only if every earlier value isnull.
Example 7 — Student Marks
void main() {
String student = 'Ali';
int math = int.tryParse('80') ?? 0;
int science = int.tryParse('absent') ?? 0;
int english = int.tryParse('85') ?? 0;
int total = math + science + english;
print('Student: $student');
print('Total: $total');
}Student: Ali
Total: 165How it works
- Each mark arrives as text, the way it would from a form.
'absent'is not a number, sotryParsereturnsnulland??turns it into0.- All three marks are plain
intvalues, so they can be added:80 + 0 + 85.
Example 8 — Shopping Total
String? couponCode;
void main() {
String product = 'Notebook';
int price = 500;
int quantity = int.tryParse('') ?? 1;
int total = price * quantity;
print('Product: $product');
print('Quantity: $quantity');
print('Coupon: ${couponCode?.toUpperCase() ?? 'none'}');
print('Total: $total');
}Product: Notebook
Quantity: 1
Coupon: none
Total: 500How it works
- The quantity field was left empty.
int.tryParse('')returnsnull, so the quantity defaults to1. couponCodeisnull, so?.skipstoUpperCase()and??prints'none'.price * quantityis500 * 1.
Example 9 — Bank Balance
double? savedBalance;
void main() {
double balance = savedBalance ?? 1000.0;
double deposit = double.tryParse('250.5') ?? 0;
double withdrawal = double.tryParse('one hundred') ?? 0;
balance = balance + deposit - withdrawal;
print('Balance: $balance');
}Balance: 1250.5How it works
- No balance was saved, so
balancestarts at the default1000.0. '250.5'is valid, so the deposit is250.5.'one hundred'is not a number, so the withdrawal falls back to0instead of crashing.1000.0 + 250.5 - 0gives1250.5.
Example 10 — Temperature Reading
String reading = '31.46';
String? unit;
void main() {
double celsius = double.tryParse(reading) ?? 0;
print('Temperature: ${celsius.toStringAsFixed(1)} ${unit ?? 'C'}');
}Temperature: 31.5 CHow it works
double.tryParse('31.46')succeeds, so the fallback0is not used.toStringAsFixed(1)rounds to one decimal place for display:31.5.- No unit was supplied, so
unit ?? 'C'showsC.
Common Beginner Mistakes
1. Using a plain dot on a nullable value
String? nickname = 'Zed';
void main() {
// print(nickname.length); // ✗
}Error: Property 'length' cannot be accessed on 'String?' because it is potentially null.
Try accessing using ?. instead.Use nickname?.length, or nickname?.length ?? 0 if you need a plain int.
2. Storing the result of ?. in a non-nullable variable
String? nickname = 'Zed';
void main() {
// int letters = nickname?.length; // ✗
}Error: A value of type 'int?' can't be assigned to a variable of type 'int'.?. can give null. Declare int?, or add a fallback: nickname?.length ?? 0.
3. Forgetting the brackets around ??
void main() {
int? bonus = int.tryParse('abc');
// int total = 10 + bonus ?? 0; // ✗
}Error: A value of type 'int?' can't be assigned to a variable of type 'num'.
int total = 10 + bonus ?? 0;
^+ runs first, on a value that may be null. Write 10 + (bonus ?? 0).
4. A fallback of the wrong type
String? nickname;
void main() {
// String label = nickname ?? 0; // ✗
}Error: A value of type 'Object' can't be assigned to a variable of type 'String'.A String or an int gives Object, which is not a String. The fallback must match: nickname ?? 'none'.
5. Using ??= on a final variable
void main() {
final int? score = int.tryParse('abc');
// score ??= 0; // ✗
}Error: Can't assign to the final variable 'score'.??= is an assignment, and a final variable cannot be assigned again. Put the fallback in
the declaration instead: final int score = int.tryParse('abc') ?? 0;.
6. Using ?? on a value that can never be null
void main() {
String name = 'Ali';
print(name ?? 'Guest');
}AliThis runs, but dart analyze reports warnings:
warning - Dead code. Try removing the code, or fixing the code before it so that it can be reached. - dead_code
warning - The left operand can't be null, so the right operand is never executed. Try removing the operator and the right operand. - dead_null_aware_expressionNull-aware operators belong on nullable values only.
7. Expecting ?? to replace an empty string
String? nickname = '';
void main() {
print(nickname ?? 'Guest');
}An empty string '' is a real value — it is not null. ?? reacts to null only, so it
prints an empty line.
Best Practices
- Use
??to give every nullable value a sensible default before you display or calculate with it. - Use
??=to fill in a missing value once, instead of repeating the same??everywhere. - Put a single
?.wherenullcan appear; the rest of the chain is skipped automatically. - End a
?.chain with?? defaultwhen you need a non-nullable result. - Wrap
??in brackets whenever it sits next to other operators. - Do not add null-aware operators to values that cannot be
null— the warning is telling you the code is unnecessary. - Choose fallbacks that make sense to the reader:
'Guest',0,'Unknown'.
Mini Challenge
Sara's profile has a nullable nickname that was never set, and her age arrives as the text
'19'. Print a greeting using the nickname in capital letters, or 'SARA' if there is no
nickname, then print her age next year.
Your program should print exactly:
Hello, SARA!
Next year: 20Try it yourself before opening the solution.
Show solution
String? nickname;
void main() {
String greetingName = nickname?.toUpperCase() ?? 'SARA';
int age = int.tryParse('19') ?? 0;
print('Hello, $greetingName!');
print('Next year: ${age + 1}');
}Hello, SARA!
Next year: 20How it works
nicknameisnull, so?.skipstoUpperCase()and??supplies'SARA'.'19'is valid, soageis19— a plainint, ready for arithmetic.${age + 1}calculates20inside the string.
Practice Questions
Work through these in order — they run from beginner to application to challenge.
Every question gives you exact values, so you can compare your output with the answers.
Declare the nullable variables above main(), as this lecture does.
Beginner
- Declare
String? city;and print it using??with the fallback'Unknown'. - Declare
String? petName = 'Milo';and printpetName ?? 'No pet'. - Declare
String? language;. Use??=to set it to'Dart', then print it.
Application
- Declare
String? word = 'Dart';andString? missing;. Print the length of each using?.. - Store
int quantity = int.tryParse('two') ?? 1;and printquantity * 300. - Declare
String? nickname;. Storenickname?.length ?? 0in a plainintnamedlettersand print it. - Declare
String? code = ' ab12 ';. Print it trimmed and in capital letters using one?.chain. - Store
int? extra = int.tryParse('x');and calculatetotal = 50 + (extra ?? 0). Print the total.
Challenge
- Declare
String? first;,String? second;, andString? third = 'Zayan';. Print the first non-null name, or'Nobody'if all arenull, using one expression. - Write a temperature program. The reading arrives as the text
'abc'. Convert it withdouble.tryParse, defaulting to20.0, then printCelsius: …andFahrenheit: …(celsius * 9 / 5 + 32).
Practice Answers
Question 1 — Solution
String? city;
void main() {
print(city ?? 'Unknown');
}UnknownQuestion 2 — Solution
String? petName = 'Milo';
void main() {
print(petName ?? 'No pet');
}MiloQuestion 3 — Solution
String? language;
void main() {
language ??= 'Dart';
print(language);
}DartQuestion 4 — Solution
String? word = 'Dart';
String? missing;
void main() {
print(word?.length);
print(missing?.length);
}4
nullQuestion 5 — Solution
void main() {
int quantity = int.tryParse('two') ?? 1;
print(quantity * 300);
}300'two' is not a number in digits, so quantity falls back to 1.
Question 6 — Solution
String? nickname;
void main() {
int letters = nickname?.length ?? 0;
print(letters);
}0Question 7 — Solution
String? code = ' ab12 ';
void main() {
print(code?.trim().toUpperCase());
}AB12Question 8 — Solution
void main() {
int? extra = int.tryParse('x');
int total = 50 + (extra ?? 0);
print(total);
}50Without the brackets, 50 + extra would run first and be rejected.
Question 9 — Solution
String? first;
String? second;
String? third = 'Zayan';
void main() {
print(first ?? second ?? third ?? 'Nobody');
}ZayanQuestion 10 — Solution
void main() {
double celsius = double.tryParse('abc') ?? 20.0;
double fahrenheit = celsius * 9 / 5 + 32;
print('Celsius: $celsius');
print('Fahrenheit: $fahrenheit');
}Celsius: 20.0
Fahrenheit: 68.0'abc' cannot be read as a number, so celsius defaults to 20.0. 20.0 * 9 / 5 is
36.0, and 36.0 + 32 is 68.0.
Quick Revision
- What does
a ?? bgive whenaisnull? And when it is not? - What is the difference between
x ??= 5andx = 5? - What does
name?.lengthgive whennameisnull? - Why is the result of
?.always a nullable type? - In
name?.trim().toUpperCase(), why doestoUpperCase()not need its own?.? - How do you turn
name?.lengthinto a plainint? - Why should
10 + (bonus ?? 0)have brackets? - Does
??replace an empty string''?
Key Takeaways
- Null-aware operators check for
nullfor you and react safely. a ?? busesa, orbwhenaisnull; with a non-null fallback, the result is non-nullable.- Several
??can be chained; the first non-null value wins. a ??= bassignsbonly whenaisnull— existing values are left alone.a?.breads a property or calls a method only whenais notnull; otherwise the result isnull, so its type is nullable.- After
?., anullshort-circuits the rest of the chain. ?.followed by??turns a nullable value into a non-nullable one.??has low precedence — wrap it in brackets next to other operators.
What's Next?
Lecture 015 — Null Assertion Operator !. Every operator in this lecture plans for
null. But sometimes you know a value cannot be null, even though its type says it might
be. Next we will learn how ! tells Dart "trust me, there is a value here" — and what happens
when that promise is wrong.