Quick Summary

This insight covers a real native app to Flutter migration we ran for a D2C e-commerce client with separate iOS and Android codebases and an active checkout flow we could not freeze. The focus is on the four Phase 0 decisions made before any Dart was written, the sequencing we used, and the measured outcomes at 90 days post-go-live.

Introduction

Most native app to Flutter migration guides on the internet walk through the same five phases: audit, architecture, module migration, testing, and rollout. That framing is accurate but incomplete. It skips the part where engagements actually succeed or stall, which is Phase 0, the set of decisions made before a single line of Dart code is written.

We saw this directly on a recent native app to Flutter migration for a D2C e-commerce retailer running separate Swift and Kotlin codebases. Their checkout traffic was too high to freeze, their iOS feature parity gap had stretched to several weeks behind Android, and a previous attempt with a different vendor had been abandoned half a year in. The difference between that failed attempt and the engagement that shipped was entirely in the Phase 0 decisions. This insight walks through what we decided before we started, why we sequenced the migration the way we did, and what the 90-day numbers looked like on the other side.

What the Client Walked in With

A D2C e-commerce brand in the nutrition category with over a million monthly active users across iOS and Android. Two separate native teams, around ten engineers in total, both maintaining independent codebases that had diverged meaningfully over four years. The checkout flow alone carried a meaningful share of in-app revenue and could not tolerate more than a short downtime window in any single deploy.
The structural problems going in:

  • IOS releases consistently lagged Android by 4 to 6 weeks because the iOS team was understaffed and the Swift codebase carried more technical debt from a 2022 redesign that never fully completed.
  • QA coverage was uneven. The Android team had a mature Espresso suite. iOS had XCUITest but with a significant portion of critical flows uncovered, which showed up as production regressions a couple of times per quarter.
  • Product roadmap was planning a web storefront refresh in the following 18 months, which meant the eventual Flutter web target was part of the business case, not a side benefit.
  • The previous vendor had attempted a Big Bang rewrite and abandoned it because they had frozen native development during the rebuild. Six months of no new features had cost the client a measurable hit on conversion.

That last point is important. By the time they came to us, the client was not asking whether to do a native app to Flutter migration. They were asking how to run one without repeating the mistake that had already cost them once.

Why Flutter Was the Right Target in 2026 Specifically

The Flutter ecosystem in 2026 is not what it was even two years ago, and the timing of a native app to Flutter migration matters more than most articles acknowledge. Flutter 3.41 shipped with Impeller as the only rendering engine on iOS and the default on Android API 29+, which the Flutter team reports delivers 30–50% fewer jank frames during complex animations compared to the old Skia pipeline. For an e-commerce app where scroll smoothness on the product listing page directly correlates with session depth, that is not a cosmetic improvement.

Market adoption reinforced the decision. Appfigures reported in 2025 that non-native frameworks now power 15% of all new app releases, with Flutter present in roughly 1 in 4 new mobile apps across iOS and Android by mid-2025. That adoption curve matters for hiring. Dart developer supply has caught up with demand in a way that was not true in 2022, which directly affects the team ramp-up risk that kills most migrations.

Phase 0: The Four Decisions We Made Before Writing Any Code

Every native app to Flutter migration engagement we have run since 2022 has come down to four decisions made in the first 3 weeks. Getting them right takes longer upfront and saves months later. Getting them wrong produces the abandoned-vendor outcome the client had already lived through.

Decision 1: Big Bang vs Add-to-App vs Hybrid, decided by revenue exposure

The default industry answer for a native app to Flutter migration is “do Add-to-App because it is safer.” That is directionally right and specifically wrong. The real question is how much of your revenue is routed through flows that must stay online during the migration. For this client, a substantial share of revenue lived in checkout. A Big Bang rewrite would have meant freezing that flow, which was the exact mistake that killed the previous attempt. We picked phased Add-to-App, with one addition: checkout, payment gateway integration, and the address book module stayed native for most of the migration and only moved over after we had proven Flutter stability in lower-risk surfaces.

