Migration to Flutter Guide
Discover our battle-tested 21-step framework for a smooth and successful migration to Flutter!

Flutter Add-to-App: Overview, Challenges, and Lessons From Real-Life Cases

Marcin Chudy - Senior Frontend and Flutter Developer at LeanCode
Marcin Chudy - Senior Flutter Developer at LeanCode
Sep 11, 2026 • 15 min
Flutter mobile development by LeanCode
Marcin Chudy - Senior Frontend and Flutter Developer at LeanCode
Marcin Chudy
Senior Flutter Developer at LeanCode

Rating: 5.00 / 5 Based on 22 reviews

Flutter mobile development by LeanCode

Rating: 5.00 / 5 Based on 22 reviews

Flutter has taken the mobile market by storm. We often hear from our clients that they are considering building an application in Flutter. Although starting a new project with Flutter is often straightforward, the decision is harder when you already have an existing iOS or Android app.

A complete rewrite is not always practical. Existing users still expect new releases, business teams still need new features, and a large native application cannot simply disappear while its replacement is being developed. This is where Flutter Add-to-App comes in.

In this article, we'll cover:

  • what Flutter Add-to-App is and when to use it;
  • lessons from our first banking project with Add-to-App;
  • key Flutter and native integration challenges;
  • what has improved and what still needs work;
  • how we use Add-to-App today across 90+ banks;
  • our recommended migration strategy.

It will be based on real-life cases from the banking industry. If you are looking for a list of enterprise companies that use Flutter, check out our article.

Choose your migration strategy wisely
Migration to Flutter ebook mockup

What is Flutter Add-to-App?

Add-to-App is a Flutter feature developed by Google. You don’t always have to write a Flutter application from scratch. Flutter can be integrated into your existing application piecemeal as a library or module. 

If you have an existing native mobile application for Android and iOS, you can use Flutter to render only some views in your application or reuse some business logic in Dart between the two platforms.

Flutter Add-to-App can be added to iOS (Flutter can be incrementally added into your existing iOS app seamlessly with CocoaPods or with pre-generated embedded frameworks) and an Android app (Flutter can be embedded into your existing Android app piecemeal, as a source code Gradle subproject or as AARs).

Summing up: Flutter renders part of an app that already exists - a screen, a flow, or a single component (embedded inside native screens). The rest stays native. You add Flutter piecemeal and keep shipping instead of betting on a big-bang rewrite.

When to use Flutter Add-to-App?

Cross-platform development is a very promising concept for Product Owners. It saves their time and money by maintaining one codebase between iOS and Android apps, making it easier to manage the development team with a single Sprint goal.

Therefore, cross-platform frameworks such as Flutter and React Native have become the first choice for greenfield projects, including startups and new enterprise ventures.

However, things can be more complicated if you already have an existing application (iOS or Android) and consider changing the technology. In that case, you can integrate Flutter, and this option is called Add-to-App. It enables the mobile development team to add Flutter features to the existing native app.

Yes, you read it correctly: you can integrate the Flutter framework and take the benefits of cross-platform development even if you have previously developed native code. 

Yet there are strict conditions under which this scenario is worth considering.

1. Gradually rewriting the existing mobile application

This is perhaps the most obvious scenario. It means you have decided to build the new app with Flutter to replace the existing native solution, but you don’t want to suspend releases of new tasks. In that case, a Flutter Add-to-App will help you add new features to the existing Android or iOS app while replacing the existing ones with the new Dart code.

The Flutter module scenario works best when one team is working on new features and adding them to the app, while the other mobile team works in the background to rewrite the native part. Such an approach is recommended for existing applications with a solid user base in a highly competitive market, where introducing new features is vital.

However, as we'll see later, the order in which you migrate screens matters enormously.

2. Getting the arguments for using Flutter technology

Suppose you are a Flutter advocate looking for a way to attract business stakeholders to the idea of rebuilding your current mobile app in Flutter. In that case, it is a good idea to showcase some small, additional features developed as Flutter Add-to-App.

