Use Flutter in existing Android Apps

Jan Reehuis
grandcentrix
Published in
6 min readAug 22, 2018
Photo by Stephen Frank on Unsplash

You might know the pain: An App should get a new Feature which is UI-wise heavily demanding, but Androids build-in capabilities are limited or rather complex to use.

So what can be done here? Invest a lot of time building the UI and possibly spend way more time later to maintain the implementation?

Quickly it comes to mind if it would be possible to use Flutter as rescue. It has sophisticated powers especially when it comes to UI. A really nice addition would be that an iOS App also could use that UI component without having to write it twice.

Disclaimer: I did not have a look on the iOS part, but as Flutter also compiles for iOS it should be possible without much effort.

For my journey I want to use the PageTransformer Flutter Example and integrate it as a library into a native Android App. For more easy reading I will name the App CustomerApp and the Example PageTransformerLib from now on.

My goal is to showcase:

  • Starting the PageTransformerLib as an Activity in the CustomerApp
  • Display the PageTransformerLib as a View embedded into a native Android Layout in the CustomerApp
  • Modify the PageTransformerLib so we can embed it into a native Android Layout in the CustomerAppand replace the shown Items

Project Setup

Most of the Apps I develop at work are already some days old. Their repositories have their own structure and can not be changed without much effort and risk. Beside that we usually have distinct GIT repositories for Android and iOS.

This plays against the Flutter tooling which requires its very own repository structure:

- android
- ios
- lib
- test

so we have to split the CustomerApp from the PageTransformerLib to satisfy both needs. As a starting point I used Add-Flutter-to-existing-Apps as guideline to migrate the default Android Flutter App template to an Android Flutter Library project.

Since the guide was written, 731ed1b got already merged and is included in the current latest Flutter beta release. Nice, less manual work!

The PagerTransformerLib build.gradle were adjusted quickly to build a library without including all the properties which are only needed for an App. In addition to the guide I renamed the MainActivity to PageTransformerActivity to not mix it up later with the CustomerApp MainActivity.

So far the preparations of the PageTransformerLib. To keep the repositories separate I added the PageTransformerLib as Git submodule to the CustomerApp.

In future versions of Flutter you can create a library project directly by a Flutter module template. It is already included in Flutter’s master branch but not yet available in the Beta. So I have not used it while writing this post.
Visit https://github.com/flutter/flutter/wiki/Add-Flutter-to-existing-apps for more information.

Starting as an Activity

Including the PageTransformerLib in the CustomerApp is the typical, rather simple process.

First, we have to reference the PageTransformerLib Gradle Project as dependency in the settings.gradle of theCustomerApp:

include 'page-transformer'
project(":page-transformer").projectDir = file("page-transformer/android/app")

And add it as a normal dependency to the build.gradle in the CustomerApp:

implementation project(':page-transformer')

Now, it is a piece-of-cake to start the PageTransformerActivity anywhere from the CustomerApp by the usual

val intent = Intent(this, PageTransformerActivity::class.java)
startActivity(intent)

When deploying the CustomerApp to the emulator I ran into the installation error INSTALL_FAILED_NO_MATCHING_ABIS . Some poking around led the to flutter.gradle#137 which explicitly states that Flutter only includes its x86 native libraries into builds of type debug. I worked around the issue by changing flutter.gradle#206 and flutter.gradle#299 so that it include the x86 native libs for all builds.

It should not cause issues if your release build later on does not ship x86. All x86 devices I know are shipped with an arm -> x86 emulation.

Embed as View

Embedding a Flutter Widget/App as a View can be achieved by using FlutterView and starting the right Dart entry point on it. Also we have to initialize the Flutter Runtime at the right point.

As our CustomerApp has no direct dependency to the flutter.jar it is not possible to use FlutterView directly without breaking the separation to the PageTransformerLib. So I decided to wrap the FlutterView in a custom PageTransformerView.

The flutter_view Example sets an FlutterApplication in the Manifest which takes care to start the initialization of Flutter by calling FlutterMain.startInitialization(appContext). With the separation of the CustomerApp from the Flutter lib, again we can not use the FlutterApplication class directly. Beside that, the CustomerApp might have already an custom Application class set.

My solution was to simply wrap the call in a FlutterHelper object in the PageTransformerLib and call it from the CustomerApps onCreate() method. Such helper object becomes also handy as we also have to call FlutterMain.ensureInitializationComplete(appContext, args) before running some Flutter in the FlutterView. In my example it gets called in the Activity.onCreate() .

The mentioned args are arguments which might have to be forwarded to the Flutter runtime. You can find some examples in the initialization of the flutter_view_ Example.

After all, we can implement a method in the PageTransformerView which invokes the Flutter main() method in the FlutterView by

private val flutterView = findViewById<FlutterView>(R.id.flutterView)start() {
val bundlePath = FlutterMain.findAppBundlePath(appContext)
flutterView.runFromBundle(bundlePath, null);
}

Putting everything together:

  • Add our PageTransformerView to an Activity layout
  • Call FlutterHelper.ensureInit(appContext, args) in Activity.onCreate()
  • Call PageTransformerView.start() in the Activity

Caution!
FlutterView has to be bound to the Android Lifecycle so it can clean up some resources and manage some internal callbacks. Make sure you call FlutterView.onPostResume() and FlutterView.onPause() in the right Lifecycle methods.

Setting the Items

Flutter provides an Android <-> Flutter communication by sending messages through aMethodChannel or a MessageChannel . All messages send though these channels will pass the Java-Native barrier, so only primitive data types are supported. For our mission this means we have to serialize the items before sending them to Flutter.

Again, to leave the flutter.jar dependency encapsulated in the PageTransformerLib we implement the serialization in PageTransformerView.setItems() .

setItems(items: List<IntroItem>) {
val jsonList = items.map {
mapOf(
"title" to introItem.title,
"category" to introItem.category,
"imageUrl" to introItem.imageUrl
)
}

mItemChannel.send(jsonList);
}

We are using a Map<String, Any> here as the Flutter build-in JSON serializing does not support custom objects by default.

On the Flutter side we have to listen for those item messages and update the Flutter App State with the received items.

_channel.setMessageHandler((items) async {
var introItems = new List<IntroItem>();
for (var value in items) {
introItems.add(
new IntroItem(
title: value["title"],
category: value["category"],
imageUrl: value["imageUrl"])
);
}

setState(() {
items = introItems;
});
});

And we are already done with providing an option to update the items from the native Android App at the Flutter Widget.

Tl;Dr

Possible:

  • a whole Feature written in Flutter as new Activity
  • Including a Single Flutter Widget or Subfeature in Flutter as a View in an native Android App

Drawbacks:

  • Testing on Emulator is wonky up-to impossible without adjusting flutter.gradle or explicitly using debug mode
  • The Flutter Project configuration has to be manually setup/adjusted to compile to a Library instead of an App (will get fixed soon)
  • Communication Android <-> Flutter requires quite some code and ceremony
  • Starting the Flutter Runtime takes some time which you should hide from the user

Conclusion:
It is a nice option to write a new feature in Flutter and than embed it into the existing App. Especially the UI features like Animations and Material Design support are amazing. But be aware that you have only two options:

  • Decouple the new Feature from the Rest of the App so that it might run as a standalone App
  • Invest quite some time and coding effort to establish a connection between the Android and Flutter App part.

So every use case have to be evaluated if it is really worth the time and effort. With including Flutter you also got one more tooling you have to maintain and keep up-to-date. This should also be in mind that this will increase the App complexity and maintenance costs.

--

--