← All posts

KMP or Flutter: you're arguing about the wrong thing

July 4, 2026 5 min read

Every KMP-versus-Flutter debate I’ve sat through eventually stopped being about technology. It turned into an argument about who we could hire, and who would still own the code after the people who wrote it moved on.

I work in a codebase that ships all three: a Kotlin Android app, a Swift iOS app, and a Flutter module carrying newer features. That makes it hard to be a partisan. It also means I’ve paid the integration cost of each one, which is the part nobody puts in the comparison table.

The old dividing line is gone

Eighteen months ago the answer was easy, and I gave it confidently: KMP shares logic, Flutter shares UI. Decide which one you wanted and the tool fell out of the decision.

Compose Multiplatform closed that gap. KMP can render a shared UI on iOS now, which means the clean split I used to rely on describes a world that no longer exists. The choice is three-way, not two-way.

                  native UI    Compose MP   Flutter
                  + KMP        + KMP
UI widgets        own          [shared]     [shared]
Navigation        own          [shared]     [shared]
State holders     [shared]     [shared]     [shared]
Domain rules      [shared]     [shared]     [shared]
Networking        [shared]     [shared]     [shared]
Platform APIs     own          own          [plugin]
Draws with        UIKit        Skia         Impeller

Read the columns, not the rows. The middle column is the one that didn’t exist when most of the blog posts you’ll find on this topic were written.

Both of them draw their own pixels

Here is the part that gets stated wrong most often, including by me for a while. Compose Multiplatform on iOS is not UIKit underneath. It draws its own widgets onto a canvas, the same category of thing Flutter does.

So “Flutter isn’t native, KMP is” is only true when you keep SwiftUI or UIKit on top and share nothing but logic. The moment you adopt Compose Multiplatform for iOS UI, you have accepted the same fundamental trade-off Flutter asks for: your widgets are drawn, not borrowed. Scroll physics, text selection, accessibility focus order, and the small OS behaviours users never articulate all become someone’s implementation rather than the platform’s.

That trade-off is often fine. It is not a difference between the two tools, and treating it as one is how teams pick Compose Multiplatform believing they kept a native guarantee they actually gave up.

Where each one actually hurts

Neither of these is the frictionless option, and the friction is not symmetric.

KMP: the shared module becomes a black box. If your iOS engineers don’t read Kotlin, they can consume the shared code but can’t debug or extend it. That turns into a queue with your Android team at the front of it. The organisational fix is harder than the technical one.

KMP: the iOS build path stays the rough edge. Coroutines don’t map cleanly onto Swift concurrency, so you take on a bridging dependency and keep it current as both languages move. Breakpoints across the boundary work, but slower than debugging either side alone.

Flutter: you now maintain two ecosystems. Platform integrations arrive through plugins, and when a plugin lags an OS release, you write the platform channel yourself. That is native work performed by whoever is available, which is usually not the person who knows that platform best.

Flutter: hiring splits. A Flutter engineer and a native Android engineer are different hires with different career paths. On a small team that’s a real constraint, not a detail. I’ve watched a Flutter module go quietly unmaintained because the one person fluent in it changed teams.

The pattern in all four: the cost lands on people and boundaries, not on frames per second. Performance is where these arguments start and almost never where they end.

The decision that actually matters

Strip away the benchmarks and two questions decide it.

Two mature native apps already shipping?
|
+-- yes -+-- iOS team will read Kotlin?
|        |   |
|        |   +-- yes -> KMP: share logic, keep both UIs
|        |   +-- no  -> stay native, dedupe another way
|
+-- no --+-- Is the hard part the UI, or the rules?
             |
             +-- the UI    -> Flutter
             +-- the rules -> KMP + Compose Multiplatform

The left branch is about what you already own. Two healthy native apps with invested platform teams are an asset, and KMP is the only option here that adds sharing without displacing either team. Flutter in that position asks both teams to hand over the part of the job they’re best at.

The right branch is about where your complexity lives. An app that is mostly screens — forms, lists, navigation, a lot of visual surface — gets more from Flutter, because that’s exactly the layer Flutter shares best. An app whose difficulty is rules, sync, offline state, and correctness gets more from sharing the domain in Kotlin, with the UI as a follow-on decision rather than the premise.

Pick the seam, not the framework

I’ve stopped answering “which one is better” because the question has no content. Both ship real products at scale. Both have production war stories that would talk you out of them.

What you are choosing is a seam: the line where shared code stops and platform code starts. Every one of these tools is a different opinion about where that line goes, and every cost I’ve described is a cost of maintaining it.

So choose the seam your team can still defend in code review two years from now, with the people you’ll actually have. The framework is downstream of that. Teams that pick the framework first spend the next two years discovering which seam they accidentally bought.