KMP or Flutter? The checklist I use for a production app
Having shipped production apps with both Flutter and Kotlin Multiplatform, these are the questions I ask before choosing one for a real product.
On this page
“Should we use Kotlin Multiplatform or Flutter?” usually gets asked as if one of them is simply better. In my experience it is never decided by the framework. It is decided by the product you already have, the people who will maintain it, and how expensive it would be to change your mind.
I have shipped on both sides: native Android in telecom apps, Flutter and native Android features in bKash (an app with 40M+ users), and CFClimb, a Compose Multiplatform app on both Google Play and the App Store. In an earlier post I explained why KMP became my personal default. This post is different: it is the checklist I go through when the decision is for a specific product, and it does not always end with KMP.
1. Is there a native app worth keeping?
This is the first question because it outweighs most of the others.
If you already have a healthy native Android app, KMP lets you share one class at a time, and Android calls it like any other Kotlin dependency. Flutter’s add-to-app adopts a screen at a time and brings a second runtime: a fine trade for a new feature area, a heavy one if the goal is “stop the two apps from disagreeing about business rules”. I covered this in more detail in the earlier post.
If there is no existing app, this question drops out and the rest of the list decides.
2. What do you actually want to share?
“Cross-platform” hides three different goals:
- Rules only. Validation, sync policy, pricing logic, API models. Each platform keeps its own UI. This is plain KMP.
- Rules and most screens. Shared UI, with native code where it matters. This is KMP with Compose Multiplatform, or Flutter.
- Everything, including the look. One design, pixel-identical on both platforms. Flutter is built for this.
Teams often say they want the second or third option when the pain they are actually feeling is the first: two implementations of the same rule drifting apart. If that is the problem, sharing the UI is solving something you did not ask for.
3. How native does the UI need to feel on iOS?
Flutter and Compose Multiplatform both draw their own UI instead of using UIKit or SwiftUI components. Both can look good on iOS, and both need deliberate work to match platform conventions such as navigation gestures, text selection and system controls.
So if iOS users expect a fully native feel, neither shared-UI option gives it for free. The honest options are:
- share the UI and accept a consistent cross-platform design, or
- share logic with KMP and build the iOS UI in SwiftUI.
KMP makes the second option possible without a rewrite later. That flexibility is worth a lot when you are not yet sure which way the product will go.
4. Who will build and maintain it?
Frameworks are chosen by people who will not always be the ones maintaining them.
- A team of Android engineers is productive in KMP from day one. The language, coroutines, Gradle and Compose are all familiar. The learning curve is the iOS side: Xcode, signing, and how Kotlin APIs look from Swift.
- A mixed or web-leaning team without strong native experience often moves faster in Flutter, because one language and one toolkit cover the whole app.
- Separate Android and iOS teams fit KMP better. The shared module becomes a contract between them, while each team keeps ownership of its own UI.
Building CFClimb for both platforms on my own made this concrete. The Kotlin parts never slowed me down. The time went into the iOS-only edges: Xcode project settings, signing, widgets and background work.
5. How much platform-specific work is in the roadmap?
Every app eventually needs things only the operating system can provide: widgets, background tasks, notifications, payments, biometrics, camera, deep links.
This is where the two models differ most:
| Kotlin Multiplatform | Flutter | |
|---|---|---|
| Calling Android APIs | Direct: shared code is Kotlin | Through platform channels, Pigeon or JNI bindings |
| Calling iOS APIs | Kotlin/Native calls Apple frameworks; some APIs need Swift | Through platform channels, Pigeon or FFI |
| Home-screen widgets, app extensions | Native code (Swift on iOS) | Native code (Swift on iOS) |
| When a package is missing | Write the platform code in the language the platform expects | Write the platform code, plus the bridge to Dart |
Note the widget row. With either framework, an iOS widget is a SwiftUI extension. In CFClimb the widget does not load the Kotlin framework at all; the app writes a small snapshot to a shared App Group and the widget reads it. A Flutter app would end up with the same design.
If the roadmap is mostly screens and network calls, this section barely matters. If it is full of platform features, KMP’s advantages start to add up: shared code is already Kotlin on Android, and on iOS it calls Apple frameworks directly instead of sending messages over a channel.
6. Which platforms beyond iOS and Android?
If the web is a first-class target, check the current maturity of each option for your specific needs before committing. Both Flutter and Compose Multiplatform target web and desktop, and their stability levels per platform change from release to release, so the official release notes are a better source than any blog post, including this one.
What has not changed: KMP’s strongest platforms are Android and iOS, and its shared business logic also runs on the JVM, so a Kotlin backend can reuse the same models and rules.
7. What does it cost to be wrong?
I ask this last, but it often settles the decision.
- Backing out of KMP is cheap on Android: the shared code is already Kotlin, so moving it back into the app module is mostly cut and paste. On iOS you rewrite the shared logic in Swift, which is a contained job if you kept the shared API small.
- Backing out of Flutter means rewriting everything that lives in Dart, UI and business logic alike, on both platforms.
Neither is a reason to avoid a framework. But when the team is unsure, the option with the cheaper exit is the safer first step.
So which do I pick?
Three situations come up often:
- New product, small team, one design for both platforms, no native code to keep: Flutter. It is fast to build with, mature and well documented, and I would not talk anyone out of it.
- Mature native apps with separate Android and iOS teams: KMP for shared logic, native UI on both sides. Add Compose Multiplatform screens later only where it clearly pays off.
- An Android-first app, or an Android team that now needs iOS: KMP with Compose Multiplatform. This was CFClimb: the Android app came first, then the UI moved to Compose Multiplatform and shipped to the App Store.
Key takeaways
- Start with what already exists. An existing native app tilts the decision toward KMP.
- Separate “share the rules” from “share the UI”. They are different problems with different answers.
- Neither Flutter nor Compose Multiplatform gives you native iOS UI for free; plain KMP with SwiftUI does.
- Match the framework to the people who will maintain it, not to whoever chose it.
- Platform features like widgets need native code either way; count them in the plan.
- Check web and desktop maturity against current release notes, not old comparisons.
- When unsure, prefer the option that is cheaper to back out of.