This will help you demonstrate the most significant advantage of this technology: ease and development speed. As a result, it increases the chances of approval to continue implementing Flutter throughout the app.

If you are looking for more pros and cons of Flutter, check out also this article.

3. Building a proof of concept type of app

The main point of building a proof of concept is to test whether a particular concept is feasible and beneficial from a technical standpoint. This is similar to the point mentioned above. 

However, it means you don’t need to release that feature to your end users; you only need to showcase it to some internal stakeholders. This is enough to prove whether your mobile app's performance will be satisfying or not.

4. Implementing a small, isolated feature

Let’s assume that your current native mobile app is working and you don’t have the native team assembled to perform new tasks. Yet, you want to add some fairly isolated features. 

Previously, the only chance to do so was to consider adding the PWA component and displaying the integrated WebView. However, you can stick to the components created in Dart with Flutter. This will allow you to deliver a better user experience in Android and iOS apps and save time by avoiding the need to gather the two native teams for the same task on both platforms.

5. Improving the UI of your current application

One of the strongest selling points of building applications in Flutter is the ease of implementing custom, complex user interfaces. And with Flutter Add-to-App, it’s no different. This is where Flutter can spread its wings and significantly speed up development. Once you add Flutter to the existing application and start drawing things on the screen, the UI development is the same flawless experience as with pure Flutter apps. And the app's UI in Flutter looks really well.

The con of mixing two technologies in a single app is that if you want the design consistent across all screens (which is most often the case), many components of the app's UI in Flutter need to be rewritten from scratch. It might be technically possible to reuse some native components, but it kind of breaks the purpose of using Flutter in the first place. And for later maintenance, keeping all the UI components in sync can be a significant managerial challenge.

Our first Add-to-App project: proving Flutter in a banking application

Our first major experience with Add-to-App came several years ago in the banking industry.

At LeanCode, a Flutter app development company, we had the opportunity to develop a Flutter Add-to-App proof of concept for a major banking client - Millennium Bank. Our goal was to prove that Flutter was the right technology for a new mobile banking application that was to be written from scratch. 

Step 1: Prove it first

As part of the PoC, we verified some native features, such as the camera, biometrics, and animations. The main part was to rewrite one complex business process in an existing native app.

The process involved integrating with existing native screens and APIs, feature flag mechanisms, and doing some background processing. Considering the sector, fulfilling strict security requirements was also highly important.

We rebuilt a complex BLIK payment process inside an existing retail application with 2M+ MAU.

Step 2: Two phones, one module

For stakeholders, we wanted the comparison to be tangible. They were handed two phones: one with a native screen and the other using Flutter, and they couldn't tell the difference between them.

One version of the process remained native. Another used Add-to-App and Flutter. That meant stakeholders could compare the experience directly rather than evaluating Flutter from a presentation or isolated prototype.

The Add-to-App part itself was a proof of concept rather than functionality intended to become the final production implementation. The goal was to answer a bigger question: Could Flutter be trusted to build the new application?

It could.

Step 3: Working against immature tooling

It is important to put the experience in its historical context. Add-to-App was significantly rougher than it is today.

Some of the infrastructure available now either didn't exist or wasn't mature enough to rely on. Engine groups, for example, were not yet something we could use as we would today. For background Flutter execution, we had to build significant infrastructure ourselves just to support requirements like BLIK's local notifications.

And yet the experiment succeeded. At the same time, that first project taught us a great deal about where Add-to-App becomes difficult.

Challenges of introducing the Flutter Add-to-App feature

1. First of all, the official documentation leaves something to be desired. The Flutter Add-to-App approach is not the default or recommended way to create apps with Flutter. 

The general process is described in the docs, and it seems straightforward. When it comes to integrating with a big, long-lived native app, where many things often have been implemented in a custom, non-standard way, the integration can cause many hard-to-solve problems. 

