Why I’m Not a Fan of ‘late’ in Dart — My Journey Through Frustrations and Lessons

Chahat Gupta
3 min readAug 26, 2023

--

I have expressed my unfriendly feelings towards late initialization in my previous writings, and it just worsens in Dart. In this one, I will list four major reasons why you should avoid using late in Dart.

💡 Heads Up: If you are unaware of ‘late’ in Dart or late initialization in general, this short article will get you in sync…

Reason #1 — The risk of runtime errors

We use late in Dart when we are so sure about the initialization happening later that we take the risk of runtime errors. If you see it from a different point of view, it is no different from declaring a nullable variable and calling a non-null assertion on it every time.

late String name;

// maybe a hundred lines later, or in a different file
void printName() {
print(name);
}

This is dangerous as the compiler does not run an initialization check on these variables and they become a prime source of app crashes on production. If you miss, you will be greeted by this bad boy.

Reason #2 — Readability and code maintenance

Using late will make your code less readable and harder to maintain over time. While reading code, it is not instantly obvious that the variable is late unless you navigate to its definition.

// definition
String? name;
late String email;

// usage
void startPrint() {

print(name!); // obviously nullable
print(name ?? 'NA'); // obviously nullable

print(email); // looks non-nullable but is late (might be uninitialized)
}

This lack of clarity can confuse fellow developers (including your future self) and make debugging more challenging.

Reason #3 — The unavoidable future crashes

This point might come out as personal, because it once crashed my live app. For a simplified example, consider this model with late variables and a fromJson() named constructor.

class User {
late int id;
late String name;

User.fromJson(Map<String, dynamic> json) {
id = json['id'];
name = json['name'];
}

.. more named constructors ..
}

Initially it looks harmless, but if a team member (or your future self) adds a new field to this class, adds all related functionality but forgets to add a line in the fromJson() constructor, the app will still compile but crash at runtime, and will crash only when the specific constructor is called.

This is more recurrent in UI elements as they mostly have multiple named constructors.

Reason #4 — Performance Considerations

While Dart’s runtime efficiently manages late variables, there is a minor overhead associated with initializing these variables when they are accessed for the first time.

This overhead is usually inconsequential, but it might be noticeable in extremely performance-sensitive scenarios. In such cases, direct initialization might be preferable.

Conclusion

While the late keyword might appear convenient, it comes with potential downsides that can affect your code’s reliability, readability, maintainability, and overall performance.

By opting for alternative strategies like direct initializalization, you can create more robust Flutter apps and save your future self from a lot of debugging. Let’s meet in another blog. Happy coding!

( just another meme for the feature image )

--

--

Chahat Gupta
Chahat Gupta

Written by Chahat Gupta

Mobile Tech Lead specialising in Android, iOS, and Flutter. Sharing insights and learnings on mobile development to inspire and elevate tech professionals.

Responses (2)