For reference, the three migration strategies we evaluate on every engagement:

  • Big Bang (Full Rewrite): The entire app is rebuilt in Flutter from scratch. Clean-slate approach, strong fit when the existing codebase carries significant technical debt. The downside is real: native development freezes during the rewrite, no new features or bug fixes for users until the Flutter version is complete.
  • Phased Migration (Add-to-App): Flutter is embedded into the existing native app as a module. Individual screens or flows migrate incrementally while the rest of the app continues running natively. Trades speed for stability; the business never has to freeze development.
  • Hybrid Migration: Core UI flows and business logic move to Flutter. Modules with deep native dependencies, such as payments, biometrics, and push notification handling, stay native and communicate with Flutter via platform channels. Works particularly well for fintech and healthcare applications where compliance-specific integrations are tightly coupled to the native layer.

Decision 2: State management picked before folder structure

We chose Riverpod over Bloc for this client, even though Bloc has stronger enterprise-pattern inertia. The reason was specific: part of the client team was already familiar with Provider from a prior project, and Riverpod’s compile-safe provider graph was closer to what they knew than Bloc’s event/state ceremony. A native app to Flutter migration where developers fight the state management pattern for the first month or two will run meaningfully over budget every time. Picking the pattern that matches the team’s existing mental model at the start of a native app to Flutter migration saved roughly that.

Decision 3: The design system was built before the first screen migrated

Flutter’s rendering engine gives you pixel-identical output across iOS and Android, which sounds like a feature until you realize it also means your app can feel subtly wrong on both platforms simultaneously if you do not invest in theming. In a native app to Flutter migration with an established brand, that feel-wrong risk is real. We built a Material 3 + Cupertino adapter layer in week 3, before any production screen was migrated. Every migrated Flutter screen routes through this layer. Result: when Android users saw migrated screens, the feel matched native Android conventions; same for iOS. The hybrid phase did not produce the jarring cross-platform dissonance that usually shows up in half-migrated apps.

Decision 4: The BaaS and analytics stack decision was pulled forward

Firebase had been the Android analytics layer; iOS used a mix of Firebase and Amplitude. The instinct on most native app to Flutter migration projects is to defer consolidation until post-migration. We did the opposite. Early in the engagement, we picked Firebase as the sole analytics surface, wrote an Amplitude-to-Firebase event mapping, and migrated the native event pipelines before touching UI. This meant every migrated Flutter module emitted events into a consistent schema from day one. Most teams discover their event taxonomy is broken well into the migration, during the analytics cleanup sprint nobody budgeted for. We front-loaded that work and skipped the cleanup entirely.

Planning a native app to Flutter migration, but unsure where to start?

Hire Flutter developers to get a structured migration plan and execution you can trust.

How We Sequenced the Native App to Flutter Migration

Sequencing is where the industry process guides for a native app to Flutter migration are most useful and also most generic. “Start with low-risk UI-heavy modules” is correct advice that does not tell you which module is low-risk for your app. For this client, the sequencing looked like this:

Phase Module Migrated Why This Order
Phase 1 Onboarding, account settings, notifications preferences No revenue exposure, UI-heavy, let the team learn Flutter under real conditions
Phase 2 Product catalog, search, category browse High traffic, low transactional risk. Validated Impeller performance under real product image loads before anything revenue-critical moved
Phase 3 Cart, wishlist, recommendations Cart reads checkout data but does not write payment data. Good bridge between browsing and transactions
Phase 4 Account, order history, subscriptions Deep customer data, but read-mostly. Exposed the subscription billing integration edge cases we needed to map before checkout
Phase 5 Checkout, payment, address book Migrated last. By this point the team had most of a year of Flutter production experience, and Add-to-App had proven stable across every prior module

One specific sequencing lesson worth calling out: we migrated the recommendations module before subscriptions, not after. The recommendations module was newer Kotlin code with good test coverage. Migrating it first let us ship a Flutter win to the business side (the team rebuilt the recommendation card with better animation in Flutter and the CTR lift showed up in Firebase soon after rollout) which bought us the credibility to do the slower subscription migration without the product team pushing back on velocity.

What Actually Broke During the Native App to Flutter Migration

Three specific problems from this engagement that most native app to Flutter migration writeups skip over.

Platform channel cost ran well over our initial estimate