2. It's nearly impossible to predict all edge cases, which can disrupt the entire application development flow. 

Having multiple engines (instances) of Flutter in more advanced integration scenarios increases the complexity. The risks are reduced when we use the more straightforward, isolated approach of adding Flutter or integrating with smaller native apps. Still, some help and engagement of native developers must be considered while planning the work.

3. You also need to keep in mind that Add-to-App as a long-term solution can decrease the efficiency of your app. 

In most cases, you would need to maintain two UI component sets and a bridging layer, with developer experience decreased due to context switching and longer build times.

4. When you have decided on Add-to-App, we recommend isolating the Flutter module as much as possible rather than a complex integration process.

Because it requires a lot of extra work and introduces issues that don't exist in pure Flutter apps. Some complex technical topics can be researched on the side without opening the native app, avoiding the time spent integrating with native components. Remember that the development effort can vary depending on code quality and the technical debt of native applications. 

Navigation is where the native and Flutter definitely need to cross. Flutter Add-to-App, by definition, means native and Flutter screens work seamlessly together. In our case, though, things appeared more complex.

First, we discovered that app bars in the native iOS app used a custom animation for screen transitions. It quickly turned out that it didn’t play well with Flutter. While it seemed technically possible to handle such transitions gracefully, it would require considerable work to ensure the animations worked well together.

Due to all those issues, it turned out that it might be faster to rewrite more screens in Flutter (especially as the UI was not that complex) than to look for a workaround to keep those screens native. And in fact, we did just that - a few screens that weren’t initially meant to be rewritten in Flutter were migrated to Flutter. That way, we could keep the whole process working in Flutter and avoid extra native communication. The entire business logic stayed multiplatform in Dart.

We also noticed that the native iOS app included generic view controllers that didn’t integrate well with Flutter screens. We had to develop a custom wrapper for that need, which wasn't a lot of extra work but required some native knowledge and could be tricky for many Flutter devs.

If your app requires transitions between Flutter and native screens, each such path must be handled. A bridge must be created for every such transition (in Dart, Kotlin, and Swift). You can make the bridges pretty generic, but that still adds some development overhead that wouldn’t be present in a pure Flutter app.

Managing Flutter screens, dialogs, and engine lifecycles

Navigation was not the only challenge. The business process we were developing was intrinsically asynchronous. It involved checking data in the background and displaying dialogs whenever the state of the process changed.

As a result, we needed a way to display Flutter dialogs from anywhere in the application — both over native screens and other Flutter screens. This required maintaining multiple Flutter engines and carefully managing their lifetimes.

This kind of engine management is specific to more complex Add-to-App integrations. The more Flutter contexts an application needs to maintain, the more attention developers have to pay to engine initialization, lifecycle, state, and communication between the Flutter and native parts of the application.

Networking between the native and Flutter app

Almost every mobile app communicates with backend services of some sort. Unless the Flutter module is tightly isolated (e.g., it depends on third-party APIs not used by the native app), another communication point arises between the native app and Flutter. 

When rewriting an existing native module in Flutter, two approaches can be taken:

  1. The first is implementing networking (headers, serialization, etc.) in Flutter with the thinnest possible bridge to handle authentication. Usually, the authentication flow will have already been implemented natively before adding a new framework to the app. Some edge cases with refreshing tokens and keeping requests in the queue to preserve order can be tricky to handle.
  2. The other is to reuse the existing native networking, keep all endpoint definitions over there, and expose a full-on client to Flutter. The latter can make sense if the protocol is complex and heavily customized, so reimplementing it in Dart for a PoC is unnecessarily expensive.

Issues with debugging in case of multiple engines

One of the reasons Flutter became so popular and is considered a mobile framework of choice for many developers worldwide is the developer experience it provides.

