Reading time: 8 minutes
Introduction
Null safety in Flutter (Dart) is great—until it isn’t. If you’ve been staring at a compile error for ten minutes because of a single question mark, you’re not alone. Dart’s null safety system eliminates whole classes of bugs, but it’s easy to stumble when migrating code, working with APIs, or dealing with legacy packages.
I’ve debugged hundreds of null safety bugs in Flutter projects. Most come from a handful of repeating patterns. Once you know them, fixing these issues takes seconds instead of hours of frustration and endless searching.
This guide covers the most common problems you’ll encounter in production apps, along with precise solutions and ready-to-use code examples.
Overview: Understanding Null Safety in Dart
Dart’s null safety introduces an important distinction: a variable can be either nullable (can hold null) or non-nullable (cannot hold null). You declare this using a question mark. For example, String is non-nullable, while String? is nullable.
The issue arises when your code tries to assign a nullable value to a non-nullable variable—Dart won’t allow it. This is intentional, forcing you to consider nulls before runtime.
Most null safety errors stem from type mismatches, delayed initialization, or misunderstanding when Dart treats a variable as “safe” after null checks. Let’s address these issues.
Problem 1: Nullable Assignment Error
You’re trying to assign a nullable value to a non-nullable variable, and Dart refuses to compile.
String? user = getUserFromApi();
String displayName = user;
// Error: A value of type 'String?' can't be assigned to a variable of type 'String'
Solution: Use the Null Coalescing Operator (??)
Provide a default value if the nullable variable is null:
String? user = getUserFromApi();
String displayName = user ?? 'Guest';
// displayName is guaranteed non-null now
The ?? operator checks whether the left side is null and falls back to the right side if it is. A single, simple operator saves your day.
Alternative: Force Unwrap with (!)
When you’re absolutely sure the value isn’t null, use the exclamation mark to tell Dart to trust you:
String displayName = user!;
// If user is null, your app crashes at runtime
Use this cautiously — in 99% of cases, ?? is safer and more reliable.
Problem 2: Null Check After If Statement
Sometimes Dart complains about a possible null even after you’ve checked it. This happens when the analyzer can’t infer the control flow correctly.
String? email = getEmail();
if (email == null) return;
print(email.length); // Works fine
But this fails:
String? email = getEmail();
if (email != null) {
print(email.length);
} else {
print(email); // Error if used outside of if block
}
Fix: Use Type Narrowing
Dart automatically narrows the type inside an if-block. Stay within that scope:
if (email != null) {
sendEmail(email);
}
Or use Dart 3 if-case syntax:
if (email case String e) {
print(e.length);
}
Problem 3: Late Variable Initialization
When you plan to initialize a variable later, Dart might complain unless you mark it correctly.
String name;
@override
void initState() {
super.initState();
name = 'Alice';
}
Fix: Use the late Keyword
late String name;
@override
void initState() {
super.initState();
name = 'Alice';
}
late tells Dart to trust that the variable will be initialized before use. Misuse it, and you’ll face a runtime error.
Problem 4: Handling API Responses
API fields can be null, so you need to handle them safely when mapping JSON to models.
class User {
final String id;
final String name;
final String? phone;
User({required this.id, required this.name, this.phone});
factory User.fromJson(Map json) => User(
id: json['id'] ?? '',
name: json['name'] ?? 'Unknown',
phone: json['phone'],
);
}
Mark optional fields as nullable and handle them gracefully in your UI.
Problem 5: Nullable Lists and Maps
Collections can contain null values. Filter or coalesce before using them.
List names = ['Alice', null, 'Bob'];
String first = names[0] ?? 'Unknown';
Or filter nulls before use:
List valid = names.whereType().toList();
Problem 6: Callbacks with Nullable Return Types
Function signatures must match nullability exactly.
typedef Formatter = String Function(int);
String? getName() => 'Alice';
Formatter f = (int n) => getName() ?? 'Guest';
Pro Tips
- Enable strict null safety in analyzer options.
- Prefer non-nullable types unless absolutely necessary.
- Test null edge cases in your unit tests.
Conclusion
Null safety in Flutter keeps your app stable and bug-free. Use ?? for defaults, avoid ! unless absolutely sure, initialize variables properly, and use nullable types wisely. Master these patterns, and debugging will become quicker and easier.