Skip to main content

Menu

System onlineAI Tutorials / 2026

AI Tutorials
Knowledge search

Recent · Popular · Tutorials

  • dart3 tutorials
  • java1 tutorial
  • Javascript3 tutorials
  • Node.js Introduction2 tutorials
  • dartdart · General · In depth
  • JavascriptJavascript · Advanced · In depth
  • JavascriptJavascript · Advanced · Mid
  • JavascriptJavascript · Advanced · Short

Find a tutorial

Tell the assistant what you want to learn. It suggests published chapters.

For example: “I want to learn Rust ownership from the start.” The conversation is kept only in this tab.

0 / 2,000 characters

Enter to send, Shift+Enter for a new line. Do not include personal information.

Chapter 4

Null Safety: Living Without Surprises

Before we get to the fix, we need to understand the problem.
Level: GeneralLength: In depth5 min read

Read at another depth

Null Safety: Living Without Surprises

What "null" means, and why it causes trouble

Before we get to the fix, we need to understand the problem.

In most programming languages, a variable can hold a special value called null. Null means "nothing at all" — no text, no number, no object. It is the programming equivalent of an empty envelope. The envelope exists, but there is no letter inside.

The trouble starts when a program tries to use the empty envelope as if it had a letter in it. Imagine this instruction: "Take the name written on the envelope and print it." If the envelope is empty, the program has no idea what to do. In many languages, the result is a crash — the program stops suddenly with an error like "null reference exception." This kind of crash is so common that its inventor once called it his "billion-dollar mistake," because companies have spent enormous amounts of time and money fixing it.

Dart's answer is called null safety. The idea is simple and powerful: the Dart compiler — the program that checks your code before it runs — refuses to let you use a value as if it exists when it might not. Instead of crashing at runtime (while the program is running), you find out about the problem earlier, while writing the code.

Two kinds of variables

With null safety, every type in Dart comes in two flavors:

  • String — a variable that is guaranteed to hold text. Never null. Always safe to use.
  • String? — a variable that holds either text or null. The question mark is a signal: "this might be empty."

The same pattern works for every type: int?, bool?, double?, and so on.

Code example in dart
String name = 'Ada';    // guaranteed to be text
String? nickname;       // starts as null — no nickname yet

print(name);            // fine: name definitely has text
print(nickname);        // fine: printing null is allowed
print(nickname.length); // ERROR: Dart stops you right here

That last line is the heart of null safety. With an ordinary String, asking for its length is always fine. With a String?, the compiler says: "Wait — how do you know there is a string there at all?" You are not allowed to continue until you answer that question in code.

Four tools for working with "maybe null" values

Dart gives you a small toolkit of operators, each with its own symbol. Let's go through them one by one.

1. The null-aware operator `??` — "use this instead"

The double question mark reads as "otherwise." If the value on the left is null, use the value on the right.

Code example in dart
String? maybeName;
print(maybeName ?? 'guest'); // prints: guest

2. The null-aware assignment `??=` — "set it only if it's empty"

This assigns a new value, but only when the current one is null.

Code example in dart
String? maybeName;
maybeName ??= 'Ada'; // now it holds 'Ada'
maybeName ??= 'Grace'; // no effect — it already has a value
print(maybeName); // prints: Ada

3. The safe navigation operator `?.` — "only do this if it exists"

A normal dot (.) says "do this action on the value." The ?. says "do it if the value exists; otherwise the whole expression quietly becomes null."

Code example in dart
String? maybeName = 'Ada';
int length = maybeName?.length ?? 0; // 3

Notice how the two tools combine: maybeName?.length is null if maybeName is null, and then ?? 0 supplies a fallback of zero. This "maybe a value, maybe a fallback" chain is extremely common in real Dart code.

4. The bang operator `!` — "I promise it's there"

Sometimes you know something the compiler cannot prove. The exclamation mark tells Dart: "Trust me, this is not null. If I'm wrong, crash on purpose."

Code example in dart
String? maybeName = 'Ada';
print(maybeName!.length); // 3 — works because we just set it

The ! operator should be rare. It puts the risk of a crash back in your hands, which is exactly what null safety tries to avoid. Use it only when you can clearly explain why the value cannot be null at that moment.

callout note The four symbols at a glance:

  • String? — this variable may be null.
  • a ?? b — use a unless it's null, then use b.
  • a ??= b — assign b to a only if a is currently null.
  • a?.b — access b only if a is not null.
  • a!.b — access b and crash if a is null (use sparingly).

Null safety in the wider flow of a program

Here is how the compiler sees your code. Every time you touch a nullable value, it checks whether you have made the value safe first.

caption: How the compiler decides whether a nullable value can be used safely. alt: A flow diagram starting at declaring a nullable string, followed by a decision about whether null-ness has been ruled out. If not, a compiler error is shown leading to a fix step that loops back to the decision; if yes, the value can be used safely.

One of those fixes deserves special mention: a plain if check. Once Dart sees code that proves a value is not null, it remembers for the rest of that region of code.

Code example in dart
String? nickname;
if (nickname != null) {
  print(nickname.length); // allowed — Dart knows it's not null here
}

This is called flow analysis: the compiler follows the flow of your program the way a careful reader would, and only complains when the proof is missing.

Declaring values that start empty but fill up soon

Sometimes a variable genuinely cannot have a value at the moment of declaration, but you know it will get one before anyone uses it. The keyword late handles this: it tells Dart "I will fill this in before reading it. Check me at runtime, not at compile time."

Code example in dart
late String configFile;

void loadSettings() {
  configFile = 'app.config'; // filled in before anyone reads it
}

void main() {
  loadSettings();
  print(configFile); // fine
}

If you forget to call loadSettings() first, you get a clear, immediate error saying the value was read too early — much easier to diagnose than a mysterious null appearing somewhere later.

callout warning late and ! shift safety checks from compile time to runtime. They are useful escape hatches, but every use is a place where a crash becomes possible again. Prefer ??, ?., and plain null checks whenever you can.

Putting it together: a small worked example

Code example in dart
void main() {
  // A user profile where every field is optional.
  String? email;
  String? phone;

  // The user later adds an email but no phone.
  email ??= 'ada@example.com';

  // Build a contact line safely.
  final contact = [
    email ?? 'no email',
    phone ?? 'no phone',
  ].join(', ');

  print(contact); // ada@example.com, no phone
  print('email length: ${email?.length ?? 0}');
  print('phone length: ${phone?.length ?? 0}'); // 0, no crash
}

Notice how the whole program runs without a single ! and without a single if (x != null). That is the goal of null safety: expressing "maybe missing, here is what to do instead" directly and briefly.

exercise Q: You have String? city;. Write one line that prints "unknown" if city is null and the city's name otherwise. A: print(city ?? 'unknown'); — the ?? operator substitutes "unknown" whenever city holds null.

exercise Q: Rewrite print(name!.length); without using the ! operator, keeping the behavior safe when name is null. A: print(name?.length ?? 0); — safe navigation yields null when name is null, and ?? 0 turns that into a harmless zero.

exercise Q: What happens if you read a late variable before assigning it? A: The program fails at runtime with an error stating the late variable was used before being initialized — the check happens when the program runs, not when it compiles.

estimate minutes: 45

Spotted a mistake, or code that does not work?

Report an issue

Chapter: Null Safety: Living Without Surprises

0 / 2,000 characters

Plain text, for example: the second code sample does not compile. Do not include personal information.

You can't do that