Once you’re only using one Flutter engine in your app (i.e., the Flutter part is heavily isolated), the debugging experience is the same as with pure Flutter apps. But for multiple engines, we have found issues with debugging multiple Flutter contexts. The debugger wouldn’t attach to some engines, and hot reload wouldn't always work.

With a pure Flutter app, thanks to the myriad libraries available and Flutter's rendering approach, one rarely needs to develop and compile native Android and iOS code (which can take quite a lot of time). 

Hot reload and hot refresh are very fast, and the developer can immediately see all changes in the code. With native recompilation, all screen state is lost, and a developer needs to click through all screens to retest a feature. Developers lose time recompiling native parts and often need to debug across many IDEs, which hurts productivity. 

Screen lifecycle differences

Another interesting thing is that due to the lifecycle of Flutter engines in Add-to-App, the screen state gets cached. So if you’re, for example, going from a native screen to a Flutter screen, do something on it, then go back to the native screen, and then again to the Flutter screen, it will look exactly the same once you have left it.

This is often an undesired behavior. The difference in the screen lifecycle can be pretty confusing to developers. They would expect the screen code to run from square one, but it doesn’t. To properly handle that case without destroying the engine, special native bridges are necessary.

Native libraries in Flutter Add-to-App

While the UI is pure, reusable Flutter, almost every app needs some sort of native integrations (camera, push notifications, storage, geolocation, etc.). Those need Flutter libraries containing native code. Adding such plugins is a very straightforward process in a pure Flutter app (usually, adding it is limited to changes only in the Dart code).

With Add-to-App, some native libraries may require extra configuration. It is often undocumented, as those libraries are not designed and tested with Add-to-App in mind.

The native app we worked on did not use CocoaPods on iOS (a package manager for which Flutter Add-to-App has built-in integrations). As a result, we were forced to manually embed all native frameworks in the native app. That meant building frameworks, opening Xcode, and manually adding them for each native library, an action that is easy to forget and tedious for developers.

On Android, we were forced to use Flutter fragments instead of activities because of the existing native app architecture (activities and fragments are two distinct UI components on Android). While it is not a problem in itself (Flutter supports fragments), we later realized that some native libraries we had installed did not work as expected. It turned out that some extra configuration was needed, and the documentation was written only with activities in mind.

Background services and Flutter Add-to-App

Implementing some background services in the mobile app was necessary as part of the process. We needed to check for status changes periodically, and the logic had to continue executing even when the entire application was closed.

We could simply execute the existing native background services, but our goal was different - the business logic had to be a platform-independent module in Dart. We wanted to prove a hypothesis that we could achieve high code reusability even in more complex cases.

Obviously, for all of that to work, we still had to communicate with native code. The background process had to be a separate Dart isolate (a concept similar to a thread), which meant it needed to be run in a separate Flutter engine. The engines can communicate with each other (in pure Dart), but they consume more resources.

The Flutter team introduced a new concept called engine groups, which brings huge optimizations, but it was still unstable at the time we were using it. The infrastructural logic and turning different engines on and off must remain native.

We implemented a bridge for talking with iOS and Android using Pigeon - a handy but not that well-known tool yet for generating type-safe contracts for Dart, Android, and iOS bridges. It should be noted that code for managing engine lifecycles can be tricky, especially for developers inexperienced with native development. For example, a lack of a deeper understanding of the reference-counting mechanism in Swift can cause unexpected crashes.

You can find an article about implementing background services in Flutter add-to-app here.

App size when using Flutter Add-to-App

Because Flutter has its own rendering engine and runtime, it will affect the app's size. The increase is not dramatic, but if app size is very important for your app, it should be kept in mind. Remember to measure size only on release builds. Debug build sizes are definitely not representative, as they contain a lot of tooling that only developers need.

The Flutter module we developed consisted of a few Flutter screens, some assets, and a limited number of pub libraries. The size increase for our app was 27 MB for iOS and 48 MB for Android. However, the Android app was still being deployed to the Play Store in a deprecated APK format. Uploading in that format is no longer possible for new apps. It is now required to use the superior AAB (Android App Bundle) format.

