You can migrate a React Native app to Flutter without losing a single install, review or login, as long as the Flutter build ships as an update to the same app. That means the same bundle ID on iOS, the same package name and signing key on Android, a higher version number, a plan to read the data the old app saved on the device, and a staged rollout that you can pause. Users who skip that work see a logged out, empty app and leave.
The framework rewrite is the easy part to plan. What decides whether users stay is everything that lives outside your code: store identity, local storage, sessions, push tokens, deep links and purchases. This guide walks through each one in the order you need to deal with it, and it is honest about the cases where moving to Flutter is not worth it.
Who this is for: founders, product owners and engineering leads with a live React Native app on the App Store and Google Play who are weighing, or have already approved, a move to Flutter. The store behaviour described here is taken from Apple and Google documentation current in October 2026.
Should You Migrate From React Native To Flutter At All?
Migrate when the framework itself is the bottleneck, not when a few screens are slow. React Native has improved a great deal with its New Architecture, and a team that is fluent in React and TypeScript loses real speed during a switch to Dart. A migration is a product freeze in disguise, so it needs a reason that outlasts the disruption.
Signals that a move to Flutter usually makes sense:
- The UI is highly custom or animation heavy and you keep fighting platform differences to make iOS and Android look identical.
- Upgrades of React Native and its native libraries regularly break the build and eat whole sprints.
- You plan to reach web, desktop or embedded screens from the same codebase, and Flutter already supports the targets you need.
- Your team is being rebuilt anyway, or you already have Dart and Flutter skills in house.
Signals to stay on React Native:
- The app works, the team is productive in JavaScript, and the pain is limited to a handful of screens you can fix in place.
- You share a lot of logic with a React web app and that sharing is a genuine cost saving.
- You rely on over the air JavaScript updates to ship fixes between store releases. Flutter compiles to native code and has no built in equivalent, so your release process will change.
- You have no budget for a feature freeze or a parallel build. A half finished migration is worse than either framework.
What Losing Users Actually Means In A Migration
Users rarely leave because the new app is written in a different language. They leave because something they relied on quietly broke after the update. These are the failure points to design against, and every step later in this guide maps to one of them.
Risk | What causes it | How you prevent it |
|---|---|---|
App shows up as a new listing | A new bundle ID, package name or Android signing key | Ship Flutter as an update to the existing app records |
Users are logged out | Tokens saved by the React Native app are not read by the Flutter app | Read the old storage on first launch, or refresh the session server side |
Settings, drafts or offline data vanish | AsyncStorage and local databases live in locations Flutter packages do not read by default | Write a one time migration routine and test it on real upgraded devices |
Push notifications stop | Device tokens are not reregistered after the switch | Register and sync the token with your backend on first Flutter launch |
Links open the wrong screen | Route names or link handling changed | Keep every existing URL and route working, then redirect |
Paid users lose access | Entitlements were cached on the device only | Restore entitlements from the store or your server at launch |
Ratings drop | A regression reaches every user at once | Staged rollout with crash and review monitoring, plus a pause plan |
Rewrite Or Incremental Migration: Which Approach Fits Your App?
There are two realistic paths. The right one depends on how big the app is and how long you can afford to run two technologies side by side.
Option 1: Full Rewrite, Released As One Update
You build the complete Flutter app in parallel, reach feature parity, and ship it as a single version update to the existing store listings. This is the cleaner choice for small and mid sized apps, apps with heavy technical debt, and apps where a redesign is planned anyway. The cost is a long period where new features land in the old app only, or have to be built twice.
Option 2: Incremental Migration With Add To App
Flutter can be embedded into an existing app as a module. Google calls this add to app, and it supports Android and iOS hosts, hybrid navigation stacks where some screens are Flutter and some are not, and Flutter views embedded inside existing screens. A React Native app has native Android and iOS projects underneath it, so the Flutter module is added to that native layer and new or rewritten screens move over one at a time.
Be clear eyed about the trade offs before you choose this path:
- For a period you ship two runtimes, the JavaScript engine and the Dart runtime, which increases app size and memory use.
- Moving between React Native screens and Flutter screens, and sharing state such as the logged in user, is custom bridge work through native code. Plan and test it like a feature.
- Flutter documents some limits for mobile add to app: only one Flutter module can be packed into an app, Android hosts must use AndroidX, and plugins that assume a Flutter activity is always present can misbehave.
- Apps on the Expo managed workflow need native project folders generated first, because add to app works at the native layer.
Factor | Full rewrite | Incremental (add to app) |
|---|---|---|
Best for | Small to mid sized apps, apps due a redesign | Large apps that cannot freeze features for long |
User facing risk | Concentrated in one release | Spread across many smaller releases |
Engineering overhead | Two codebases in parallel until launch | Bridge code and two runtimes inside one app |
App size during migration | Unchanged until the switch | Larger until React Native is removed |
When it ends | On the day the Flutter update goes live | When the last React Native screen is removed |
The Step By Step Plan To Migrate React Native To Flutter
The steps below apply to both approaches. With a full rewrite you do them once before launch. With an incremental migration you do steps 1 to 4 up front and repeat steps 5 to 9 for every batch of screens.
Step 1: Audit The Existing App And Freeze Its Contract
Before anyone writes Dart, document what the current app promises its users and its backend. This list becomes your definition of done.
- Every screen, flow and edge state, including empty, error and offline states.
- Every value the app stores on the device, its storage key, its format and where it lives.
- Every native module and third party SDK, such as payments, maps, analytics, crash reporting and camera.
- Every URL scheme, universal link and Android app link the app responds to.
- Every analytics event name and property, so dashboards keep working after the switch.
- The API versions the app calls, so the backend does not change underneath you mid migration.
Step 2: Map React Native Dependencies To Flutter Equivalents
Most building blocks have a direct counterpart, and the official Flutter guide for React Native developers covers the core mappings in detail. The table below shows the ones that matter most in a migration plan.
React Native | Flutter equivalent | Migration note |
|---|---|---|
View, StyleSheet | Container, Row, Column and other layout widgets | Layout is composed from widgets rather than style props |
FlatList, SectionList | ListView.builder | Builds visible items on demand for long lists |
React Navigation | Navigator, or a router package such as go_router | Recreate every deep link route, not only the visible ones |
AsyncStorage | shared_preferences | Does not read existing AsyncStorage data, see Step 4 |
Keychain or keystore libraries | flutter_secure_storage | Check service names and keys before assuming tokens carry over |
fetch, axios | http or dio | Keep the same headers, timeouts and retry rules |
Redux, Zustand, Context | Riverpod, BLoC or Provider | Choose one state approach and use it everywhere |
Native modules | Platform channels or existing plugins | Often the slowest part of the estimate |
Mark every dependency with no mature Flutter package as a risk item. Those are the places where you will write platform code, and they are where estimates slip.
Step 3: Keep The Same App Identity In Both Stores
This is the single most important step for keeping users. To the App Store and Google Play, your Flutter build must look like the next version of the app people already have.
- iOS: use the same bundle identifier and the same App Store Connect record, and submit the Flutter build as a new version.
- Android: use the same application ID and sign the release so Google Play delivers it with the same app signing key. Android only installs an update when the certificate matches the installed version, so a different key or package name produces a separate app instead of an update.
- Version numbers: set the Flutter version and build number higher than the last React Native release. In Flutter both come from the version line in pubspec.yaml.
- Signing assets: confirm who holds the upload key, the Apple certificates and the provisioning profiles before development starts, not in launch week.
Keeping the same listing also keeps your ratings, reviews, download history and store search position, which a new listing would have to earn again from zero.
Step 4: Plan The Local Data Handoff
When the Flutter update installs, the files the React Native app wrote are still on the device, but Flutter will not find them on its own. AsyncStorage keeps its data in its own storage location, and shared_preferences reads a different one. If the app keeps drafts, preferences, onboarding state or offline records, someone has to move them.
- On first launch, check a flag that records whether migration has already run.
- If it has not, read the old values through a small platform channel or a package that can access the old storage location.
- Write them into the new Flutter storage, verify the result, then set the flag.
- Keep the old data in place for at least one release so a rollback does not wipe anything.
Where possible, move important data to your backend before the switch. Anything the server holds can simply be fetched again, which is far safer than relying on device side migration code.
Step 5: Protect Logins, Push Notifications And Deep Links
Logins. Forcing every user to log in again is one of the fastest ways to lose them. Either migrate the stored session tokens in Step 4, or let the Flutter app exchange the old token for a new one on first launch. If you use Firebase Authentication with the same Firebase project, the session is managed by the native Firebase SDKs and usually carries over, but treat that as something to prove in testing, not to assume.
Push notifications. Register for push on the first Flutter launch and send the device token to your backend, even if you expect it to be unchanged. Reuse the same Firebase project and notification configuration so messages keep arriving.
Deep links. Marketing emails, QR codes and shared links will keep pointing at old paths for years. Keep every existing URL scheme and link path working in the Flutter router, and redirect retired paths to the closest current screen instead of failing silently.
Step 6: Carry Over Purchases And Subscriptions
Purchases belong to the user and the store account, not to the framework, so they survive the migration as long as the product IDs stay the same. What breaks is the app’s knowledge of what the user owns. On first launch, restore entitlements from the store or from your own server, and never rely on a flag the React Native app cached on the device. Test with sandbox accounts that bought each product type, including an expired and a renewed subscription.
Step 7: Reach Feature Parity Before Launch
Users judge the update against the app they had yesterday. Hold the release until every item from the Step 1 audit works in Flutter, including accessibility labels, dark mode, font scaling and offline behaviour. Keep analytics event names identical so you can compare retention and conversion before and after the switch on the same dashboards.
This is also the wrong release for a redesign or new features. If retention drops, you want to know whether the migration caused it, and you cannot tell if ten other things changed at the same time.
Step 8: Test The Upgrade Path, Not Only The New App
A fresh install of the Flutter app proves very little. The real test is a device that has been running the React Native version for months. For each supported OS version:
- Install the current production React Native version from the store or TestFlight.
- Log in, change settings, save drafts and offline data, buy or restore a subscription and allow push notifications.
- Install the Flutter build over it as an update, without deleting the app.
- Confirm the user is still logged in, data and settings are intact, a test push arrives, purchases are recognised and old deep links open the right screen.
Run the same script on internal testers through TestFlight and a Google Play testing track before any public release.
Step 9: Roll Out In Stages And Watch The Right Metrics
Never send the first Flutter version to everyone on day one. Both stores let you release gradually:
- App Store: Apple’s phased release for version updates spreads automatic updates over 7 days, starting at 1 percent of users and reaching 100 percent on day 7. You can pause it for up to 30 days in total. Anyone can still download the update manually from the store during this time.
- Google Play: a staged rollout on Google Play sends the update to a percentage you choose and only increases when you raise it. If you halt it, no new users receive the version, but users who already have it stay on it.
Because users who already updated stay on the new version, your fix path is a new build, not a rollback. Have a hotfix process ready before you start the rollout. Watch these numbers for each step of the rollout and compare them with the last React Native release:
- Crash free users and app not responding rates.
- Login success and forced logout events.
- Day 1 and day 7 retention for updated users.
- Push delivery and open rates.
- New store reviews and their wording, which often explain a problem before the metrics do.
Step 10: Remove React Native Cleanly
Once the Flutter version has been at 100 percent for a stable period, remove the React Native code, the JavaScript bundle and the data migration routine in a later release. Keep the migration code until your analytics show that almost no active users are still opening the app from an old version, because people who skip several updates will still arrive with React Native data on their devices.
What Drives The Timeline And Cost Of A React Native To Flutter Migration
There is no honest single number for a migration, and any fixed quote given before an audit should be treated with caution. These are the factors that move the estimate most:
Factor | Why it matters |
|---|---|
Number of screens and flows | The base volume of UI work to rebuild |
Native modules and SDKs | Each one without a mature Flutter plugin needs platform code |
Local data and offline features | More stored data means more migration code and more testing |
Payments and subscriptions | Requires careful sandbox testing on both stores |
Approach | Add to app adds bridge work but avoids a long feature freeze |
Test coverage in the old app | Existing tests double as a parity checklist |
Team experience with Dart | Ramp up time is real for a JavaScript first team |
A practical way to get a defensible estimate is a short discovery phase: complete the Step 1 audit, rebuild one or two representative screens in Flutter, and price the rest from what that pilot actually took.
Common Mistakes That Cost Users During A Migration
- Publishing the Flutter app as a new store listing and asking users to switch.
- Testing only clean installs and never an upgrade from the live React Native version.
- Shipping a redesign, new pricing and the framework change in the same release.
- Renaming analytics events, which makes it impossible to compare before and after.
- Releasing to 100 percent on day one with no hotfix plan.
- Deleting the data migration routine too early, which strands users who update late.
- Losing access to the Android upload key or Apple certificates when a previous agency or developer leaves.
When To Bring In An Experienced Flutter Team
If your app has paying users, offline data or a lot of native modules, the risky part of this project is not writing Flutter widgets. It is the upgrade path: identity, data handoff, sessions, purchases and rollout. A team that has done this before will usually spend its first weeks on exactly those items before building screens.
If you want a second opinion on your migration plan, our Flutter app development company can review your current React Native app, flag the parts most likely to cost you users, and recommend a full rewrite or an incremental path based on what we find.
Frequently Asked Questions
Q: Can I Convert A React Native App To Flutter Automatically?
No reliable tool converts a production React Native codebase into maintainable Flutter code. The languages, UI models and state patterns are different. You can reuse your designs, API contracts, business rules, test cases and assets, but the app code is rewritten.
Q: Will Users Have To Download A New App?
Not if you ship Flutter as an update with the same bundle ID on iOS and the same application ID and signing key on Android. Users receive it like any other update and keep their reviews, ratings and install history.
Q: Will My Users Be Logged Out After The Update?
Only if the Flutter app cannot find the session the React Native app saved. Migrate the stored tokens on first launch or refresh the session through your backend, and confirm it works by upgrading a real device that is already logged in.
Q: Can React Native And Flutter Run In The Same App?
Yes. Using Flutter add to app, a Flutter module can be embedded in the native Android and iOS projects underneath a React Native app, so you can move screen by screen. Expect larger app size during the transition and custom work to share navigation and state between the two.
Q: How Long Does A React Native To Flutter Migration Take?
It depends mainly on the number of screens, native modules and offline features, and on whether you rewrite in one go or migrate incrementally. A discovery phase with a small pilot gives a far more accurate estimate than a guess based on screen count alone. If you would like help scoping it, you can talk to a Flutter migration team about your app.



