If you just look at the languages, Kotlin has a lot of nice features that Dart doesn't currently have. Non-nullable types, extension methods, and data classes are on the top of my mind.
If you look at Kotlin and Dart as implementations, then there are differences under the hood. Kotlin is deeply married to the JVM. That has a lot of obvious pros: you can seamlessly interop with your existing Java code, reuse the existing JVM ecosystem, and incrementally migrate your code to Kotlin a file at a time. There are trade-offs to this, though. Kotlin fundamentally doesn't own its runtime. It gets the JVM for free, but anything the JVM lacks, it has little easy way to acquire.
Dart is both its own language, and its own ecosystem and runtime. This means the adoption cost is higher, and our growth has been slower. We had to build more from scratch. But the return on investment is that we have a runtime that is deeply tuned for Dart and the needs of our users. For example:
* Our garbage collector is tuned for the frequently-allocated but short-lived objects that get constructed every frame by a reactive framework.
* When you deploy your app, we compile your Dart code ahead of time, all the way to machine code, statically link in the runtime, and give you a standalone native executable. Our deployment model is closer to C/C++ than to Java or C#.
* Our runtime object representation faithfully understands the full language. For example, there's no type erasure, and generics are represented at runtime. You can ask a list if it's a List<int> or List<Object> at runtime and it will tell you true.
* We can expose interesting debugging, profiling, and other diagnostic hooks from the runtime to tools. For example, the Flutter Inspector [0] will show you the bounding boxes of every widget on screen from the running app on your device. Touching one navigates your IDE to the corresponding source code. We can do that because we have the ability to plumb things all the way through the IDE, parser, compiler, runtime, and debugger, because it's all a custom stack.
* Likewise, the near-instant hot reload stuff we do works because the language design, Flutter framework design, IDE integration, compiler, and runtime all work in concert to enable that. Even non-obvious things like the fact that static fields are lazily initialized in Dart make this stuff easier.
Of course, the Kotlin+JVM advantages are real too. It's a question of which trade-offs are right for you today, and tomorrow. One thing I'm particularly excited about right now with Dart is that we're in a much better places to evolve the language quickly now that we've moved to 2.0, a full static type system, and a shared front end.
Right now, we're implementing non-nullable types. Ours will be fully sound, unlike in Kotlin and TypeScript, because they have to handle values flowing in through interop. We're also implementing extension methods. I'm hoping we can do something a la data classes not too long after. If we pull that off, I hope users will feel that we're at parity at the language level and then it becomes more a question of which tools are the best ones for your need.
[0]: https://flutter.dev/docs/development/tools/inspector