The problem with APKs is that they must include binaries for multiple architectures. For AABs, the app the end user downloads from the Play Store is optimized for their device. With AABs, the effective increase would be 3-4 times smaller. Changing the deployment format and thus optimizing the app's size with Flutter can require extra organizational effort.

If your app contains assets that are also to be used by Flutter, they can be shared between native code on iOS, but unfortunately, at the time of writing, this feature is not yet available on Android. It means those assets will need to be included twice in the final app bundle, and they will increase the overall app size.

Flutter engines and performance overhead

When you’re adding Flutter runtime over a running native app, it is impossible not to add some extra performance overhead to the existing iOS and Android app. Flutter needs its own execution environment, and in more complex scenarios, multiple environments.

The most expensive operation is starting the Flutter engine (pre-warming). All assets and the Flutter library must be loaded, a Dart virtual machine must be started, and the entry point code must be executed. Depending on the use case, this can be done at app startup, after the app displays some initial data to the user, or only when the user opens the Flutter screen.

It all depends on how much extra time you’re willing to let the user wait. While this can mostly happen in the background, Flutter still needs to block the main thread for up to a few hundred milliseconds during initialization.

With the engine being prewarmed, rendering the first Flutter UI frame also has some latency. In our testing, it was around 100ms slower than rendering the native screen. This was completely acceptable in our case and was hard for the user to notice. Times for subsequent renders were comparable to those for native views.

When you open a Flutter screen and then go back to a native screen, the Flutter engine by default keeps running. The UI state is cached to make subsequent screen openings faster. This keeps eating the CPU, and you need to keep an eye on RAM.

When your Flutter process is not isolated, there’s a high chance you will need multiple Flutter engines. Every Flutter engine needs resources to run, so the performance overhead increases. However, the Flutter team has recently introduced a new concept called FlutterEngineGroup. It makes it possible to share resources across multiple engines, keeping the overhead of running another engine minimal. This means significant improvements for more complex Add-to-App scenarios.

Continuous integration in Flutter and native apps

For native apps, you most likely have some Continuous Integration system set up so that new iOS or Android app versions are automatically published for testers. The build process can also be integrated with the Google Play Store and App Store so that deployments take minimal effort.

While Flutter builds to native platform code (APKs and IPAs), some changes to the CI process are required. There are two basic approaches. Firstly, you can install the Flutter pipeline on build machines - it’s the most straightforward way. Adding a few extra steps (Flutter build, tests, etc.) will complete the Flutter Add-to-App integration.

If that’s a problem in your organization, e.g., due to security concerns (as it happened in our case), there is a way around it in which you can build the Flutter part into native AARs (for Android) and xcframeworks (for iOS). Then, theoretically, it’s possible to commit those built artifacts to the repository and keep the existing CI process untouched.

It will, of course, lack testing and static analysis of the Dart code, but it will suffice for publishing the app with Flutter for some quick testing. There are still problems with Android, which requires network access to some Flutter Maven repositories. We quickly found out that those were blocked in the corporate network.

What's improved since our first Add-to-App project?

This is where the 2026 picture becomes considerably more positive. Some of the problems we experienced in the first project were not permanent limitations of Add-to-App. Flutter itself has continued evolving.

Three developments are particularly relevant to the kinds of integrations we encountered.

Hybrid composition improvements

Embedding native views such as maps and WebViews inside Flutter historically introduced performance and synchronization challenges, particularly on Android.

Newer Android compositing work improves this situation and addresses some of the pain associated with embedding native surfaces. This matters because hybrid applications frequently cannot move every platform-specific component to Flutter immediately.

That said, our architectural recommendation remains the same: just because embedding is possible doesn't mean it should become the foundation of your migration strategy.

FlutterEngineGroup

This is one of the most important improvements for more sophisticated Add-to-App architectures. In our earlier work, multiple Flutter engines came with significant overhead.

