On this page28 sections
- Prerequisites
- Connection With Previous Lectures
- Learning Objectives
- 1. Two Kinds of Types
- 2. Non-Nullable Is the Default
- 3. Making a Type Nullable with ?
- 4. Nullable Variables Start as null
- 5. A Nullable Variable Can Hold Real Values Too
- 6. Every Type Has a Nullable Version
- 7. Where Nullable Values Come From
- 8. Non-Nullable Fits into Nullable
- 9. Nullable Does Not Fit into Non-Nullable
- 10. What Dart Refuses on a Nullable Value
- 11. What You Can Still Do with a Nullable Value
- 12. Why the ? Matters: A Crash Dart Prevents
- 13. Object, Object?, and dynamic
- 14. Nullable or late?
- 15. Choosing Nullable or Non-Nullable
- 16. 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 13 — Nullable & Non-Nullable Types
| Category | 3 — Null Safety |
| Lecture | 013 of 140 |
| Estimated video length | 10 min |
| Target | Dart 3.12.2 |
| Level | Beginner |
A plain
Stringcan never benull. Add one character,?, and the variable may also hold "no value" — and Dart tracks that difference in every line you write.
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–012:
- declaring variables with an explicit type, with
var, and withlate(Lectures 004–006) - why a non-nullable variable must hold a value before it is read (Lecture 004)
int.tryParseanddouble.tryParse, which returnnullfor bad text (Lecture 010)Object,dynamic, and the difference between compile-time and run-time errors (Lecture 011)- what
nullis, why it causes crashes, and that Dart types are non-nullable by default (Lecture 012)
Connection With Previous Lectures
In Lecture 010 we met this error:
void main() {
var count = int.tryParse('5');
print(count + 1);
}Error: Operator '+' cannot be called on 'int?' because it is potentially null.
print(count + 1);
^The text '5' is a perfectly good number, yet Dart refused. The message names a type we
have never declared ourselves: int?. Lecture 012 told us why Dart guards against null.
This lecture shows how: every type comes in two versions, and Dart always knows which one
a variable has.
Learning Objectives
By the end of this lecture you will be able to:
- Explain the difference between a nullable and a non-nullable type
- Declare nullable variables with
?for any type - Predict the value of a nullable variable declared without a value
- Recognize where nullable values come from, such as
tryParse - Explain why a non-nullable value fits into a nullable variable, but not the other way round
- Recognize what Dart allows — and refuses — on a nullable value
- Compare
Object,Object?, anddynamic, and choose betweenlateand a nullable type - Decide which variables in a program should be nullable
1. Two Kinds of Types
Every type in Dart has two versions: a non-nullable one, which always holds a real value,
and a nullable one, which may also hold null.
Analogy — think of a sign-up form. "Name" is a required field: the form will not go through while it is empty. "Middle name (optional)" may be left blank. A non-nullable type is a required field; a nullable type is an optional one.
| Non-nullable | Nullable | Can hold null? |
|---|---|---|
String | String? | only the ? version |
int | int? | only the ? version |
2. Non-Nullable Is the Default
Every type you have written so far — String, int, double, bool — is non-nullable.
Giving one of them null is a compile-time error:
void main() {
String name = null;
print(name);
}Error: A value of type 'Null' can't be assigned to a variable of type 'String'.
String name = null;
^The value null has its own type, Null, and Null does not fit into String. So when you
see String, you can be sure there is real text inside.
3. Making a Type Nullable with ?
Write ? straight after the type, with no space. Now null is allowed:
void main() {
String name = 'Sara';
String? middleName = null;
print(name);
print(middleName);
}Sara
nullRead String? as "a String or null". Printing a null value shows the word null.
4. Nullable Variables Start as null
You rarely need to write = null. A nullable variable declared without a value starts as
null automatically — your editor even suggests removing = null as unnecessary:
void main() {
String? middleName;
int? score;
print(middleName);
print(score);
}null
nullThe same works at the top level. Compare this with Lecture 004, where String name; outside
a function was an error:
String? nickname;
void main() {
print(nickname);
}nullDart demanded a value only because String had no safe starting value. String? has one:
null.
5. A Nullable Variable Can Hold Real Values Too
A nullable variable is not stuck at null. It can hold a real value, and go back to null
later:
void main() {
int? score;
print(score);
score = 85;
print(score);
score = null;
print(score);
}null
85
nullType safety still applies: score = 'eighty'; would be rejected, because int? means "an
int or null" — nothing else.
6. Every Type Has a Nullable Version
The ? works on any type:
void main() {
String? city;
int? age;
double? temperature;
bool? isPaid;
print('$city, $age, $temperature, $isPaid');
}null, null, null, nullnum?, Object?, and every type you meet later work the same way.
7. Where Nullable Values Come From
You will not only write ? yourself — Dart's own tools hand you nullable values. In
Lecture 010, int.tryParse returned null for bad text. That is why its result type is
int?, and why var infers int?:
void main() {
var good = int.tryParse('42');
int? bad = int.tryParse('forty-two');
print(good);
print(bad);
}42
nullgood is an int? even though it holds 42. Dart decides the type from what tryParse
might return, not from what it happened to return this time.
8. Non-Nullable Fits into Nullable
A real value always fits where "a value or null" is allowed:
void main() {
String city = 'Lahore';
String? lastCity = city;
print(lastCity);
}Lahore9. Nullable Does Not Fit into Non-Nullable
The other direction is refused, even when the value is not null:
void main() {
var count = int.tryParse('5');
int total = count;
print(total);
}Error: A value of type 'int?' can't be assigned to a variable of type 'int'.
int total = count;
^int promises "never null". Dart cannot keep that promise with a value that might be
null, so it stops you before the program runs. Ways to get the value out safely are the
subject of Lectures 014–016.
10. What Dart Refuses on a Nullable Value
Anything that needs a real value is refused — arithmetic, and properties such as length:
String? nickname = 'Ali';
void main() {
print(nickname.length);
}Error: Property 'length' cannot be accessed on 'String?' because it is potentially null.
Try accessing using ?. instead.
print(nickname.length);
^^^^^^nickname holds 'Ali', but its type is String?, and null has no length. The hint
mentions ?. — that operator is the topic of the next lecture.
Note — if you try this with a local variable you have just given a value (
String? name = 'Ali';insidemain()), Dart may acceptname.length: it can see the value is notnull. That cleverness is called type promotion, and we study it in Lecture 016. Values fromtryParseand top-level variables, like the ones here, are not promoted.
11. What You Can Still Do with a Nullable Value
Some things work for every value, including null: printing, putting it in a string,
toString(), and comparing it with null using ==:
void main() {
var age = int.tryParse('twenty');
print(age);
print('Age: $age');
print(age.toString());
print(age == null);
}null
Age: null
null
trueage == null gives a bool answer. Acting on that answer needs if, from Lecture 022;
comparisons get their own lecture, Lecture 018.
12. Why the ? Matters: A Crash Dart Prevents
dynamic (Lecture 011) switches the checks off, so it shows what happens without null
safety:
void main() {
dynamic nickname = null;
print(nickname.length);
}Unhandled exception:
NoSuchMethodError: The getter 'length' was called on null.
Receiver: null
Tried calling: lengthThat is the classic null crash, and it happens while the program runs. With String?,
the same mistake is a compile-time error (section 10); with String, it cannot happen at
all.
13. Object, Object?, and dynamic
In Lecture 011 we promised one more difference between Object and dynamic. Here it is:
Object is non-nullable, like every type without ?:
void main() {
Object item = null;
print(item);
}Error: A value of type 'Null' can't be assigned to a variable of type 'Object'.
- 'Object' is from 'dart:core'.
Object item = null;
^Object? holds any value or null. dynamic accepts null too:
void main() {
Object? item = 'Ali';
dynamic value = 20;
item = null;
value = null;
print(item);
print(value);
}null
null| Type | Any value? | null? | Checked? |
|---|---|---|---|
Object | Yes | No | Yes |
Object? | Yes | Yes | Yes |
dynamic | Yes | Yes | No |
So Object? is the truly checked version of "anything at all".
14. Nullable or late?
In Lecture 006, late let us declare a String now and assign it later, and we said Dart
needs that promise because of null safety. Now you can see why: a String has no safe
starting value, so late says "a real value will arrive before anyone reads it."
- Use
late Stringwhen the value will arrive — it is only delayed. - Use
String?when the value may never arrive — it is genuinely optional.
A middle name may never exist: String?. A cashier's name set at the start of main() will
exist: late String.
15. Choosing Nullable or Non-Nullable
Ask one question for each variable: can "no value" be a real, correct answer here?
| Value | Can it be missing? | Type |
|---|---|---|
| a student's name | No | String |
| a middle name | Yes | String? |
| a price in a shop | No | int or double |
| an exam score not taken yet | Yes | int? |
a number read with tryParse | Yes | int? |
Make a variable nullable only when the answer is yes: non-nullable values need no extra care.
16. A Real-World Example
Sara fills in a sign-up form. Name and age are required; the middle name and phone number are optional, and she leaves them blank. The age arrives as text:
void main() {
String name = 'Sara';
String? middleName;
String? phone;
int? age = int.tryParse('18');
print('Name: $name');
print('Middle name: $middleName');
print('Phone: $phone');
print('Age: $age');
print('Name letters: ${name.length}');
print('Age missing: ${age == null}');
}Name: Sara
Middle name: null
Phone: null
Age: 18
Name letters: 4
Age missing: falsename is non-nullable, so name.length works directly. The blank optional fields are
String?. age is int? because tryParse might fail — we can print it and compare it
with null, but not calculate with it yet.
Code Examples
Example 1 — Non-Nullable and Nullable Side by Side
void main() {
String name = 'Ali';
String? middleName;
print('Name: $name');
print('Middle name: $middleName');
}Name: Ali
Middle name: nullExample 2 — A Nullable Variable Changing Value
void main() {
int? goals;
print(goals);
goals = 2;
print(goals);
goals = null;
print(goals);
}null
2
nullExample 3 — Nullable Versions of Every Type
void main() {
String? city = 'Karachi';
double? temperature;
bool? isPaid = true;
print('City: $city');
print('Temperature: $temperature');
print('Paid: $isPaid');
}City: Karachi
Temperature: null
Paid: trueExample 4 — tryParse Returns a Nullable Type
void main() {
int? quantity = int.tryParse('12');
double? weight = double.tryParse('heavy');
print(quantity);
print(weight);
}12
nullExample 5 — Non-Nullable into Nullable
void main() {
String winner = 'Zayan';
String? lastWinner;
print(lastWinner);
lastWinner = winner;
print(lastWinner);
}null
ZayanExample 6 — Comparing with null
void main() {
var first = int.tryParse('30');
var second = int.tryParse('thirty');
print(first == null);
print(second == null);
}false
trueHow it works
'30'is a valid number, sofirstholds30and is notnull.'thirty'is not, sosecondholdsnull.
Example 7 — Student Marks with a Missing Mark
void main() {
String student = 'Ali';
int? math = int.tryParse('80');
int? science = int.tryParse('');
print('Student: $student');
print('Math: $math');
print('Science: $science');
print('Science missing: ${science == null}');
}Student: Ali
Math: 80
Science: null
Science missing: trueHow it works
- The marks arrive as text from a form, so they are read with
tryParse. - The science box was left empty:
''is not a number, soscienceisnull. - Adding the marks would be refused, because both are
int?— Lecture 014 shows how to handle that.
Example 8 — Shopping Total with an Optional Coupon
void main() {
String product = 'Notebook';
int price = 500;
int quantity = 3;
String? couponCode;
int total = price * quantity;
print('Product: $product');
print('Total: $total');
print('Coupon: $couponCode');
}Product: Notebook
Total: 1500
Coupon: nullHow it works
- Price and quantity are always known, so they are non-nullable — and the multiplication just works.
- A coupon is optional, so
couponCodeisString?and starts asnull.
Example 9 — Bank Balance and the Last Deposit
void main() {
double balance = 1000.0;
double? lastDeposit;
print('Balance: $balance, last deposit: $lastDeposit');
double deposit = 250.5;
balance = balance + deposit;
lastDeposit = deposit;
print('Balance: $balance, last deposit: $lastDeposit');
}Balance: 1000.0, last deposit: null
Balance: 1250.5, last deposit: 250.5How it works
- A new account always has a balance, so
balanceisdouble. - Before any deposit there is no "last deposit", so
lastDepositisdouble?. - The non-nullable
depositfits intolastDeposit(section 8).
Example 10 — Temperature Readings
void main() {
double? morning = double.tryParse('18.5');
double? noon = double.tryParse('ERR');
print('Morning: $morning');
print('Noon: $noon');
print('Noon reading failed: ${noon == null}');
}Morning: 18.5
Noon: null
Noon reading failed: trueHow it works
- A sensor sends readings as text; a broken reading arrives as
'ERR'. double.tryParseturns the good reading into18.5and the broken one intonull.
Common Beginner Mistakes
1. Giving null to a non-nullable variable
void main() {
// String middleName = null; // ✗
}Error: A value of type 'Null' can't be assigned to a variable of type 'String'.If the value can be missing, write String? middleName;.
2. Copying a nullable value into a non-nullable variable
void main() {
var count = int.tryParse('5');
// int total = count; // ✗
}Error: A value of type 'int?' can't be assigned to a variable of type 'int'.The same goes for count + 1. When the text is certainly a number, use int.parse, which
returns int.
3. Expecting var with null to make a nullable type
void main() {
var score = null;
score = 'ninety';
print(score);
}ninetyNo error — that is the problem. Like var score; in Lecture 005, var score = null; makes
a dynamic variable, which accepts anything. Write the type: int? score;.
Best Practices
- Keep types non-nullable unless "no value" is a real, correct answer.
- Write
String? name;rather thanString? name = null;—nullis already the default. - Use
latefor values that will arrive later, and?for values that may never arrive. - Prefer
Object?todynamicwhen a variable can hold anything, includingnull. - Never use
var x = null;— write the nullable type you mean.
Mini Challenge
Zayan joins a reading club. His name is 'Zayan' (required), he has no nickname (optional),
his age arrives as the text '6', and his favourite colour is 'blue' (optional). Choose a
type for each variable, then print them.
Your program should print exactly:
Name: Zayan
Nickname: null
Age: 6
Favourite colour: blueTry it yourself before opening the solution.
Show solution
void main() {
String name = 'Zayan';
String? nickname;
int? age = int.tryParse('6');
String? favouriteColour = 'blue';
print('Name: $name');
print('Nickname: $nickname');
print('Age: $age');
print('Favourite colour: $favouriteColour');
}Name: Zayan
Nickname: null
Age: 6
Favourite colour: blueHow it works
- A name is required, so
nameisString. - Nickname and colour are optional, so both are
String?; the nickname starts asnull.
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.
Beginner
-
Declare a nullable
Stringnamednicknamewithout a value, and print it. -
Declare
int? goals;, print it, assign3, and print it again. -
Which of these lines compile? Explain each.
int age = null; int? year = null; double? price = 9.5; bool isOpen = null;
Application
-
Use
double.tryParseon'72.5'and on'hot'. Store each result in a variable with an explicit type, and print both. -
This program does not compile. Fix it by changing only the type, so that it prints
null:void main() { String city = null; print(city); } -
This program does not compile. Fix it by changing only the type of
total, so that it prints7:void main() { var count = int.tryParse('7'); int total = count; print(total); } -
Predict the output without running it, then check:
void main() { String? a; String b = 'Sara'; print(a); a = b; print(a); a = null; print(a); print(b); } -
Sara opens a bank account with a balance of
1500and has made no withdrawal yet. Choose types forbalanceandlastWithdrawal, and printBalance: 1500andLast withdrawal: null.
Challenge
-
This program does not compile. Explain in one sentence why, then fix it by changing one word so it prints
500:void main() { var price = int.tryParse('250'); print(price * 2); } -
Write a student record for Ali: name
'Ali', roll number12, an email address he has not given, and an exam score for an exam he has not taken yet. Choose the right type for each, and print:Name: Ali Roll number: 12 Email: null Exam score: null
Practice Answers
Question 1 — Solution
void main() {
String? nickname;
print(nickname);
}nullQuestion 2 — Solution
void main() {
int? goals;
print(goals);
goals = 3;
print(goals);
}null
3Question 3 — Solution
Only the two nullable lines compile:
int age = null;— ✗intis non-nullable.int? year = null;— ✓int?allowsnull(thoughint? year;is the usual way).double? price = 9.5;— ✓ a nullable variable can hold a real value.bool isOpen = null;— ✗boolis non-nullable.
Question 4 — Solution
void main() {
double? first = double.tryParse('72.5');
double? second = double.tryParse('hot');
print(first);
print(second);
}72.5
nullQuestion 5 — Solution
Add ? so the type allows null:
void main() {
String? city = null;
print(city);
}nullWriting String? city; gives the same result and is the preferred style.
Question 6 — Solution
count is an int?, so total must be one too:
void main() {
var count = int.tryParse('7');
int? total = count;
print(total);
}7Question 7 — Solution
void main() {
String? a;
String b = 'Sara';
print(a);
a = b;
print(a);
a = null;
print(a);
print(b);
}null
Sara
null
SaraQuestion 8 — Solution
void main() {
int balance = 1500;
int? lastWithdrawal;
print('Balance: $balance');
print('Last withdrawal: $lastWithdrawal');
}Balance: 1500
Last withdrawal: nullQuestion 9 — Solution
tryParse returns int?, and Dart refuses arithmetic on a value that might be null.
The text is certainly a number, so use parse, which returns a non-nullable int:
void main() {
var price = int.parse('250');
print(price * 2);
}500Question 10 — Solution
void main() {
String name = 'Ali';
int rollNumber = 12;
String? email;
int? examScore;
print('Name: $name');
print('Roll number: $rollNumber');
print('Email: $email');
print('Exam score: $examScore');
}Name: Ali
Roll number: 12
Email: null
Exam score: nullEvery student has a name and a roll number, so those are non-nullable. The email is optional and the score does not exist yet, so both are nullable.
Quick Revision
- What is the difference between
StringandString?? - What value does
int? score;start with? - Why is
String? nickname;allowed at the top level, whenString nickname;is not? - What type does
int.tryParsereturn, and why? - Can you assign a
Stringto aString?variable? And aString?to aString? - Name three things you can do with a nullable value, and one thing you cannot.
- How are
Object,Object?, anddynamicdifferent? - When do you choose
lateinstead of a nullable type?
Key Takeaways
- Every type has a non-nullable version (
String) and a nullable version (String?). - Types are non-nullable by default; only a
?type can holdnull. - A nullable variable declared without a value starts as
null— even at the top level. - Tools like
tryParsereturn nullable types, because their result might be missing. - A non-nullable value fits into a nullable variable; a nullable value does not fit into a non-nullable one.
- Dart refuses arithmetic and properties like
lengthon a nullable value, catching null mistakes at compile time instead of run time. Objectexcludesnull;Object?anddynamicallow it, but onlyObject?stays checked.- Use
?for values that may never exist,latefor values that arrive later, and keep everything else non-nullable.
What's Next?
Lecture 014 — Null-Aware Operators. Dart now stops us from adding to an int? or reading
the length of a String?. But real programs still need to use those values. Next we
learn the operators built for exactly that — ?., ??, and ??= — which let us work with
a value that might be null, and supply a fallback when it is.