I Used late Instead of late final, and It Broke My App Later!

Md. Al - AminMd. Al - Amin
3 min read

It was 1:30 AM. I was staring at the screen, wondering “Why is this value changing?”

I didn’t write code to change it. I was sure. But somehow… it did.

That’s when I realized
I used late.
But I should’ve used late final.

The Context: A Simple User Controller

Let me explain.

I had a controller that fetched user data from the server when the app started. Here’s what it looked like:

class UserController {
  late User user;

  Future<void> loadUser() async {
    user = await userService.getCurrentUser();
  }
}

So far, so good.

Now in the rest of the app, I used userController.user.name, userController.user.email, etc. confidently assuming this value would stay the same.

But I made one tiny mistake:
I used late instead of late final.

The Bug: Value Got Replaced Silently

After a few days, a new developer joined and added a “Refresh Profile” button that called loadUser() again.

So, what happened?

The user variable got replaced with new data, and in some parts of the app things started breaking subtly:

  • Analytics logs had inconsistent user IDs.

  • Cache was overwritten.

  • Some values were missing.

It wasn’t a big crash just small, silent chaos.
And it took me hours to figure out why.

The Root Cause: late is Not Read-only

When we write:

late User user;

We’re saying:

“I will assign this later, but I can assign it again too.”

So, this is valid Dart code:

late String name;
name = 'Alamin';
name = 'Karno'; // ✅ No error!

But I didn’t want that.
In my case, the user should be assigned once and then locked.

The Fix: late final

I updated my code to:

class UserController {
  late final User user;

  Future<void> loadUser() async {
    user = await userService.getCurrentUser();
  }
}

Now if someone tries this:

user = anotherUser; // ❌ BOOM! Compilation error!

Dart says:

“Nope. This is final. You already assigned it once.”

And I sleep peacefully now.

Real Flutter Example

Imagine you’re building a screen that depends on one-time loaded data like this:

class ProductController {
  late final Product product;

  Future<void> fetchProduct() async {
    product = await api.getProduct();
  }
}

In your widget:

Text(productController.product.name)

If you mistakenly used late, someone could reassign it later and the UI would update with incorrect info or worse, inconsistent app behavior.

A Quick Comparison

  • var- Regular values that can change

  • final- Fixed values known immediately

  • late- Delayed but changeable (risky!)

  • late final- Delayed and locked after 1st set

When to Use late final

Use late final when:

  • The value will be set later (e.g., async calls, initState, etc.)

  • It must not change again.

  • You want to protect your variable from accidental reassignment.

Friendly Advice

If you ever write this:

late SomeType something;

Ask yourself:

“Will this value change after it’s set?”

If the answer is ❌ No
Just write:

late final SomeType something;

It’s safer. Cleaner. And future you will thank you.

Final Words

We don’t always break our app with big mistakes.
Sometimes, it’s just one small word:

late instead of late final.

From that day on, I started using late final in almost all of my controllers, models, and services unless I really needed to change the value later (which is rare).

So yeah, don’t ignore this little trick. It might just save your next release.

0
Subscribe to my newsletter

Read articles from Md. Al - Amin directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Md. Al - Amin
Md. Al - Amin

Experienced Android Developer with a demonstrated history of working for the IT industry. Skilled in JAVA, Dart, Flutter, and Teamwork. Strong Application Development professional with a Bachelor's degree focused in Computer Science & Engineering from Daffodil International University-DIU.