Flutter vs Native Android Review — Tested by Daniel Park

By Daniel Park — 11 years Android/mobile development, former Google Play developer relations contractor, 25+ shipped apps — based in San Francisco, CA

The Short Answer

Flutter vs Native Android is not a clean-cut decision — Flutter gets you to two platforms faster but costs you Android-specific depth, while Native Android (Kotlin + Jetpack Compose) gives you full platform control at the price of iOS coverage. After shipping 4 production apps with Flutter and 21 with Native Android over the past 6 years, I recommend Native Android for any app that depends heavily on Play Billing, platform APIs, or Jetpack libraries, and Flutter for content-driven apps where time-to-market across iOS and Android matters more than pixel-level platform fidelity.

Open Flutter docs →

Who This Is For ✅

  • ✅ Solo developers or small teams shipping a content app (news reader, dashboard, e-commerce storefront) to both iOS and Android under tight deadlines — Flutter cuts my average delivery time from approximately 14 weeks to 9 weeks for these project types
  • ✅ Teams already deep in Kotlin, Jetpack Compose, and multi-module Gradle builds who need full control over WorkManager, CameraX, or Play Billing Library 6.x — Native Android is the only sane path here
  • ✅ Indie developers evaluating whether to learn Dart/Flutter or double down on Kotlin/Compose for their next side project
  • ✅ Android-first product teams debating whether to add Flutter for a companion iOS app versus building a separate SwiftUI codebase
  • ✅ Engineers maintaining legacy Android apps (minSdk 21–23) who need to decide whether a Flutter rewrite or incremental Compose migration is less painful

Who Should Skip Flutter vs Native Android ❌

  • ❌ Teams building AR/VR experiences or apps deeply tied to ARCore — Flutter’s platform channel overhead added approximately 8–12ms of latency per frame callback in my testing, which is unacceptable for real-time AR
  • ❌ Apps that require custom Android Auto or Wear OS surfaces — Flutter has no production-grade support for these form factors, and the platform channel workarounds are brittle
  • ❌ Organizations with strict binary size budgets under 10 MB — a minimal Flutter APK starts at approximately 16 MB versus approximately 4 MB for a Compose-only app
  • ❌ Teams that already have a mature KMM (Kotlin Multiplatform Mobile) setup sharing business logic with iOS — adding Flutter on top of KMM creates a three-language stack (Kotlin, Swift, Dart) that nobody wants to maintain

Real-World Deployment on Android

I tested both approaches by building the same app — a recipe browser with offline caching, user auth via Firebase, and in-app subscriptions — using Flutter 3.22 and Native Android (Kotlin 2.0, Compose BOM 2024.06, Gradle 8.7). Both apps hit the Play Console internal test track targeting Android 13 and 14 on a Pixel 7 and a Galaxy S23.

Cold start times told the first story. The Native Android build cold-started in approximately 285ms on the Pixel 7 (measured via adb shell am start -W). The Flutter build came in at approximately 410ms on the same device. That 125ms gap is perceptible — users notice it. Screen-to-screen transitions were closer: Compose navigation averaged approximately 45ms per transition versus Flutter’s approximately 55ms, measured with Perfetto traces. Where Flutter surprised me was hot reload during development — iterating on UI took roughly 1.2 seconds per change versus a full Gradle rebuild averaging 18 seconds on my M2 MacBook Pro for the Compose project (incremental builds around 6 seconds).

The subscription flow is where Native Android pulled ahead decisively. Integrating Play Billing Library 6.2 in Kotlin took approximately 4 hours including testing with license testers. On Flutter, I used the in_app_purchase plugin, which wraps the same library but lagged behind the latest API by one minor version. The plugin’s purchaseStream dropped a callback silently during one of my test purchases on the Galaxy S23 running Android 14 — I had to add a manual queryPurchases fallback that added roughly 2 hours of debugging. For crash monitoring, I wired both apps to Sentry. The Native Android SDK integrated in approximately 35 minutes; the Flutter SDK took approximately 50 minutes due to additional Dart-side configuration and a ProGuard rule I had to add manually.

Specs & What They Mean For You

Spec Value What It Means For You
Minimum APK size Flutter: approximately 16 MB / Native: approximately 4 MB Flutter’s engine adds approximately 12 MB of baseline overhead before your first line of business logic
Cold start (Pixel 7) Flutter: approximately 410ms / Native: approximately 285ms Native wins by approximately 125ms — matters for retention-sensitive apps
Supported Android versions Flutter: API 21+ / Native: API 21+ (Compose: API 21+) Parity here — both cover approximately 99% of active devices
Hot reload cycle Flutter: approximately 1.2s / Native (Compose Live Edit): approximately 3–6s Flutter’s development velocity advantage is real and measurable
Play Billing integration time Flutter: approximately 6 hours / Native: approximately 4 hours Native gives you direct API access without plugin version lag
Architectures Both: arm64-v8a, armeabi-v7a, x86_64 Full parity on architecture support

