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


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 changefinal
- Fixed values known immediatelylate
- 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.
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.