FlutterEngineGroup allows multiple Flutter engine instances to share resources, substantially reducing the incremental cost of additional engines. That doesn't make lifecycle management disappear, but it changes the performance characteristics of architectures that require more than one Flutter context.

What still hurts in Flutter Add-to-App?

The tooling is better, but Add-to-App still isn't equivalent to building a pure Flutter application. Several gaps remain particularly noticeable.

Developer-experience gaps

Hot reload can still be less reliable than in a pure Flutter application. Developers may need to work across Flutter tooling, Android Studio, and Xcode. Native/Flutter boundaries still require contracts and glue code.

The development experience is therefore inherently more fragmented. Add-to-App is at its best when Flutter modules are large enough and isolated enough that developers can spend most of their time on one side of the boundary.

AI tooling

There is also a newer developer-experience consideration: AI-assisted development.

LLMs generally handle conventional Flutter applications reasonably well. Add-to-App is more difficult because the solution often spans Dart, Swift, Kotlin, engine lifecycle management, platform channels, native project configuration, and application-specific glue code.

There is also less Add-to-App material available than material about conventional Flutter development. For teams increasingly relying on AI-assisted development workflows, this is worth keeping in mind.

Continuous integration with Flutter and native applications

Large native applications usually already have established CI/CD processes. Adding Flutter means those pipelines need to accommodate another toolchain. The straightforward approach is to install Flutter on the build machines and add Flutter build and test steps to the existing process.

In organizations where this isn't possible — for example, because of security restrictions — Flutter can instead be built into native artifacts that are consumed by the existing native build.

That approach can minimize changes to the native pipeline but comes with trade-offs, including additional build complexity and potentially separating Dart testing and static analysis from the main application build.

In our earlier banking project, corporate network restrictions also affected access to external repositories. These problems are not specific to Flutter. They are typical of introducing a new technology into a large organization.

But Add-to-App exposes them particularly quickly because two technology stacks need to coexist.

Add Flutter to an existing app vs. organizational problems

Introducing Flutter in a large organization and introducing it as part of an existing native application brings a lot of challenges at the management level. Large corporations often use proxies, artifact management systems for libraries, and custom certificates. The network traffic can be heavily restricted.

Flutter development may therefore require close cooperation with other teams to resolve those issues, enabling developers to set up their development environment on their machines. This process can take some time, and one has to remember that it can completely block development and waste resources.

When the Add-to-App idea is to rewrite an existing part of the iOS and Android app in Flutter, you have to consider whether the whole business process has up-to-date documentation. For long-running mobile apps, documentation is often outdated, and developers will ultimately need to analyze the native code to understand how the app works. This is much slower than working with well-defined requirements, especially when code quality is poor.

Introducing Flutter can also bring some tension with the native developers currently working on the application. They will need to install and set up Flutter locally to continue working locally. Suppose that brings problems with the environment, workstations, or security. In that case, Flutter devs can deliver pre-built native artifacts (AARs and xcframeworks) so that native devs don’t need any additional tooling for the Flutter project. The approach is suboptimal due to the extra manual work required from the Flutter team.

If you want to improve your work, check out our list of non-obvious Flutter and Dart plugins and packages.

Add-to-App today: a white-label banking application

Several years after our first banking PoC, we found ourselves working with Add-to-App again. This time, the context was different. The project is a white-label banking application developed by SoftNet and used by more than 90 cooperative banks.

There is one underlying banking application, but it is re-skinned and configured for individual banks. That creates an interesting architectural constraint: Every architectural decision is multiplied across more than 90 deployments.

A workaround that seems acceptable for one application can become painful when it needs to work reliably across dozens of bank configurations. That makes migration strategy particularly important. Instead of asking, "Which screen would make the biggest impact if we rewrote it in Flutter?", we need to ask: "Where can Flutter be introduced with the cleanest boundaries?" That distinction changes everything.

