Null Safety

By Syncfusion®, Inc. · Last updated

What null is, how Dart stops null crashes, nullable types, the operators ?. ?? ??= and !, and late variables.

What Is Null Safety?

Null = "no value at all". An empty box.

Null safety = Dart's rule that a variable cannot be empty unless you say it may be.

For example:

  • String name must always hold text
  • String? nickname may hold text, or be empty (null)

Dart checks this before your program runs.

Simple lesson: with null safety, Dart knows which boxes may be empty, and makes you handle them.

A Real-World Example

A student asks the canteen for a samosa.

  • The shopkeeper opens the box and hands one over.
  • One day the box is empty, but nobody checked.
  • The shopkeeper reaches in, finds nothing, and the order fails.

In older languages, using an empty value crashed the app the same way. Dart makes you check the box first, so this crash cannot happen by surprise.

Every Run box on this page runs on the course's Dart runner in your browser.

Some boxes fail on purpose, to show the errors null safety prevents.

What You Will Learn

In this lesson, you will learn:

  1. What null safety is and why Dart added it.
  2. Nullable and non-nullable types.
  3. The operators ?., ??, ??= and !.
  4. late and late final variables.
  5. The most common null mistakes, and how to avoid them.

Why Null Safety Was Introduced

The person who invented null, Tony Hoare, called it his "billion-dollar mistake".

The problem:

  • Any variable could secretly be null.
  • Using it, like name.length, crashed the program while it ran.
  • These crashes often reached users, because nothing warned the programmer.

Dart's fix, added in 2021 and always on since Dart 3:

  • A normal type can never be null. Dart checks it before running.
  • A ? type may be null, and Dart makes you handle that case.

Simple lesson: null safety moves the "empty box" crash from your user's phone to your own screen, before the app ships.

Nullable vs Non-Nullable Types

Non-nullable type = a type that can never hold null: String, int, bool.

Nullable type = a type with ? after it, which can also hold null: String?, int?.

Non-Nullable: Never Empty

A String can never be null

void main() {
  String name = null;
  print(name);
}

Dart says: The value 'null' can't be assigned to a variable of type 'String' because 'String' is not nullable.

Nullable: Maybe Empty

A String? may be null

void main() {
  String? nickname;
  print(nickname);
  nickname = 'Chintu';
  print(nickname);
  nickname = null;
  print(nickname);
}
  • A String? with no value starts as null.
  • It can switch between text and null.

Compare the two kinds of types

Simple lesson: add ? only when a value can really be missing.

The Null Safety Rules

Rule 1: You Must Check Before You Use

This code is refused before it runs:

String? nickname;
print(nickname.length);

Dart says: The property 'length' can't be unconditionally accessed because the receiver can be 'null'.

In plain words: "This box may be empty. Check it first."

Rule 2: A Check Makes It Safe

Type promotion = after you check that a value is not null, Dart treats it as non-nullable.

Check first, then use

void main() {
  String? nickname = 'Chintu';
  if (nickname != null) {
    print('Nickname has ${nickname.length} letters.');
  } else {
    print('No nickname.');
  }
}

Inside the if block, Dart knows nickname is a String, so .length is allowed.

Rule 3: Non-Nullable Variables Need a Value Before Use

int total;
print(total);

Dart says: The non-nullable local variable 'total' must be assigned before it can be used.

Simple lesson: if a value may be null, check it, and Dart lets you use it after the check.

Null Safety Operators

Checking with if every time is long. Dart has four short operators.

Switch the value and watch all four:

Compare:

  • ?. never crashes. It gives null when the box is empty.
  • ?? always gives a value. It uses 'Guest' when the box is empty.
  • ! crashes when the box is empty.

?. Null-Aware Access

?. = "if it is not null, use it. If it is null, give null."

?. gives null instead of crashing

void main() {
  String? nickname;
  print(nickname?.length);
  nickname = 'Chintu';
  print(nickname?.length);
  print(nickname?.toUpperCase());
}

?? Null Coalescing

?? = "use the left value, but if it is null, use the right value instead".

?? gives a default value

void main() {
  String? nickname;
  print('Hello, ${nickname ?? 'Guest'}!');
  nickname = 'Chintu';
  print('Hello, ${nickname ?? 'Guest'}!');

  int? bonusMarks;
  int total = 80 + (bonusMarks ?? 0);
  print('Total: $total');
}

