Add Flutter to your existing app

Developing mobile applications using cross-platform frameworks like Flutter has become very popular in recent years, with many large projects successfully leveraging its benefits. Starting a new app from scratch with Flutter is straightforward, but what if you already have a native app with complex business logic and UI, and now you want to switch entirely to Flutter, it might not be feasible all at once, right?

That’s why the Flutter team introduced an approach called “Add-to-App,” a.k.a Flutter Module. With this approach, you can gradually integrate new UI or non-UI logic into your existing native app as a module, and this module is rendered separately from the existing app using the Flutter engine.

Add-to-App is currently supported on Android, iOS, and the web, with a similar methodology across these platforms. In this article, I will focus on the iOS platform and demonstrate how it works.

1. Generate the Flutter module project.

Let’s create a Flutter module first.

flutter create --template module my_flutter

The structure of this project is quite similar to a non-module project. This is where you develop new features using the Flutter framework. You might notice the .ios/ subfolder, which is autogenerated and doesn’t affect your existing native source code. Its sole purpose is to allow you to run a standalone version of your module on iOS.

You might want to take a look at this repository, it can help you better understand how to use Method Channels to communicate with the native app, as well as how to define the entry points that allow the native app to interact with the modules.

2. Embed a Flutter module in your iOS app.

Once you have completed the module development process, you can embed it into the existing native app using the following methods:

  1. Use CocoaPods.
  2. Use iOS frameworks.
  3. Use CocoaPods and iOS frameworks.

The advantages of these methods are well explained in the documents, so I will point out the disadvantages from my own experience. After working with these methods for a few months, I noticed that embedding frameworks is quite complicated, increases the size of the source code, is time-consuming, and involves numerous file changes with every new release which is a huge mess with .xcodeproj. Additionally, unexpected errors occur occasionally and importantly - I don’t like working with these methods locally.

So, I came up with a fourth method that leverages the third one, but I will implement it remotely to address the disadvantages I mentioned earlier. My method is also beneficial if you want to use Flutter modules across multiple native apps from a single source. For example, if your company has multiple native apps managed by different teams and now wants to develop a new Chat/Video feature using Flutter Modules and then integrate it into all the native apps, you wouldn’t want to copy-paste the modules to each team, right? Technically, you could, but that would be a mid approach. Let’s stick with my new method and see how it resolves this issue.

Step 1:

flutter build ios-framework --output=archive/ --cocoapods [--no-profile] [--no-debug] [--no-release]

There are three build modes. You can use --no-profile and --no-debug to run the app on a real device and to prepare it for release. Alternatively, you can use --no-profile and --no-release to run the app on the simulator. If you use these options incorrectly, you might encounter a white freeze screen.

After a few seconds of running the command above, you will see new .frameworks and Flutter.podspec files generated inside the /archive folder. Move these frameworks (excluding Flutter.podspec) to your custom folder, like this:

You will use the Flutter.podspec file in the next step, so make sure not to remove it.

Step 2:

Now, commit these changes, push them to your remote repository, and create a Git tag for the commit. You can use this tag to specify the version of your module.

Step 3:

Move to the existing native source code and create a new folder named Flutter inside the root directory with the following structure:

Flutter.podspec is the file you generated in the previous step, so copy and paste it here.
Module.podspec is a new file that enables you to pull Flutter modules from the cloud into the native source code. Here are the details of this file.


Update your Podfile by adding these two lines, then run pod install to apply the changes

1
2
pod 'Flutter', :podspec => './Flutter/Flutter.podspec'
pod 'Module', :podspec => './Flutter/Module.podspec'

Pitfall: If you encounter any issues during the installation process, run pod deintegrate followed by pod install again.

That’s it! Now you can use your modules just as you would with external frameworks using CocoaPods. I believe this methodology will also work with other dependency managers like Carthage or Swift Package Manager or even Maven for Android. Would you like to give it a try?