Our rollout strategy

Our current approach can be summarized in three stages.

1. Start with a separate module

The first Flutter module serves a different customer type and is entered from sign-in. That makes it relatively self-contained. It provides a useful first production step without immediately forcing Flutter deep into the most interconnected areas of the native application.

This is the kind of starting point we now prefer: an isolated module with clearly defined dependencies.

2. Rewrite native screens strategically

Once Flutter is established in the application, native screens can be migrated according to a prioritized backlog. But priority shouldn't be determined only by business visibility.

Dependencies matter. For example, currency exchange is a useful early candidate because it behaves more like a low-dependency leaf in the application's architecture. Migrating leaves first allows Flutter territory to grow without repeatedly crossing the native/Flutter boundary.

3. Flip the shell

Eventually, there is a point where enough of the application has moved to Flutter that the architecture should change. Instead of a native application containing isolated Flutter screens, Flutter becomes the application's primary shell.

Home, navigation, and the main flows move to Flutter. Selected screens that still need to remain native then become the leaves. This is an important part of migration planning that is easy to overlook.

If Add-to-App is a migration strategy, you should know what you're migrating toward.

Don't only decide how Flutter enters the application. Decide when Flutter takes over.

Is add-to-app the right migration path?
Migration to Flutter ebook mockup

When Add-to-App doesn't work: starting with the Home screen

Our experience also gives us a useful counterexample. In another project, the business wanted the migration to make the biggest possible impact early. The obvious candidate seemed to be the Home screen.

So the Home screen was rewritten first.

From a business perspective, the idea made sense. Home is highly visible and immediately demonstrates the new technology. Architecturally, however, it was a poor starting point. The Home screen touched everything.

Home was connected to a large number of features and dependencies. Instead of creating an isolated Flutter module, rewriting it meant immediately dealing with a tangle of native integrations.

Some components had to remain native. Certain functionality relied on proprietary native SDKs. Those components could not simply be rewritten in Flutter. The result was a Flutter Home screen containing native elements alongside Flutter elements.

More embedding created more work. That forced additional embedding infrastructure — precisely the type of complexity that a gradual migration should try to avoid. Instead of reducing the native/Flutter boundary, the first migration step placed that boundary in one of the most interconnected parts of the application.

The lesson is important: Don't necessarily start with the screen that has the greatest business impact. Start with the module that has the cleanest architectural boundaries.

How to add Flutter to an existing app: three approaches

Understandably, if you want to rewrite your entire application in Flutter right away, it may feel like too big a change. With Flutter, though, you can start step by step. You can add Flutter to existing native apps, either as a single screen or as part of an existing screen.

Adding Flutter this way can be a small proof of concept to introduce the new technology in your organization. You can also implement the same feature natively and in Flutter to compare performance and user engagement with A/B tests.

We can take several approaches when considering how to include Flutter in a native app:

  • Self-contained Flutter module - when we have a business module that is ideally kept in one place in the app (e.g., one tab of the application). We can implement it in Flutter and restrict native integrations to a minimum.
  • Hybrid navigation - when the business feature we want to implement in Flutter crosses paths with some existing native screens (e.g., we can go from a Flutter screen to a native screen and then to another Flutter screen). This approach brings some additional technical challenges.
  • Mixing native and Flutter views is technically possible: some parts of native screens can be rendered with Flutter (and vice versa). This is the most complex and often suboptimal way of using the Flutter Add-to-App.

You can find a handy tutorial on adding Flutter to an existing application on the official Flutter page.

A strategy for Flutter Add-to-App

After working with Add-to-App both when the tooling was young and in current production applications, we can reduce our recommendations to four principles.

1. Have a migration strategy

Don't treat Add-to-App as a mechanism for randomly introducing Flutter screens into an existing application. Decide what should be rewritten and in what order. Start with isolated, self-contained modules wherever possible. Think in terms of dependencies rather than just screen visibility and minimize embedding.

