Exploring Dart Features for Kotlin Developers: A Flutter Mobile Development Perspective
This article shares a Kotlin developer’s experience transitioning to Flutter, examining Dart’s syntax and features such as null safety, late initialization, extensions, top‑level and higher‑order functions, and operator overloading, while explaining why Flutter was chosen for a cross‑platform audio player project.
Preface
I was originally a Java Android developer, later switched to Kotlin during university, and after a year I fell in love with Kotlin. While exploring cross‑platform options, I discovered Flutter + Dart, which initially felt abstract and nested, but eventually I decided to give it a try.
My first impression of Flutter’s cross‑platform capability was impressive, and the winter break gave me the chance to learn it. When I tried to build a business feature with Flutter, I realized it was not unacceptable at all – especially if you have experience with Android Compose, the UI‑business binding feels very natural.
This project does not mean I will always choose Flutter or abandon native Android/KMP; I simply want to expand my skill set.
If you want a quick overview of a Kotlin developer’s experience with Dart, skip to the “Misunderstood Dart” section.
Motivation
The motivation consists of two parts: interest in cross‑platform development and potential future job requirements. I aim for an "experience" rather than deep mastery, seeking a balance between learning cost and project quality.
Flutter was chosen because of its mature ecosystem, abundant ready‑made components, and strong support for desktop and mobile, allowing rapid onboarding.
Why Flutter
The project’s core feature is an audio player. Using KMP would require building many platform‑specific libraries from scratch, which is beyond my current ability, so I opted for the more mature Flutter solution.
Misunderstood Dart
My initial impression of Dart was that it resembled Java with many awkward syntax quirks, but after using Kotlin I discovered several similarities.
Null Safety
Both Kotlin and Dart support nullable types using the "?" suffix. Example:
class AudioMediaItem {
String title;
String description;
AudioMediaType type;
String? mediaUrl;
String? bvId;
// ... other fields
}Accessing a nullable property works like Kotlin: audioMediaItem?.mediaUrl returns null if the property is null, while audioMediaItem!.mediaUrl asserts non‑null (note Dart uses "!" instead of Kotlin’s "!!").
To emulate Kotlin’s Elvis operator "?:", you can write:
audioMediaItem?.mediaUrl ?? "default";Late Initialization
Kotlin’s lateinit var has a Dart counterpart late: late String name; It behaves like Kotlin’s lateinit var name: String, allowing a non‑null variable to be assigned later.
Extension Functions
Kotlin extensions are written as ClassName.extensionName() { … }. Dart provides a similar syntax:
extension StringExtension on String {
String capitalize() {
if (isEmpty) return this;
return '${this[0].toUpperCase()}${substring(1)}';
}
}Here StringExtension is just a label, on String indicates the target type, and capitalize is the added method.
Bringing Kotlin Built‑ins to Dart
Using extensions, we can mimic Kotlin’s also and let functions:
extension AlsoExtension<T> on T {
T also(void Function(T) block) {
block(this);
return this;
}
}
extension LetExtension<T> on T {
R let<R>(R Function(T) block) {
return block(this);
}
}
// Usage example
String demo = "xada".let((it) => "${it}xadadawdwad");Top‑Level Functions
Both Kotlin and Dart allow defining functions outside of classes, making them globally accessible.
val json = Json {
prettyPrint = true
isLenient = true
ignoreUnknownKeys = true
}
var retrofit = Retrofit.Builder()
.baseUrl("https://api.juejin.cn/")
.addConverterFactory(json.asConverterFactory(MediaType.parse("application/json;charset=utf-8")!!))
.build()In Dart the same idea looks like:
final _cookieJar = CookieJar();
final Dio dioClient = Dio(BaseOptions(
baseUrl: baseUrl,
connectTimeout: const Duration(seconds: 5),
receiveTimeout: const Duration(seconds: 5),
contentType: Headers.jsonContentType,
persistentConnection: true,
))
..transformer = BackgroundTransformer()
..let((it) {
if (!kIsWeb) {
it.interceptors.add(CookieManager(_cookieJar));
return it;
} else {
return it;
}
});Higher‑Order Functions
Kotlin’s forEach is a higher‑order function that takes a lambda. Dart’s version is similar, requiring an explicit void type for the lambda parameter.
void forEach(void action(E element)) {
for (E element in this) action(element);
}
List<String> demoList = ["da", "da", "da"];
demoList.forEach((element) {
print(element);
});Operator Overloading
Kotlin uses the operator keyword to overload operators; Dart achieves the same with operator inside an extension.
extension ListPlusOperatorExtension<T> on List<T> {
List<T> operator +(List<T> elements) {
List<T> result = this;
addAll(elements);
return result;
}
}
List<String> demo1 = ["da", "da"];
List<String> demo2 = ["da", "d1a"] + demo1;Conclusion
Dart shares many features that Kotlin developers appreciate, such as null safety, late initialization, extensions, top‑level functions, higher‑order functions, and operator overloading. If you are comfortable with Kotlin, transitioning to Dart for Flutter development should be smooth.
In the next article I will discuss Flutter’s layout constraints and other development experiences.
Feel free to comment with any interesting Dart features you discover, and point out any mistakes in this article.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Rare Earth Juejin Tech Community
Juejin, a tech community that helps developers grow.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
