On this page27 sections
- Prerequisites
- Connection With Previous Lectures
- Learning Objectives
- 1. The Null-Safety Toolkit
- 2. Dart Follows Your Local Variables
- 3. Storing null Ends the Promotion
- 4. Top-Level Variables Are Never Promoted
- 5. Why This Lecture's Inputs Live Above main()
- 6. Where Does null Come From?
- 7. Parsing Input with a Default
- 8. An Optional Middle Name
- 9. Default Settings with ??
- 10. Filling Settings with ??= — and Promotion
- 11. ?. and ?? Together
- 12. Choosing Between ??, ?., and !
- 13. late or Nullable?
- 14. Convert Once, Early
- 15. 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 16 — Null Safety in Real Code
| Category | 3 — Null Safety |
| Lecture | 016 of 140 |
| Estimated video length | 12 min |
| Target | Dart 3.12.2 |
| Level | Beginner |
You know every null-safety tool one at a time. Real programs need them together — to turn messy input, optional details, and missing settings into values you can safely use.
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–015:
- variables,
var,final, andlate(Lectures 004–006) - string methods such as
trim(),toUpperCase(),replaceAll(), andlength(Lecture 008) int.tryParse,double.tryParse, andtoStringAsFixed(Lectures 009–010)null, nullable types likeString?, and non-nullable types (Lectures 012–013)- the null-aware operators
??,??=, and?.(Lecture 014) - the null assertion operator
!and the crash it can cause (Lecture 015)
Connection With Previous Lectures
Each null-safety lecture so far showed one tool on a small example. A real program is not that tidy. Imagine a sign-up form:
var ageInput = '19';
String? middleName;
String? chosenTheme;The age is text that may not be a number; the middle name and the theme may never arrive. You have four tools. Which one fits where?
There is also a loose end. Lectures 013 and 014 noted that inside main(),
String? name = 'Ali'; print(name.length); is accepted — Dart "can see" the value is not
null. This lecture explains how, then shows how to choose the right tool each time.
Learning Objectives
By the end of this lecture you will be able to:
- Explain type promotion and why Dart promotes local variables but not top-level ones
- Recognize where
nullcomes from in real programs - Parse text input safely with
tryParseand a default value - Handle optional values and missing settings with
??and??= - Combine
?.and??to get a non-nullable result - Choose between
??,?., and!for a given situation - Decide between
lateand a nullable type - Turn nullable values into non-nullable ones as early as possible
1. The Null-Safety Toolkit
Here is everything from Lectures 012–015 in one table:
| Tool | Meaning | Result |
|---|---|---|
String? | "this may hold null" | a nullable variable |
a ?? b | "use a, or b if a is null" | non-nullable if b is |
a ??= b | "if a is null, store b in it" | a now holds a value |
a?.b | "use b only if a is not null" | nullable |
a! | "I promise a is not null" | non-nullable, or a crash |
The rest of this lecture is about which answer each situation deserves — but first, how Dart decides whether you need one at all.
2. Dart Follows Your Local Variables
Inside main(), Dart reads your code line by line, the way it will run. When you store a
real value in a nullable variable, Dart remembers that it is not null — until you change
it — and lets you use it like a non-nullable one. This is called type promotion: for
those lines, the String? is promoted to String.
void main() {
String? name = 'Ali';
print(name.length);
name = 'Sara Khan';
print(name.length);
}3
9No ?., no !, no error. Each time, Dart can see that the last value stored was a real
string. The work of following values through the code is called flow analysis.
3. Storing null Ends the Promotion
Promotion lasts only as long as Dart is sure. Store null (or anything nullable) and the
variable is a plain String? again:
void main() {
String? name = 'Ali';
print(name.length);
name = null;
// print(name.length); // ✗
}Error: Property 'length' cannot be accessed on 'String?' because it is potentially null.
Try accessing using ?. instead.
Context: Variable 'name' could not be promoted due to an assignment.The Context: line even tells you why: the assignment name = null removed the promise.
The same goes for values Dart cannot see into. int.tryParse(...) might return null, so
its result is never promoted, whatever text you pass in.
4. Top-Level Variables Are Never Promoted
Promotion only works for variables inside main(). A variable declared above main()
stays nullable — even if you assign it on the line before:
String? nickname;
void main() {
nickname = 'Ali';
// print(nickname.length); // ✗
}Error: Property 'length' cannot be accessed on 'String?' because it is potentially null.
Try accessing using ?. instead.A top-level variable can be read and changed from anywhere in the program. Once you write
your own functions (Lecture 030), another piece of code could set it back to null between
any two lines of main(). Dart cannot follow all of that, so it plays safe and never
promotes it. A variable inside main() belongs to main() alone, so Dart can track it.
5. Why This Lecture's Inputs Live Above main()
Real nullable values come from outside the program — a form, a file, a setting. If you
fake one with a value inside main(), Dart sees straight through it:
void main() {
String? chosenTheme = 'dark';
print(chosenTheme ?? 'light');
}darkIt runs, but your editor and dart analyze warn:
warning - The left operand can't be null, so the right operand is never executed. Try removing the operator and the right operand.Dart is right — chosenTheme was promoted, so the ?? can never be used. That is why,
just like Lecture 014, the examples in this lecture declare "outside" values above
main() or get them from tryParse. Then they behave like real input.
Dart can also promote a variable after you check it with == null. Acting on that check
needs if statements — we will see it in Lecture 022.
6. Where Does null Come From?
In real code, null rarely comes from writing null yourself. It comes from three places:
- Conversions that can fail —
int.tryParse('abc')returnsnull. - Optional information — not everyone has a middle name or a nickname.
- Things not decided yet — a setting the user has not chosen.
String? middleName;
String? chosenTheme;
void main() {
var age = int.tryParse('abc');
print(age);
print(middleName);
print(chosenTheme);
}null
null
nullAll three print null, but they mean different things: "invalid", "not provided", and
"not chosen". Each deserves its own handling.
7. Parsing Input with a Default
User input always arrives as text. Here we store it in a String variable and pretend it
came from a form. tryParse gives an int? or double?; ?? turns it into a real number:
void main() {
var quantityInput = 'three';
var priceInput = '249.99';
int quantity = int.tryParse(quantityInput) ?? 1;
double price = double.tryParse(priceInput) ?? 0.0;
print('Quantity: $quantity');
print('Price: $price');
}Quantity: 1
Price: 249.99'three' is not a number, so tryParse returns null and ?? supplies 1. Choose a
default that makes sense: 1 for a quantity, 0 for a score, 0.0 for a decimal. An empty
string '' also fails to parse, while spaces around a number are fine:
int.tryParse(' 25 ') gives 25.
8. An Optional Middle Name
A middle name is genuinely optional, so String? is the honest type. For display, ??
replaces the missing value with something readable. For a full name, replaceAll (Lecture
008) removes the double space an empty middle name would leave:
String? middleName;
void main() {
String firstName = 'Ali';
String lastName = 'Khan';
print('Middle: ${middleName ?? '(none)'}');
var joined = '$firstName ${middleName ?? ''} $lastName';
print(joined.replaceAll(' ', ' '));
middleName = 'Raza';
joined = '$firstName ${middleName ?? ''} $lastName';
print(joined.replaceAll(' ', ' '));
}Middle: (none)
Ali Khan
Ali Raza Khan${...} lets you put the whole ?? expression inside the string. With if statements
(Lecture 022), you will be able to add the middle name only when it exists.
9. Default Settings with ??
A setting the user has not chosen should fall back to the app's default. ?? gives you the
value to use, without changing the original variable:
String? chosenTheme;
String? chosenLanguage = 'Urdu';
void main() {
var theme = chosenTheme ?? 'light';
var language = chosenLanguage ?? 'English';
print('Theme: $theme');
print('Language: $language');
}Theme: light
Language: UrduThe user picked a language, so it wins. They did not pick a theme, so the default is used.
10. Filling Settings with ??= — and Promotion
When you want a variable itself to hold the default from now on, use ??=. Copy the
saved setting into a local variable first, and Dart's flow analysis helps you:
int? savedFontSize;
int? savedVolume = 30;
void main() {
int? fontSize = savedFontSize;
int? volume = savedVolume;
fontSize ??= 14;
volume ??= 50;
print('Font size: $fontSize');
print('Volume: $volume');
print('Bigger font: ${fontSize + 2}');
}Font size: 14
Volume: 30
Bigger font: 16volume already had a value, so ??= left it alone. And look at the last line: after
fontSize ??= 14;, the local fontSize holds a value either way, so Dart promotes it and
fontSize + 2 is allowed. ! does the same for the lines after it (Lecture 015). On a
top-level variable, neither would promote.
11. ?. and ?? Together
?. keeps you safe, but its result is still nullable. Add ?? at the end to get a normal
value:
String? nickname;
void main() {
int nicknameLength = nickname?.length ?? 0;
String shout = nickname?.toUpperCase() ?? 'NO NICKNAME';
print(nicknameLength);
print(shout);
}0
NO NICKNAMERead nickname?.length ?? 0 from left to right: "the length, if there is a nickname —
otherwise 0". This pair is the most common null-safety pattern in real Dart code.
12. Choosing Between ??, ?., and !
Ask one question: "What should happen if the value is missing?"
| If the value is missing… | Use | Example |
|---|---|---|
| use a sensible replacement | ?? | int.tryParse(input) ?? 0 |
| store a default for later | ??= | fontSize ??= 14 |
| skip the operation | ?. | nickname?.toUpperCase() |
| skip, then use a replacement | ?. + ?? | nickname?.length ?? 0 |
| it can't be — I guarantee it | ! | int.tryParse('40')! |
String? nickname = 'Zee';
void main() {
var ageInput = 'unknown';
int age = int.tryParse(ageInput) ?? 0;
String tag = nickname?.toUpperCase() ?? 'GUEST';
int year = int.tryParse('2026')!;
print('$age, $tag, $year');
}0, ZEE, 2026'2026' is written in the code and always parses, so ! is a true promise. With text a
user typed, ! would be a gamble — 'abc' would crash the program with
Null check operator used on a null value. When in doubt, choose ??. It can never crash.
13. late or Nullable?
Both late and String? let a variable start without a value, but they make opposite
claims:
late String name; | String? name; | |
|---|---|---|
| Claim | "a value will arrive before anyone reads it" | "a value may never arrive" |
| Reading it empty | stops the program (Lecture 006) | gives null |
| Using it | like a normal String | needs ??, ?., or ! |
late String welcomeMessage;
String? middleName;
void main() {
welcomeMessage = 'Welcome, Sara';
print(welcomeMessage.length);
print(middleName?.length ?? 0);
}13
0A welcome message is always set before use, so late fits. A middle name might never
exist, so it must be nullable. If "missing" is a normal situation, choose String?.
14. Convert Once, Early
The cleanest programs deal with null as soon as a value enters the program, then work
with non-nullable variables everywhere else:
void main() {
var scoreInput = '85';
final int score = int.tryParse(scoreInput) ?? 0;
var bonus = 10;
var total = score + bonus;
print('Score: $score');
print('Total: $total');
}Score: 85
Total: 95After the first final line, score is a plain int. Nothing below it needs to think
about null at all.
15. A Real-World Example
A sign-up form for Sara. The form's values live above main(). Every one is handled once,
at the top of main(), and the rest of the program uses safe values:
// Raw form input
var nameInput = ' Sara ';
var ageInput = '19';
String? middleName;
String? nickname = 'sar';
String? chosenTheme;
void main() {
// Deal with null once, early
final name = nameInput.trim();
final int age = int.tryParse(ageInput) ?? 0;
final middleText = middleName ?? '(none)';
final displayNick = nickname?.toUpperCase() ?? 'NONE';
final theme = chosenTheme ?? 'light';
// From here on, everything is non-nullable
print('Name: $name');
print('Middle name: $middleText');
print('Age next year: ${age + 1}');
print('Nickname: $displayNick');
print('Theme: $theme');
}Name: Sara
Middle name: (none)
Age next year: 20
Nickname: SAR
Theme: lightThis is the important idea: decide what "missing" means at the edge of the program, then let the rest of the code work with real values.
Code Examples
Example 1 — Parse with a Default
void main() {
var input1 = '42';
var input2 = 'forty-two';
int a = int.tryParse(input1) ?? 0;
int b = int.tryParse(input2) ?? 0;
print(a);
print(b);
}42
0Example 2 — Optional Middle Name
String? middleName = 'Ahmed';
String? otherMiddleName;
void main() {
print(middleName ?? '(none)');
print(otherMiddleName ?? '(none)');
}Ahmed
(none)Example 3 — Length of a Nullable String
String? city = 'Lahore';
String? village;
void main() {
print(city?.length ?? 0);
print(village?.length ?? 0);
}6
0Example 4 — Filling Default Settings
String? language;
bool? notifications = false;
void main() {
language ??= 'English';
notifications ??= true;
print('Language: $language');
print('Notifications: $notifications');
}Language: English
Notifications: falsenotifications was false, not null — and false is a real value, so ??= keeps it.
Example 5 — Convert Once, Then Calculate
void main() {
var widthInput = '12.5';
var heightInput = '4';
final double width = double.tryParse(widthInput) ?? 0.0;
final double height = double.tryParse(heightInput) ?? 0.0;
print('Area: ${width * height}');
}Area: 50.0Example 6 — late Beside Nullable
late String greeting;
String? nickname;
void main() {
greeting = 'Hello, Zayan';
print(greeting.toUpperCase());
print(nickname?.toUpperCase() ?? 'no nickname');
}HELLO, ZAYAN
no nicknameExample 7 — Student Marks from a Form
void main() {
var mathInput = '80';
var scienceInput = '75';
var englishInput = 'absent';
int math = int.tryParse(mathInput) ?? 0;
int science = int.tryParse(scienceInput) ?? 0;
int english = int.tryParse(englishInput) ?? 0;
int total = math + science + english;
print('Math: $math');
print('Science: $science');
print('English: $english');
print('Total: $total');
}Math: 80
Science: 75
English: 0
Total: 155How it works
- Each mark arrives as text, as it would from a form.
'absent'is not a number, sotryParsereturnsnulland?? 0counts it as zero.- After the three conversions, every mark is a plain
int, so adding them is allowed.
Example 8 — Shopping Total with an Optional Coupon
double? couponDiscount;
void main() {
var priceInput = '250.5';
var quantityInput = '2';
double price = double.tryParse(priceInput) ?? 0.0;
int quantity = int.tryParse(quantityInput) ?? 1;
double subtotal = price * quantity;
double total = subtotal - (couponDiscount ?? 0.0);
print('Subtotal: ${subtotal.toStringAsFixed(2)}');
print('Total: ${total.toStringAsFixed(2)}');
}Subtotal: 501.00
Total: 501.00How it works
- Price and quantity are parsed with sensible defaults:
0.0and1. - No coupon was entered, so
couponDiscount ?? 0.0treats it as "no discount". The brackets make Dart work out the discount before subtracting. toStringAsFixed(2)formats the money for display (Lecture 009).
Example 9 — Bank Deposit
void main() {
int balance = 1000;
var depositInput = 'five hundred';
var withdrawInput = '200';
balance = balance + (int.tryParse(depositInput) ?? 0);
balance = balance - (int.tryParse(withdrawInput) ?? 0);
print('Balance: $balance');
}Balance: 800How it works
balancestarts at1000and is never nullable.'five hundred'is not a number, so the deposit counts as0— a bad input changes nothing instead of crashing the program.- The withdrawal
'200'parses correctly, so200is subtracted.
Example 10 — Temperature with a Default Unit
String? unitSetting;
void main() {
var reading = '30';
double celsius = double.tryParse(reading) ?? 20.0;
unitSetting ??= 'F';
double fahrenheit = celsius * 9 / 5 + 32;
print('Reading: $celsius C');
print('Show in: $unitSetting');
print('Converted: $fahrenheit $unitSetting');
}Reading: 30.0 C
Show in: F
Converted: 86.0 FHow it works
double.tryParse('30')gives30.0; with bad input it would fall back to20.0.- The user never chose a unit, so
??=stores'F'inunitSetting. 30.0 * 9 / 5 + 32is86.0.
Common Beginner Mistakes
1. Storing a tryParse result in a non-nullable variable
void main() {
var input = '25';
// int age = int.tryParse(input); // ✗
}Error: A value of type 'int?' can't be assigned to a variable of type 'int'.tryParse can return null. Add a default: int.tryParse(input) ?? 0.
2. Using ! on user input
void main() {
var input = 'abc';
// int age = int.tryParse(input)!; // ✗ crashes while running
}Unhandled exception:
Null check operator used on a null valueThe code compiles, then crashes the moment someone types something unexpected. Use ??.
3. Calling a property on a nullable value
void main() {
String? middleName;
// print(middleName.length); // ✗
}Error: Property 'length' cannot be accessed on 'String?' because it is potentially null.
Try accessing using ?. instead.Write middleName?.length ?? 0.
4. Forgetting the ?? after ?.
void main() {
String? nickname;
// var size = nickname?.length + 1; // ✗
}Error: Operator '+' cannot be called on 'int?' because it is potentially null.nickname?.length is still an int?. Write (nickname?.length ?? 0) + 1.
5. Passing a nullable string to tryParse
void main() {
String? couponInput;
// var discount = double.tryParse(couponInput); // ✗
}Error: The argument type 'String?' can't be assigned to the parameter type 'String'.tryParse only accepts a real String. Give it one: double.tryParse(couponInput ?? '').
6. Using late for a value that may never arrive
void main() {
late String welcome;
// print(welcome); // ✗
}Error: Late variable 'welcome' without initializer is definitely unassigned.Here Dart catches it before running. At the top level it often cannot, and you get a
LateInitializationError while the program runs. If "missing" is possible, use String?.
7. Expecting a top-level variable to be promoted
String? nickname;
void main() {
nickname = 'Ali';
// print(nickname.length); // ✗
}Error: Property 'length' cannot be accessed on 'String?' because it is potentially null.
Try accessing using ?. instead.Only variables inside main() are promoted. Use nickname?.length ?? 0, or copy the value
into a local variable with a default: var name = nickname ?? 'Guest';.
Best Practices
- Handle
nullonce, where the value enters the program, then use non-nullable variables. - Prefer
??with a sensible default for anything a user typed. - Pair
?.with??whenever you need a normal value at the end. - Use
??=when a missing setting should be filled in permanently. - Reserve
!for values you control and can guarantee — never for user input. - Use
String?when "missing" is normal; uselateonly when a value always arrives first. - Declare "outside" nullable values above
main()or get them fromtryParse; copy them into a non-nullable local with??before working with them. - Choose defaults that make sense for the meaning:
1item,0points,'light'theme.
Mini Challenge
Zayan fills in a profile form. Store:
nameInput = ' Zayan 'ageInput = 'six'- above
main():String? citywith no value, andString? favoriteColor = 'blue'
Clean the name, parse the age with a default of 6, show 'Unknown' for a missing city, and
print the favorite colour in capitals (or 'NONE' if missing).
Your program should print exactly:
Name: Zayan
Age: 6
City: Unknown
Favorite color: BLUETry it yourself before opening the solution.
Show solution
String? city;
String? favoriteColor = 'blue';
void main() {
var nameInput = ' Zayan ';
var ageInput = 'six';
final name = nameInput.trim();
final int age = int.tryParse(ageInput) ?? 6;
final cityText = city ?? 'Unknown';
final colorText = favoriteColor?.toUpperCase() ?? 'NONE';
print('Name: $name');
print('Age: $age');
print('City: $cityText');
print('Favorite color: $colorText');
}Name: Zayan
Age: 6
City: Unknown
Favorite color: BLUEHow it works
trim()removes the spaces around the name.'six'is not a number, so?? 6supplies the default.cityisnull, so??shows'Unknown'.favoriteColorexists, so?.toUpperCase()runs and the??is not needed.
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
- Parse
'75'withint.tryParse, defaulting to0. Add5and print the result. - Above
main(), createString? middleNamewith no value. PrintMiddle:followed by the middle name, ornoneif it is missing. - Above
main(), createint? volumewith no value. Use??=to set it to50, then print it.
Application
-
A shop form gives
quantityInput = 'twelve'. Parse it with a default of1, multiply by a price of300, and printTotal: …. -
Above
main(), createString? nickname = 'zee'. Print it in capitals, orNO NICKNAMEif missing. Then setnickname = nulland print the same expression again. -
Above
main(), createString? citywith no value. Print the number of characters in it, using0if it is missing. -
Above
main(), createString? themewith no value andString? language = 'Urdu'. PrintTheme: …andLanguage: …, using the defaults'light'and'English'. -
This program does not compile. Fix it so it prints
Age: 0, without using!:void main() { var ageInput = 'abc'; // int age = int.tryParse(ageInput); // print('Age: $age'); }
Challenge
- Convert two temperature readings,
'30.5'and'', from Celsius to Fahrenheit. Any reading that is not a number counts as20.0. Print each result as… C = … F. - Write an order program. Store
quantityInput = '4'andpriceInput = '120.5', and abovemain()storeString? couponInput = '15'— a discount amount, as text, that may be missing. Calculate the subtotal, the discount (0if the coupon is missing or invalid), and the total. Print all three with two decimal places, labelledSubtotal:,Discount:, andTotal:. Hint:tryParseneeds a realString, so use??before parsing.
Practice Answers
Question 1 — Solution
void main() {
int marks = int.tryParse('75') ?? 0;
print(marks + 5);
}80Question 2 — Solution
String? middleName;
void main() {
print('Middle: ${middleName ?? 'none'}');
}Middle: noneQuestion 3 — Solution
int? volume;
void main() {
volume ??= 50;
print(volume);
}50Question 4 — Solution
void main() {
var quantityInput = 'twelve';
int quantity = int.tryParse(quantityInput) ?? 1;
int total = quantity * 300;
print('Total: $total');
}Total: 300'twelve' is written in words, so tryParse returns null and the quantity becomes 1.
Question 5 — Solution
String? nickname = 'zee';
void main() {
print(nickname?.toUpperCase() ?? 'NO NICKNAME');
nickname = null;
print(nickname?.toUpperCase() ?? 'NO NICKNAME');
}ZEE
NO NICKNAMEQuestion 6 — Solution
String? city;
void main() {
print(city?.length ?? 0);
}0Question 7 — Solution
String? theme;
String? language = 'Urdu';
void main() {
print('Theme: ${theme ?? 'light'}');
print('Language: ${language ?? 'English'}');
}Theme: light
Language: UrduQuestion 8 — Solution
void main() {
var ageInput = 'abc';
int age = int.tryParse(ageInput) ?? 0;
print('Age: $age');
}Age: 0?? 0 turns the int? from tryParse into an int. Using ! would compile too, but it
would crash on 'abc'.
Question 9 — Solution
void main() {
var reading1 = '30.5';
var reading2 = '';
double c1 = double.tryParse(reading1) ?? 20.0;
double c2 = double.tryParse(reading2) ?? 20.0;
print('$c1 C = ${c1 * 9 / 5 + 32} F');
print('$c2 C = ${c2 * 9 / 5 + 32} F');
}30.5 C = 86.9 F
20.0 C = 68.0 FThe empty reading is not a number, so it falls back to 20.0.
Question 10 — Solution
String? couponInput = '15';
void main() {
var quantityInput = '4';
var priceInput = '120.5';
final int quantity = int.tryParse(quantityInput) ?? 1;
final double price = double.tryParse(priceInput) ?? 0.0;
final double discount = double.tryParse(couponInput ?? '') ?? 0.0;
final subtotal = price * quantity;
final total = subtotal - discount;
print('Subtotal: ${subtotal.toStringAsFixed(2)}');
print('Discount: ${discount.toStringAsFixed(2)}');
print('Total: ${total.toStringAsFixed(2)}');
}Subtotal: 482.00
Discount: 15.00
Total: 467.00couponInput ?? '' gives tryParse a real String. If the coupon were missing, the empty
string would fail to parse, and the second ?? 0.0 would make the discount zero. Both cases
are handled in a single line.
Quick Revision
- Name three places
nullcomes from in real programs. - What does
int.tryParse(input) ?? 0give you, and what type is it? - What is the difference between
??and??=? - Why does
nickname?.lengthstill need??before you add to it? - When is
!a safe choice? When is it a bad one? - When should you choose
String?instead oflate String? - Why handle
nullonce, early, instead of everywhere? - What is type promotion, and what ends it?
- Why does Dart never promote a top-level variable?
- After
fontSize ??= 14;on a local variable, why can you writefontSize + 2?
Key Takeaways
- Type promotion: inside
main(), Dart follows your code and treats a nullable variable as non-nullable while it can see the value is notnull— after a real value,??=, or!. - Storing
nullends promotion, and top-level variables are never promoted — which is why this lecture's "outside" values live abovemain(). nullcomes from failed conversions, optional information, and choices not made yet.tryParse(...) ?? defaultis the standard way to read numbers from text input.??gives a value to use now;??=stores a default in the variable itself.?.keeps you safe but stays nullable — finish with??to get a normal value.!is for values you control; for user input it is a crash waiting to happen.- Use a nullable type when "missing" is normal, and
lateonly when a value always arrives before it is read. - Handle
nullonce, at the edge of the program, then work with non-nullable variables. - Promotion after a
== nullcheck needsifstatements — that comes in Lecture 022.
What's Next?
Lecture 17 — Arithmetic & Assignment Operators. This lecture closes Null Safety. Along
the way you used +, -, *, / and even one special assignment operator, ??=. Next we
start Category 4 — Operators, and look properly at Dart's arithmetic operators and the
shortcut assignment operators such as += that update a variable in one step.