← Articles

Understanding Dart Null Safety

By Mark · 29 June 20260 views

Understanding Dart Null Safety

Null safety is one of the most impactful features added to the Dart language. Introduced in Dart 2.12 and now mandatory in all modern Flutter projects, null safety eliminates an entire class of runtime crashes by moving null-related errors to compile time. If you are coming from a language without null safety (JavaScript, older Dart, or Java), the shift in thinking is significant but well worth it.

The Problem Null Safety Solves

Before null safety, any variable in Dart could be null at runtime. You had to defensively check for null everywhere, and forgetting even one check could crash your app with a NoSuchMethodError. These bugs were notoriously hard to find because the crash site was often far from where the null was introduced.

Null safety moves this class of error to compile time. If the type system says a variable is non-nullable, you cannot assign null to it, and you cannot receive null from it. The compiler enforces this statically.

Nullable vs Non-Nullable Types

In null-safe Dart, every type is non-nullable by default. To make a type nullable, append a question mark:

String name = 'Alice';   // cannot be null
String? nickname;        // can be null, defaults to null
int age = 30;            // cannot be null
int? score;              // can be null

Attempting to assign null to a non-nullable variable is a compile-time error:

String name = null; // ERROR: A value of type 'Null' can't be assigned to 'String'

Working with Nullable Values

When a value might be null, Dart gives you several tools to handle it safely.

Null-aware operators

String? nickname;

// If-null operator: use right side when left is null
String display = nickname ?? 'Anonymous';

// Null-aware access: returns null instead of throwing
int? len = nickname?.length;

// Null-aware assignment: assign only if currently null
nickname ??= 'Anonymous';

Null checks and promotion

When you check a nullable variable for null, Dart automatically promotes it to the non-nullable type inside the block:

String? nickname;

if (nickname != null) {
  // nickname is promoted to String here
  print(nickname.toUpperCase());
}

This promotion is a powerful feature — you check once and work with the non-nullable type freely.

The Late Keyword

Sometimes you know a variable will be initialized before it is first read, but you cannot initialize it at declaration time. Use late to tell the compiler to trust you:

class UserProfile {
  late String uid;

  void initialize(String id) {
    uid = id;
  }

  void display() {
    print(uid); // safe only if initialize() was called first
  }
}

If you read a late variable before it is assigned, you get a LateInitializationError at runtime — a clear, descriptive error rather than a mysterious null crash.

late is also used for lazy initialization:

late final String heavyComputation = computeExpensiveValue();

The computation runs only when heavyComputation is first accessed.

Required Named Parameters

Null safety also changed how named parameters work. Previously, named parameters were optional and could be null. Now you can mark them required:

void createUser({required String name, required String email, int? age}) {
  // name and email are guaranteed non-null
  // age may be null
}

Calling createUser without name or email is a compile-time error.

Migrating Older Code

If you maintain a package that predates null safety, you can migrate it with the built-in tool:

dart migrate

The tool analyzes your code, infers which variables can be null, and adds ? and ! annotations. Its output is a solid starting point but always needs human review — the tool is conservative and may add more nullable annotations than strictly necessary.

The Bang Operator

The bang operator (!) tells Dart that you know a nullable value is not null at this point:

String? nickname = fetchNickname();
String upper = nickname!.toUpperCase(); // throws if nickname is null

Use it sparingly. Overusing ! recreates the null-crash problem you were trying to eliminate. Prefer null checks, ??, or restructuring the code so that nulls are handled at the boundary rather than deep in logic.

Conclusion

Dart null safety is a mature, well-designed feature that makes Flutter apps significantly more reliable. The key mental shift is to treat nullable and non-nullable as genuinely different types, and to handle the nullable case at the earliest possible point. Once you internalize this, the type system becomes your ally rather than an obstacle — catching entire categories of bugs before your users ever see them.

Sign in to like, dislike, or report.

Comments

No comments yet. Be the first!

Sign in to leave a comment.

Understanding Dart Null Safety — ANN Tech