I Trusted Dart’s Null Safety… and It Still Crashed My App
Md. Al-Amin

Md. Al-Amin @alaminkarno

About: Experienced Android Developer with a demonstrated history of working for the IT industry. Skilled in JAVA, Dart, Flutter, and Teamwork.

Location:
Dhaka, Bangladesh
Joined:
Oct 25, 2022

I Trusted Dart’s Null Safety… and It Still Crashed My App

Publish Date: Jul 16
1 1

It was a chill Thursday night. I was working on a profile update feature for one of my Flutter apps. I had just refactored a bunch of code and felt pretty confident.

After all, Dart has null safety now what could go wrong?

The app launched, everything looked good. But then… Crash.
Not a red screen. Not a console warning. A full-on, “Unhandled exception: Null check operator used on a null value.”

I was confused.

“Wait, I’m using null safety. Isn’t this stuff supposed to be impossible now?”

I had sprinkled a few ! operators here and there (you know, just to keep the compiler happy). But that one exclamation mark brought the whole thing down.

That was my wake-up call.

And that night, I went deep into understanding Dart’s null safety. In this post, I’ll share:

  • What I did wrong
  • How Dart null safety really works
  • What tools Dart gives you (?, !, late, required)
  • Best practices to keep your app safe, clean, and crash-free

What Is Null Safety, Really?

Null safety in Dart means you have to explicitly declare when a variable can be null.

Before null safety, any variable could potentially be null, leading to tons of runtime crashes.

Now, you write:

String name = 'Md. Al-Amin'; // non-nullable
String? nickname;            // nullable
Enter fullscreen mode Exit fullscreen mode

Dart forces you to think about nulls. It doesn’t let you do:

print(nickname.length); // ❌ compile error
Enter fullscreen mode Exit fullscreen mode

You need to check if it’s null:

if (nickname != null) {
  print(nickname.length);
}
Enter fullscreen mode Exit fullscreen mode

Or use ! to say “I promise this isn’t null” (but use it carefully… more on that soon).


What I Did Wrong: The Dangerous ! Operator

Back to my bug…

I had this code:

String? userToken;

// later...
apiClient.setToken(userToken!);
Enter fullscreen mode Exit fullscreen mode

In my head, I was sure userToken was initialized before calling setToken. But in reality, it wasn’t.

That ! is called the null assertion operator, and it basically says:

“Trust me, this is not null don’t check, just run.”

But Dart doesn’t protect you here. If userToken is null, you crash.
And that’s exactly what happened.


Let’s Break Down the Null Safety Tools

? → Nullable Variable

String? email;
Enter fullscreen mode Exit fullscreen mode

This means email can be either a String or null.

Use ? when:

  • You’re receiving data from external sources (APIs, storage, etc.)
  • A variable truly might not be set (optional fields)

! → Null Assertion

String? email;
print(email!); // You better be 100% sure it's not null
Enter fullscreen mode Exit fullscreen mode

Use this only when you’re absolutely certain a value can never be null at that point.

When it’s okay:

if (email != null) {
  print(email!); // you're safe here
}
Enter fullscreen mode Exit fullscreen mode

When it’s dangerous:

print(email!.length); // if email is null = crash!
Enter fullscreen mode Exit fullscreen mode

Tip: Avoid using ! unless you’ve done a prior null check.


late → Delayed Initialization

What if a variable can’t be set immediately, but you’re sure it’ll be set before use?

late String token;

void init() {
  token = fetchToken();
}

void makeRequest() {
  print(token); // Dart assumes it's set before use
}
Enter fullscreen mode Exit fullscreen mode

If you forget to assign it before access Dart throws an error at runtime.

Use late when:

  • You want to avoid null, but initialize later (e.g., in initState)
  • You’re working with dependency injection

Don’t use it as a lazy shortcut for avoiding ? or !.


required → Non-nullable named parameters

Before null safety:

void createUser(String name, String email);

Enter fullscreen mode Exit fullscreen mode

After null safety:

void createUser({required String name, required String email});

Enter fullscreen mode Exit fullscreen mode

If the caller doesn’t pass name or email, Dart throws a compile-time error.

Use required for:

  • Named parameters that are essential
  • Enforcing contracts in your API or widget constructors

Real-World Use Case: User Profile Model

Let’s look at a simple User model that could crash if nulls aren’t handled well:

class User {
  final String name;
  final String? email;
  final String? phone;

  User({
    required this.name,
    this.email,
    this.phone,
  });
}
Enter fullscreen mode Exit fullscreen mode

Now, when rendering UI:

Text(user.email ?? 'No email available'),
Enter fullscreen mode Exit fullscreen mode

That’s safe. No ! needed. No risk of crash.


Best Practices for Null Safety in Dart

Here’s what I learned and now live by:

Do This

  • Use ? when data may be null
  • Use required for essential fields
  • Check nulls before using values
  • Use defaults like ??
  • Write unit tests for null cases

Avoid This

  • Making everything late to avoid ?
  • Forcing values with ! everywhere
  • Trusting external data blindly
  • Assuming things are initialized
  • Ignoring edge cases in models

Extra Tip: Null-Aware Operators in Dart

Dart gives you some powerful operators:

?? → Default value if null

print(user.email ?? 'No email');
Enter fullscreen mode Exit fullscreen mode

??= → Assign default if null

email ??= 'unknown@example.com';
Enter fullscreen mode Exit fullscreen mode

?. → Safe access

print(user.profile?.bio); // If profile is null, won’t crash
Enter fullscreen mode Exit fullscreen mode

Final Thoughts

Dart’s null safety is amazing, but it only works if we understand how to use it.

Null safety doesn’t mean “no more crashes.”
It means: “You now have the power to prevent them, but you have to think.”


My Advice to You

  • Avoid the ! unless you’re absolutely sure
  • Use ? and null-aware operators liberally they’re your friends
  • Don’t go overboard with late — it’s powerful, but sharp
  • And always, always test your edge cases

If Dart asks you to handle nulls, it’s not being annoying its saving future you from a nightmare.


I’d love to hear from you:

Have you ever been betrayed by a !? Or misused late and got runtime surprises?
Share your experience and let’s help each other write safer Dart code.

Comments 1 total

  • Randal L. Schwartz
    Randal L. SchwartzJul 16, 2025

    Pattern matching can also help. It's easy to rule out nulls from almost everywhere that way.

Add comment