From Kotlin to Dart — an Android developer’s perspective
People are asking how easy it is so switch to Dart for Flutter development, what are the differences between Dart, Java and Kotlin, and what can an Android developer expect when learning Flutter. Here are some of my thoughts on the topic — I’ve been using (and loving) Kotlin for over 1.5 years now, and I’ve been exploring Flutter and Dart in my free time for couple of months now. As always, take everything with a grain of salt, and your mileage will most definitely vary.
If you come from Java, Dart’s syntax is fine. You don’t lose much, while getting some syntactic sugar like fields assignment in costructor arugments, named parameters or proper getters/setters. Functions as first-class citizens also feel a bit better (you don’t have to hack around with interfaces). If you’re coming from Java you’re already used to having to generate
hashCode, which you also have to do in Dart. And if you're a fan of immutable classes — to using something that helps with that, like AutoValue or Immutables — in Dart there's BuiltValue for example.
Now if you come from Kotlin, then Dart as language is borderline terrible — you don’t get data classes, extension functions, null-safe types, if/when-as-expression, sealed classes, function literals with receivers, and more. Lack of any of those is already a step back, lack of all of them — it’s like you went back several years. Dart does have some features that don’t exist in Kotlin, like cascade operator, but I’d gladly give up cascade operator for an
It’s not that I’m a Kotlin fanboy (I am though), but let’s see:
- no data classes mean you’re back to generating
hashCodefor every single data class you have. For immutability you also have to either manually write
copymethod or write hefty boilerplate to let
BuiltValuegenerates those for you. And boy it feels like forever to do either, while in Kotlin you only add
datakeyword to your class.
- no extension functions makes you unable to extend somewhat limited standard library. Who doesn’t like some
- lack of null-safe types is killing me. I’m not a great fan of nulls, but I do use them — when it makes sense. Kotlin makes you document whether you expect something to be nullable or not — Dart doesn’t, and it simply adds a lot of mental overhead to deduce the nullability.
- sealed classes — once I started using those, there’s no coming back. Now in Dart (same as Java) if you contain everything in a single type, you have to juggle boolean values to know whether you have a value or not, all while opening yourself to exceptions and/or
nullvalues. If you use multiple classes, you're forced to return
dynamicand you lose all the compile-time safety.
As for things that are in Dart that aren’t in Kotlin — you get mixins, but I’m yet to make use of them (I’m sure there is potential here, though).
dynamic keyword and not having to specify type is sometimes useful (like mapping jsons if you know the structure), but overall I guess I'm a fan of knowing my types everywhere anyway ¯\(ツ)/¯. Async-await is great and a big win over Java, and I'd say the single thing that really keeps me sane with Dart (other than Flutter obviously). Kotlin does have coroutines, but I find the learning curve much steeper for them than for Dart's async-await.
Overall I feel I’m half as productive in Dart vs Kotlin, especially when working with the domain layer, where the language matters the most. I’m sure one can get used to it, but it ain’t a pleasant journey.
Now I know not every language can be Kotlin, and I don’t expect Dart to be exactly like Kotlin in every area. Will Dart get some of those useful features, though? It’s difficult to say — let’s take a look at issues for some features I’d love to see in Dart:
- Remove or make semicolons optional. While it’s not closed, the discussion doesn’t give much hope, as we hear arguments that e.g. Kotlin, as previously mentioned, makes choices that make its semicolon-insertion confusing and bug-prone.. Who writes in Kotlin and feels lack of semicolons is bug-prone raise your hand up. Oh and this issue has already been closed as not planned — 34th issue, didn’t take long, did it?
- Scoped object extensions (aka extension functions). Also not closed, but discussed for 5 years now.
- Method overloading based on parameter types — closed because, obviously, Even in language with mandatory types, type-based overloading is a bad idea, creating brittleness and ambiguity. However seems like it’s reopen as a new issue.
- Everything won’t be an expression, because it doesn’t seem like a great fit for a conservative language like Dart. People wil use it in ways that might confuse the average programmer..
- No infix function syntax — closed, as the feature violates the simple conservative design principle for Dart.
But there there still is hope, since we get some issues that are still open and apparently considered after Dart 2.0 (which should be coming soon).
All in all I hope that Dart’s development will accelerate with respect to new, fancy features. Until that happens I’ll keep wishing I could write Flutter apps in Kotlin.
Dart’s standard library is okay-ish, about on par with Java 6 that we get on Adroid. It’s not as rich as it could be, and quiver library seems to be an attempt at enriching it, but without extension functions it’s just not the same. Overall I don’t feel hindered by Dart’s standard library, and if I do, there’s a official (or a semi-official) package on pub for that (let’s just hope we don’t go as far as npm with left-pad here). Assertions library for tests feels a bit like simple JUnit, but that might be simply becuse I got used to AssertJ for Kotlin.
There are lots of third-party libraries, though, and it seems to be related to the fact that it’s much easier to distribute a library via pub than it is via maven — simply keep the proper layout of the files and run
pub publish. I really appreciate the package score as well, which shows you how popular the package is and how many issues are there in code.
Importing a library is easy enough, not that different from adding an artifact using Gradle. However sometimes the library resolution doesn’t work, since you (and all the libraries) can declare a dependency on a specific version range of a particular library. It’s easy to get a situation where e.g. two libraries depend on non-overlapping ranges of a single package. In that case it might be difficult to find just the right versions. I did have this problem, but this was also during the heated time of libraries migrating from Dart 1 to 2, and the fact that Dart 2 is a breaking release might have been a factor.
Most people will say it’s fine, but I’ve found IDE support to be much more limited than for Kotlin. I’m using IntelliJ (you can also use Visual Studio Code — maybe it’s more feature-rich), and I find writing in Dart much less convenient than in Kotlin. I consider myself IntelliJ power-user, and I tend to use every single functionality that helps me write faster. For Dart some features are not available (like postfix completion) or are not as extensive as for Kotlin. Refactoring is a pain — e.g. when changing name of the class, I still have to rename the file (for which the naming convention is
snake_case.dart) manually. For constructors that use the field assignment syntax (
this.alignment) you don't see types in parameter info popup. The available intent actions are far and apart, static analysis integration is sometimes flaky, code completion often simply doesn't work for types that haven't yet been imported in a file etc. It's not that you're back to writing in Notepad, but there's much place for improvement. Dart is getting more traction thanks to Flutter now, so I suppose we can expect improvement in this area.
Dartfmt (a tool to format the code automatically) is cool and lets you focus less on the code style, and more on coding (although it still doesn’t prevent discussions in code review like ‘plx put a comma after last parameter’). Dartalyzer, which is a tool for static analysis of Dart code, is pretty nice, and just works. Overall I very much appreciate both tools being bundled with the language and available out of the box for every project without much hassle.
One thing I miss and I put it here because I believe it’s tooling issue — I haven’t found an easy way of having multiple Dart/flutter modules in a single project. For Android projects I usually end up with several separate modules which, and most of them are separate (e.g. database module won’t know about the REST-api module, and domain module won’t know about anything basically). That forces the developers to not mix up the layers and responsibilities of the components. I don’t seem to be able to do that in Dart other than by having multiple projects, which can’t be edited in one IntelliJ instance.
Of course hot reload is also enabled by tooling (as well as language design) and it’s a godsend, not much to say here.
To sum it up, I think it’s safe to say Dart — the language, tooling and libraries — is fine, and it really shouldn’t come as surprise. Is it as good as Java? Maybe. Is it as good as Kotlin? Most definitely not. Should it prevent you from trying out Flutter? That’s for you to decide, but now you maybe know a bit more about what to expect. The best description for Dart I’ve read is that it’s a generic language. You’re not blown away by it, but you are able to steadily write good code, and that’s enough.