Flutter vs. KMP: Choosing the Right Cross-Platform Framework for Your Existing Native App
The Flutter news you actually need
No spam, ever. Unsubscribe in one click.
You have a native app. It works. Your team knows the platform-specific intricacies. Now, the business wants to expand reach or improve development efficiency by going cross-platform. The two leading contenders for a team in your position are Flutter and Kotlin Multiplatform (KMP). This isn’t a greenfield project, so the choice has profound implications for your existing code, team skills, and long-term maintenance. Let’s break it down.
The Core Philosophy: Rewrite vs. Reuse
The fundamental difference lies in the approach to your existing investment.
- Flutter is a complete UI toolkit. You build the entire interface (and most logic) in Dart, rendering to a canvas. It’s a strategic rewrite of the presentation and business layers for a consistent, high-performance experience across platforms.
- Kotlin Multiplatform is a business logic sharing technology. You write your core logic (networking, models, business rules) in Kotlin once and share it across iOS, Android, and others. The UI remains native (Swift/UIKit for iOS, Kotlin/Jetpack Compose for Android). It’s a tactical reuse of your core app.
Evaluating Your Team and Codebase
Your team has an iOS dev (Obj-C) and an Android dev (XML-based UI). This setup is crucial.
For Flutter:
- Pros: You both learn a new, singular technology (Dart/Flutter) from a similar starting point. It creates a unified team. The developer experience with hot reload, a single codebase, and a rich widget library is excellent. UI inconsistencies vanish.
- Cons: It’s a full rewrite. Your 7-year iOS veteran’s deep platform knowledge becomes less critical in the short term. The Android dev’s XML/View expertise doesn’t directly translate. You must also ensure Flutter’s plugins support your Bluetooth/WiFi device connectivity needs.
For KMP:
- Pros: Your Android dev can leverage Kotlin skills immediately. You incrementally extract and share business logic, preserving your native UI investments. It’s less disruptive initially.
- Cons: Your iOS dev, working in Objective-C, faces a steeper integration curve with Kotlin-generated frameworks. You now maintain three codebases: shared Kotlin, native Android UI, and native iOS UI. The “write once, run anywhere” promise is for logic, not UI.
The Bluetooth/WiFi Connectivity Test
This is a key practical hurdle. Let’s see how each framework handles a common Bluetooth operation.
In Flutter, you rely on community or official plugins. You write platform-agnostic Dart code.
import 'package:flutter_blue_plus/flutter_blue_plus.dart';
class DeviceScanner {
final FlutterBluePlus _flutterBlue = FlutterBluePlus.instance;
Future<List<BluetoothDevice>> scanForDevices() async {
// Start scanning
_flutterBlue.startScan(timeout: Duration(seconds: 10));
// Listen to results
return await _flutterBlue.scanResults
.firstWhere((results) => results.isNotEmpty)
.then((results) => results.map((r) => r.device).toList());
}
Future<void> connectToDevice(BluetoothDevice device) async {
await device.connect();
print('Connected to ${device.name}');
}
}
You must vet the plugin (flutter_blue_plus in this case) for stability, API coverage, and maintenance.
In KMP, you write the core scanning/connection logic in Kotlin, but the platform-specific implementation (using CoreBluetooth on iOS, Android Bluetooth APIs) lives in expect/actual declarations.
// In shared KMP module (CommonMain)
expect class PlatformBluetoothScanner {
fun startScan(deviceDiscovered: (DiscoveredDevice) -> Unit)
fun stopScan()
}
data class DiscoveredDevice(val name: String, val identifier: String)
// Android-specific implementation (AndroidMain)
actual class PlatformBluetoothScanner actual constructor() {
private val bluetoothAdapter: BluetoothAdapter? = ...
actual fun startScan(deviceDiscovered: (DiscoveredDevice) -> Unit) {
// Implement using Android Bluetooth APIs
}
actual fun stopScan() { /* Android impl */ }
}
// iOS-specific implementation (iOSMain)
actual class PlatformBluetoothScanner actual constructor() {
private val centralManager: CBCentralManager = ...
actual fun startScan(deviceDiscovered: (DiscoveredDevice) -> Unit) {
// Implement using CoreBluetooth
}
actual fun stopScan() { /* iOS impl */ }
}
KMP gives you fine-grained control but requires you to write and maintain the platform-specific bridges yourself.
Common Pitfalls to Avoid
- Underestimating Platform Channels (Flutter): Assuming Flutter plugins exist for every obscure native SDK. For complex native integrations, you may need to write custom platform channels, which requires native knowledge.
- Over-Promising Shared Code (KMP): Expecting 80%+ code sharing from day one. Start small (models, repositories). Sharing UI state logic is possible with libraries like
compose-multiplatform, but that’s a larger shift. - Ignoring Team Morale: Forcing a senior iOS dev into a pure Kotlin logic role or an Android dev into a full Dart rewrite without buy-in leads to friction. Let each team member prototype their preferred option.
A Practical Decision Framework
Ask your team these questions:
- What’s the primary goal? Uniform UI/UX and fastest feature parity → lean Flutter. Code reuse and preserving native feel → lean KMP.
- Can we afford a rewrite? If the app is large and complex, a full Flutter rewrite is a major project. KMP allows a gradual migration.
- Where is our complexity? If it’s mostly in complex, custom UI, Flutter’s widget system is a powerhouse. If it’s in intricate business logic and device communication, KMP’s logic sharing shines.
- What does our team want to learn? A unified Dart future, or a split path of Kotlin logic + native UI?
The Verdict
There is no universally “right” answer. For your specific scenario—with an Objective-C codebase and a need for deep Bluetooth/WiFi integration—the path of least initial resistance might be Kotlin Multiplatform. It allows your Android dev to drive the shared module while your iOS dev modernizes the UI layer to Swift/SwiftUI, integrating the shared Kotlin code.
However, if your long-term vision is a unified team, a single codebase, and freedom from native platform UI cycles, Flutter is the more transformative and ultimately simpler bet. The investment in learning Dart and rewriting the UI pays dividends in velocity and consistency.
Actionable Next Step: Don’t debate in the abstract. Spend one week on a spike. Have your Android dev build a simple screen (with a mock Bluetooth scan) in Flutter. Have your iOS dev integrate a small, shared KMP module (like a date formatter) into the existing app. The hands-on experience will reveal more than any blog post. The right framework is the one your team can successfully execute and maintain for the next five years.
This blog is produced with the assistance of AI by a human editor. Learn more
Related Posts
Flutter's Hidden Gem: Building Powerful Linux Desktop Apps with Ease
Flutter is gaining traction as a robust framework for Linux desktop development, offering a smoother experience compared to traditional toolkits like GTK and Qt. This post will explore why Flutter excels for Linux apps, highlight its advantages, and provide practical insights for developers looking to leverage Flutter for their desktop projects.
Simplifying Flutter Desktop Deployment: Signing and Distribution for Windows
Deploying Flutter desktop apps on Windows can be challenging, especially regarding code signing and preventing Windows from blocking installations. This post will guide developers through the process of signing their Flutter desktop applications, exploring alternatives to the Microsoft Store like creating executable installers, and covering the necessary steps to ensure a smooth user experience.
Mastering Flutter Tooling: Streamlining SDK Management and Installation on Windows
Developers frequently voice frustrations about Flutter SDK version management and installation processes, particularly on Windows. This post will explore best practices for managing multiple Flutter versions using tools like FVM, discuss integrating Flutter with package managers like Winget for a smoother Windows setup, and offer strategies to ensure a consistent development environment across projects and teams.