We budgeted several weeks of platform channel development for Apple Pay, Google Pay, and the biometric auth bridge. It took more than twice that. The issue was not the channels themselves but the lack of plugin parity for a specific subscription billing SDK the client used. We ended up writing a custom platform channel from scratch, which is non-trivial in Dart for developers coming from Kotlin/Swift. Building platform channels before the UI modules that need them became the practice we carried into every subsequent native app to Flutter migration engagement.

The QA team's Espresso suite did not translate

The Android team had spent over a year building Espresso coverage. Almost none of it transferred to Flutter’s widget test + integration test pattern. We rebuilt the test suite in parallel with the first few migrated modules, which meant QA was effectively running two test infrastructures simultaneously for months. Budgeting this upfront would have avoided the velocity dip early in the migration that the product team flagged. Every native app to Flutter migration we have run since has QA rebuild as a parallel workstream from week 1.

The team Dart ramp-up was smoother than expected

We had budgeted a month and a half for the Kotlin developers to reach productive Dart velocity on this native app to Flutter migration. They got there faster than that. The iOS team took roughly the same. The difference from 2022 Flutter engagements was concrete: Dart 3.9 null safety and async patterns now feel close enough to Kotlin coroutines and Swift async/await that the mental model transfer is faster than it used to be. This saved weeks of calendar time we had reserved for training.

The 90-Day Results After Full Native App to Flutter Migration

Directional outcomes from the client’s own dashboards 90 days after checkout went fully live on Flutter, compared with the native app baseline from the month before the native app to Flutter migration started. Numbers are anonymized ranges rather than exact readings from a specific dashboard.

MetricBefore (Native) 90 Days Post-Migration
iOS vs Android feature parity gap Several weeks Same-day parity
Time to ship a cross-platform feature Baseline Roughly half
Product listing page scroll smoothness Visible jank on mid-tier devices Smooth on same device class, attributed to Impeller
Crash-free sessions rate Stable but below target Improved and above the internal target
Per-feature engineering effort BaselineMeaningful reduction (roughly 30–40% lower)
Checkout conversion rate BaselineIncremental lift, partially attributed to smoother scroll on cart

Two caveats worth naming. The checkout conversion lift is partly attributable to a separate address-autofill improvement that shipped late in the engagement, so we do not claim 100% of that number as Flutter-driven. And the per-feature engineering effort reduction assumes stable team composition. A native app to Flutter migration does not reduce costs if you also churn through 40% of your engineering team in the same window.

What We Would Do Differently on the Next Native App to Flutter Migration

Three things. First, we would budget 10 weeks for platform channel work upfront instead of 4. Flutter’s plugin ecosystem has broad coverage, but the long tail of enterprise SDKs (specific subscription billing platforms, legacy MDM solutions, region-specific payment processors) still requires custom channels more often than plugin marketplace searches suggest.

Second, we would start the QA rebuild one full month before the first module is migrated, not alongside it. The velocity dip early in the engagement was preventable and cost roughly three weeks of calendar time we did not need to lose.

Third, we would not defer the analytics stack consolidation. We got lucky that the client had only two analytics tools; had they been on five, the pre-migration cleanup would have blown the timeline. On the next engagement, we will insist on the consolidation in Phase 0 regardless of whether the client thinks it is urgent.

Conclusion

A native app to Flutter migration is not a framework swap. It is a phased operational change that affects how your product team ships, how your QA team tests, and how your customers experience the app during the 12 to 18 months the migration is live in production. The process guides currently ranking on page one of Google cover the mechanical phases. The gap most of them leave is Phase 0, the decisions about strategy, state management, design system, and analytics that get made before code, and that determine whether the engagement ships or joins the pile of abandoned rewrites.

For teams evaluating whether to start, the decision has gotten clearer in 2026 than it was even 24 months ago. Impeller is stable on both platforms by default, Dart developer supply has caught up with demand, and the Flutter 3.41 tooling makes hot reload on web stable enough for production workflows. What has not changed is that the engagement playbook matters more than the technology choice. Explore our Flutter Migration Services to understand what Phase 0 looks like for your specific app, your revenue exposure, and your team composition, before any Dart is written.

Build Your Agile Team

Hire Skilled Developer From Us