2. Audit the native code first

Before estimating the migration, verify what you're actually migrating. Are the requirements documented? Are designs current? Where does the business logic live? Which proprietary SDKs are involved? Which native screens depend on one another? How much technical debt exists?

A migration estimate based on documentation can be dramatically wrong if the real behavior exists only in years-old Swift or Kotlin code. The condition of the native application directly affects the difficulty of Add-to-App.

3. Train the existing team

Don't leave the native developers behind. They know the application you're migrating: its architecture, business rules, edge cases, production history, and platform-specific behavior. Bring them into the Flutter journey.

This also reduces the risk of creating two isolated teams — one maintaining the "old native application" and another building the "new Flutter application" — with neither fully understanding the complete product.

4. Plan the full switch

If the ultimate goal is migration to Flutter, define the point at which the architecture should flip.

Initially:

Native shell → Flutter modules

Later:

Flutter shell → remaining native leaves

Knowing that destination influences which screens should be migrated first and prevents the application from becoming a permanent patchwork of Flutter and native components.

Conclusions on Flutter Add-to-App

Do we recommend Flutter Add-to-App in 2026? Yes - but only with a strategy.

Our view of Add-to-App has changed over the years. During our first banking project, the tooling was considerably rougher. We dealt with multi-engine complexity, native integration problems, debugging limitations, background execution challenges, and significant custom infrastructure.

And yet Add-to-App achieved its purpose.

It proved Flutter could handle the application and built enough confidence to proceed with a Flutter rewrite. Today, the tooling has matured considerably. Engine groups and improvements to Flutter/native integration make once-painful scenarios more practical. We now ship Add-to-App in production.

At the same time, some difficulties remain. Developer experience is still less seamless than in a pure Flutter application. Native integrations still require careful handling. Package management and platform-specific details can create unexpected work. And every native/Flutter boundary adds complexity.

That's why the biggest lesson isn't about a particular Flutter API. It's about migration architecture.

Start with isolated, low-dependency modules rather than simply choosing the most visible screens. Audit the native application before estimating the work. Bring the existing native developers into the migration. Minimize embedding. And decide early what the final architecture should look like.

Add-to-App works best when you don't treat it as the destination. It is a way to move safely from one architecture to another while continuing to ship a real application to real users.

The tooling is ready. The question is whether your migration strategy is.

Consider ordering a current mobile app audit to get the full picture before starting with the Flutter Add-to-App.

LeanCode's experts will advise whether this Flutter feature can help your case or whether it's better to rewrite your entire application. The best way is to reach out via our form.

Rate this article
Star 1Star 2Star 3Star 4Star 5
5.00 / 5 Based on 22 reviews

You can also like

Background services in Flutter add-to-app

Background Services in Flutter Add-to-App Case

As part of a PoC for a client from the banking sector, we had to implement a business process that required some work to be performed in the background in a Flutter module. See our case and code examples of implementing background services in Flutter add-to-app.

Article by LeanCode with insights on mobile audits

Top Findings After Performing 5 Enterprise Mobile App Audits

After auditing enterprise-grade Flutter apps, one thing became clear: the same issues keep coming back. In this article, we bring closer the most common lessons from real-world mobile app audits and show what enterprises should focus on to build safer, more reliable, and future-proof Flutter applications.

3 app migration strategies

Proven App Migration Strategies: 3 Best Ways to Move to Flutter

Explore proven app migration strategies to Flutter and learn how to choose the right approach for your product. From minimizing risk to maximizing efficiency, this guide breaks down key paths, trade-offs, and decisions behind a successful transition.

Flutter in banking

Banking Apps With Flutter? The Overview and Opinions

The number of banks that have opted for Flutter is growing. Specialists from three banks interviewed - Nubank, ING Silesian Bank, and Credit Agricole Bank Polska - rated Flutter as a 9 (out of 10 point scale). Find out if Flutter really is the right solution for building banking apps.