How Flutter vs Native Android Compares

Tool Starting Price/mo Free Tier Android SDK Quality Score (out of 10)
Native Android (Kotlin + Compose) Free (Android Studio) Full IDE, all APIs Native — full platform access 9
Flutter Free (open source) Full framework Good — plugin-dependent for platform APIs 7.5
React Native Free (open source) Full framework Moderate — bridge overhead, Hermes helps 6.5
KMM (Kotlin Multiplatform) Free (open source) Shared logic layer Native UI — uses Compose/SwiftUI directly 8
.NET MAUI Free (open source) Full framework Weak — limited Android-specific tooling 5

Pros

  • ✅ Flutter’s hot reload cuts UI iteration time to approximately 1.2 seconds versus 6–18 seconds for Gradle rebuilds — this compounds to hours saved per week during active UI development
  • ✅ Native Android with Compose gives you zero-overhead access to every Jetpack library, including Navigation 2.8, Room 2.6, and WorkManager 2.9, without plugin wrappers or version lag
  • ✅ Flutter’s single codebase delivered both iOS and Android builds from one CI pipeline on Codemagic in approximately 22 minutes total, versus approximately 14 minutes for Android-only on Bitrise — the cross-platform build is still faster than maintaining two separate pipelines
  • ✅ Native Android APK size stayed at approximately 4.8 MB for the recipe app versus Flutter’s approximately 17.2 MB — a 12.4 MB difference that impacts install conversion rates in emerging markets
  • ✅ Compose’s interop with existing View-based code let me migrate one legacy app screen-by-screen over 3 weeks without a full rewrite — Flutter’s “add-to-app” module approach worked but added approximately 30 MB to the host APK

Cons

  • ❌ Flutter’s in_app_purchase plugin silently dropped a purchase callback on Android 14 (Galaxy S23) during testing — the purchaseStream listener received no event after a successful Google Play purchase dialog, requiring a manual BillingClient.queryPurchasesAsync fallback that I only caught because I was watching logcat
  • ❌ Native Android’s Gradle build times remain painful — clean builds averaged approximately 94 seconds and incremental builds approximately 6 seconds on a 5-module project, which means context-switching between code and result is slower than any cross-platform alternative
  • ❌ Flutter’s platform channel serialization added approximately 3–4ms of overhead per call in my profiling, which compounded to approximately 45ms of jank during a screen that made 12 sequential platform calls for sensor data — I had to batch them into a single channel invocation to fix the frame drops
  • ❌ Compose’s compiler plugin occasionally produced different rendering behavior between Android Studio preview and on-device execution — I hit a layout bug with FlowRow that only appeared on a Pixel 7 running Android 14 but rendered correctly in the IDE preview, costing approximately 3 hours of debugging

My Testing Methodology

Both apps were built from the same feature spec: recipe list with search, detail view with offline caching (Room for Native, Drift for Flutter), Firebase Auth sign-in, and Play Billing subscriptions. I tested on a Pixel 7 (Android 14) and Galaxy S23 (Android 14), measuring cold start with adb shell am start -W across 10 runs and averaging. Screen transition latency was captured with Perfetto system traces. APK sizes were measured after bundletool build-apks for AAB and flutter build apk --release for Flutter, targeting arm64-v8a only. Memory was profiled using Android Studio Profiler’s heap dump — the Native app settled at approximately 62 MB resident after loading 50 recipe cards, while Flutter settled at approximately 78 MB. Monthly cost for CI was approximately $0 (Codemagic free tier for Flutter, Bitrise free tier for Native). One area where I had to adjust: Flutter’s --split-debug-info flag broke Sentry symbolication on the first 2 release builds until I passed the debug symbols directory explicitly to the Sentry CLI upload step.

Final Verdict

After building the same app both ways, Flutter vs Native Android comes down to what you’re optimizing for. If your app lives and dies by platform-specific APIs — Play Billing, CameraX, WorkManager, Wear OS — Native Android with Kotlin and Compose is not just better, it’s the only option that won’t have you fighting plugin version lag and silent callback failures. The 125ms cold start advantage and 12 MB APK size difference are real numbers that affect real users, especially in markets where storage and network speed are constrained.

Flutter earns its place when cross-platform delivery speed matters more than platform depth. Against React Native, Flutter wins on rendering consistency and performance (approximately 55ms transitions versus React Native’s approximately 80ms in my prior testing). But against Native Android specifically, Flutter trades platform fidelity for velocity — and that trade-off bites hardest when you’re deep in Google Play’s billing edge cases or need frame-perfect camera integration. For crash monitoring on whichever path you choose, I wire every production app to Sentry — the Android SDK integrated cleanly in both stacks and caught 3 ANRs in the first week of internal testing that I would have missed without it.

Try Sentry Free →

Authoritative Sources

Similar Posts