First of all, a few words about my experience.
I like Kotlin. In fact, I absolutely love it.
But one day, as a naturally curious person, I started learning Flutter.
There were some things I liked, and some things I found weird. But to fully appreciate a technology’s strengths and gain a better understanding of its weaknesses, you have to work with it full-time on a real project.
So, over time, I received an offer to work on a Flutter project, which I gratefully accepted. And then, after a year and a half, the project was mostly put on hold, so I switched back to native Android development.
In this article, I’ll share my thoughts on moving away from native development and then returning to it.
Part 1: Getting to Know a Stranger.
Dart
Learning Flutter was a great mental exercise for me.
First of all, the Dart language felt awkward.
After familiarizing myself with the syntax, my thought was “Wow, this looks like a weird cousin of Java and Kotlin”.
With my background in Java and Kotlin, it was really easy to pick up Dart. At the same time, some features felt new and unique.
However, many aspects felt off.
Semicolons? Seriously — are we back in 2010?
Also, the absence of an equivalent to Kotlin’s data classes felt awful. Just look at how you create the simplest data class in Kotlin versus in Dart!
data class BiometricCredentials(
val username: String,
val password: String,
val encryptTimestamp: Long,
val isExpired: Boolean = false,
)
class BiometricCredentials {
final String username;
final String password;
final int encryptTimestamp;
final bool isExpired;
const BiometricCredentials({
required this.username,
required this.password,
required this.encryptTimestamp,
this.isExpired = false,
});
BiometricCredentials copyWith({
String? username,
String? password,
int? encryptTimestamp,
bool? isExpired,
}) {
return BiometricCredentials(
username: username ?? this.username,
password: password ?? this.password,
encryptTimestamp: encryptTimestamp ?? this.encryptTimestamp,
isExpired: isExpired ?? this.isExpired,
);
}
@override
bool operator ==(Object other) {
if (identical(this, other)) return true;
return other is BiometricCredentials &&
other.username == username &&
other.password == password &&
other.encryptTimestamp == encryptTimestamp &&
other.isExpired == isExpired;
}
@override
int get hashCode {
return username.hashCode ^
password.hashCode ^
encryptTimestamp.hashCode ^
isExpired.hashCode;
}
@override
String toString() {
return 'BiometricCredentials(username: $username, password: $password, encryptTimestamp: $encryptTimestamp, isExpired: $isExpired)';
}
}
This felt like a major downgrade compared to Kotlin and reminded me of the good old Java 7/8.
There are other things that are just different.
For instance, Mixins are an interesting concept, and they’re used extensively in the Flutter framework.
What bothered me most was the absence of very basic class modifiers.
Prior to Dart 3, you had Classes, Mixins, and mysterious implicit interfaces.
But with Dart 3, we get most of the goodies from Kotlin: abstract classes, interfaces, final classes, and sealed classes!
This is a decent set, but the protected modifier is still missing. That’s a bummer, but I can live with it.
Another interesting thing is the way we work with asynchronous code execution. There are two tools: Futures and Isolates.
Futures are asynchronous, but run on the same thread, while Isolates concurrently on a separate threads.
Okay, this is straightforward. I’ve also found them a bit easier to use compared to Kotlin coroutines, and much, much easier than vanilla Java multithreading.
Then, there are also improvements compared to Java (though not to Kotlin).
Modern Dart supports null safety! That’s really nice. I’m spoiled by Kotlin, and going back to code without null safety would be a nightmare.
But here, the implementation is very similar to Kotlin’s, so that’s a win.