?? is perfect with tryParse() (see Data Types): int.tryParse(text) ?? 0.

??= Null-Aware Assignment

??= = "put this value in, but only if the variable is null right now".

??= fills only an empty box

! Null Assertion

! = "I promise this is not null." Dart believes you.

  • If you are right, it works like a normal value.
  • If you are wrong, the program crashes.

A broken promise crashes the program

void main() {
  int? rollNumber;
  print('Before the crash');
  print(rollNumber! + 1);
}

The error is Null check operator used on a null value.

Use ! only when you are sure a value cannot be null, and Dart just cannot see why.

If you are not sure, use ?., ?? or an if check instead.

Simple lesson: ?. is safe access, ?? is a default, ??= fills an empty box, and ! is a risky promise.

Late Variables

late

late = "this non-nullable variable will get a value later, before I use it".

A late variable gets its value later

void main() {
  late String greeting;
  var hour = 9;
  if (hour < 12) {
    greeting = 'Good morning';
  } else {
    greeting = 'Good afternoon';
  }
  print(greeting);
}
  • greeting is a String, not a String?. It is never null.
  • late tells Dart to trust you about when it gets its value.

If you read a late variable before giving it a value, real Dart crashes with a LateInitializationError.

late final

late final = gets a value later, and only once.

late final: set once, later

void main() {
  late final String topper;
  var scores = 92;
  topper = scores > 90 ? 'Asha' : 'Ravi';
  print('Topper: $topper');
}

Common Use Cases

  • A value decided after some checks, like greeting above.
  • A value that is expensive to work out, and maybe not needed: late final report = buildReport(); runs buildReport() only the first time report is used.
  • Fields in classes that are set up after the object is made (see Classes).

In real Dart, late final x = work(); waits and runs work() only when x is first used.

In this course's runner, it runs straight away. The examples on this page do not depend on the waiting.

Simple lesson: late means "a real value is coming before I use it", so the type does not need ?.

Common Pitfalls

Avoiding Null Errors

  • Prefer non-nullable types. Add ? only when a value can really be missing.
  • Give defaults with ??. name ?? 'Guest' always gives a value.
  • Check with if (x != null). Dart then trusts the value inside the block.
  • Avoid !. Every ! is a possible crash.
  • Use late only when you are sure the value will be set before it is read.

Easy way to remember

  • ? on a type = "this box may be empty".
  • ?. = "peek, and give null if empty".
  • ?? = "or else use this".
  • ! = "trust me", and a crash if you were wrong.

Simple lesson: most null bugs disappear when you use ?? and if checks instead of !.

TL;DR

  1. null = no value. Null safety = only ? types may be null.
  2. Non-nullable = String, never null. Nullable = String?, may be null.

    • Check with if (x != null) to use it. That is type promotion.
  3. Operators:

    • ?. = use it, or give null.
    • ?? = a default value. ??= = assign only if null.
    • ! = "not null, I promise". Crashes if wrong.
  4. late = a non-nullable variable that gets its value later. late final = later, and once.
  5. Avoid null errors = prefer non-nullable types, ?? and checks, and use ! rarely.

Common mistakes

MistakeWrong ideaCorrect ideaWhat goes wrong
String name = null;Any variable can be nullUse String? if it may be nullThe value 'null' can't be assigned.
nickname.length on a String?Dart will just try itCheck first, or use ?.can't be unconditionally accessed.
! everywhere! fixes null errors! only hides them until the program runsNull check operator used on a null value.
? on everythingNullable is saferIt forces checks everywhereLong, messy code.
Reading a late variable too earlylate gives a defaultYou must assign it firstLateInitializationError.
x ?? y when x cannot be null?? is always usefulIt only helps nullable valuesA warning, and dead code.
Expecting ??= to replace a valueIt always assignsIt assigns only when the value is nullThe old value stays.

Check Yourself

Exercises

What to Remember

  • A normal type can never be null. Add ? when a value may be missing.
  • Check with if (x != null), and Dart lets you use the value safely.
  • ?. gives null instead of crashing, and ?? gives a default value.
  • ??= fills a variable only if it is null. ! is a promise that crashes when wrong.
  • late means a non-nullable value will be set before it is used.

Next: Conditional Statements. You will learn how a program makes decisions with if, else and switch.