Make an android app with Kotlin, Flutter and React Native.

Italo "Lin" Lino
The Startup
Published in
9 min readFeb 14, 2020
Photo by Pavan Trikutam on Unsplash

What is the point of creating an app with these powerful technologies? Even more power, the power of freedom, not limited by one framework.

However, this is not for all cases of a product. The proposal of an app with these 3 technologies makes multiple teams of product work async with their choice framework. A good way to understand this is seeing this video by Facebook:

This allows different product teams when developing a single App to have the freedom to individually choose the technology they will work with. For example, one team may choose to React Native as their development technology and another team may choose Flutter, according to the familiarity and preferences of each team.

There are 2 reasons why you suddenly bounce into this article:

  • You are curious, just like me 🤓.
  • You want to make a HUGE APP that holds this kind of tech stack 👊.

Congratulations, this article will help you to understand how the native side deals with Flutter and React Native with a small POC. In future articles, I will explore best practices and module sharing.

Prerequisites

First, the basics of native development are important here so if you never touched the native side, I'm dropping a link with a Udacity course right below. Second, if you never worked with React Native, Flutter or both, please, follow these getting started articles, they help to set the correct environment to make your machine work nice with the framework.

Udacity Course

React Native

Flutter

Assuming that you already get all work and clean, tested React Native and Flutter we are ready to go.

Foundation

First, we need our core an Android project, open Android Studio and create a new project.

Select the empty activity template, so we already start with one activity, layout and manifest.

Give your project a name.

Change the save location to app name, and put the project inside an android folder just like this:

Click finish, with the project created and the Gradle synched open activity_main.xml and add 2 buttons:

Nice, now our activity layout has 2 buttons to in future open our respective frameworks. Let’s prepare our MainActivity now, we just need to add the listeners for the buttons that you just added.

Flutter

With our core ready to receive both frameworks, we gonna start adding flutter! There 2 ways to add Flutter to our project:

  • Automatic with Flutter Plugin on Android Studio
  • Manual with Flutter CLI

Automatic

The automatic way only can be handled with the Android Studio 3.6 with is in beta for now, so if you want to follow this you can download the beta version here.

Android studio will handle all configuration, a big tip to see what is changing is to initialize source control for your Android project before performing any steps. A local diff shows the changes.

First, we need to set on the app build.gradle the abiFilters because Flutter currently only supports building ahead-of-time (AOT) compiled libraries for armeabi-v7a and arm64-v8a. If you wanna know what devices Flutter runs you can found it here.

You don’t need to emulate an ARM device, Flutter runs normally on debug because the engine has an x86 and x86_64 version. For release mode, I recommend you to use your phone to get the full experience.

Edit your appbuild.gradle and add these lines into android { }:

android {
//...
defaultConfig {
//...
ndk {
// Filter for architectures supported by Flutter.
abiFilters 'armeabi-v7a', 'arm64-v8a'
}
}
}

NDK is a toolset that lets you implement parts of your app in native code, using languages such as C and C++.

Now go to File > New > New Module and you should see a screen like this:

Roll down and you will see a Flutter Module:

Set a name for your Flutter module:

Set the package domain and finish:

Wait for Android Studio to build the new module and we are ready to configure the core to receive Flutter packages. I continue after Manual mode.

Manual

Enter on our existing Android app at your/path/SomeApp, and that you want our Flutter project as a sibling:

$ cd some/path/SomeApp
$ flutter create -t module --org com.example my_flutter

Flutter CLI will create a new module and your workspace folder should look like this:

Before attempting to connect your Flutter module project to your host Android app, ensure that your host Android app declares the following source compatibility within your app’s build.gradle file, under the android { } block:

android {
//...
compileOptions {
sourceCompatibility 1.8
targetCompatibility 1.8
}
}

The Flutter Android engine uses Java 8 features.

Now include the Flutter module as a subproject in the host app’s settings.gradle:

rootProject.name='SomeHugeApp'
include ':app' // assumed existing content
setBinding(new Binding([gradle: this]))
evaluate(new File(
settingsDir.parentFile,
'my_flutter/.android/include_flutter.groovy'
))

Assuming my_flutter is a sibling to SomeHugeApp.

The binding and script evaluation allows the Flutter module to include itself (as :flutter) and any Flutter plugins used by the module (as :package_info, :video_player, etc) in the evaluation context of your settings.gradle.

Let's add Flutter as a dependency at the app build.gradle:

dependecies {
//....
implementation project(':flutter')
}

Open a Flutter Screen!

Flutter provides FlutterActivity to display a Flutter experience within an Android app. Like any other Activity, FlutterActivity must be registered in your AndroidManifest.xml. Add the following XML to your AndroidManifest.xml file under your application tag, your AndroidManifest.xml should look like this:

Now let's call our FlutterActivity on the Listener that we created at the start of this article. Go back to MainActivity and just add the startActivity the method inside the Flutter button click listener:

The result:

It worked properly. Still have a big delay to open the Activity, there some reasons for it

  • Debug mode
  • Not cached engine

You can see what cached engine means here.

React Native

Assuming that you already read Getting Started guide to configure your development environment.

Following the code integration documentation from React Native:

To ensure a smooth experience, create a new folder for your integrated React Native project, then copy your existing Android project to an /android subfolder.

We already set our Android core project inside a android folder, I won't abord modularizing here in this article because I would lose focus, in a further article I gonna deep dive on modularization.

Installing JS dependencies

Make sure you have installed the yarn package manager.

Go to the root directory of your project and create a new package.json file with the following contents:

{
"name": "somehugeapp",
"version": "0.0.1",
"private": true,
"scripts": {
"start": "yarn react-native start"
}
}

In order to install React Native package, run this line on the terminal or command prompt on the project root:

$ yarn add react-native

This will print a message similar to the following (scroll up in the yarn output to see it):

warning “ > react-native@0.61.5” has unmet peer dependency “react@16.9.0”.

It means we also need to install React:

$ yarn add react@VERSION_ABOVE

You will notice a node_modules folder, this folder contains some Android ( Gradle files ) dependencies to make React Native work inside our already build core project.

Now we gonna add a reference on our Android project to these dependencies inside node_modules. Add the React Native dependency to your app’s build.gradle file:

dependencies {
//...
implementation "com.facebook.react:react-native:+"
}

Add an entry for the local React Native maven directory to build.gradle. Be sure to add it to the allprojects block, above other maven repositories:

allprojects {
repositories {
google()
jcenter()
maven {
url "$rootDir/../node_modules/react-native/android"
}
}
}

This exposes all android public files that React Native has to offer into our project.

If you need to access the DevSettingsActivity add to your AndroidManifest:

<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />

This is only used in dev mode when reloading JavaScript from the development server, so you can strip this in release builds if you need to.

Code integration

First, create an empty index.js file in the root of the project and put this code in it:

Second, configure permissions for the development error overlay:

If your app is targeting the Android API level 23 or greater, make sure you have the permission android.permission.SYSTEM_ALERT_WINDOW enabled for the development build. You can check this with Settings.canDrawOverlays(this);. This is required in dev builds because React Native development errors must be displayed above all the other windows. Due to the new permissions system introduced in the API level 23 (Android M), the user needs to approve it. This can be achieved by adding the following code to your Activity's in onCreate() method.

We need to add the android.permission.INTERNET and android.permission.SYSTEM_ALERT_WINDOWto the manifest:

<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>

Third, copy the code below into your MainActivity, it will configure the permission checking of system alert window:

The React Activity

Create a Kotlin class in the android project called ReactActivity and add this to it:

The ReactInstanceManager will handle all configuration of the JS side, ReactRootView starts a React application inside it and sets it as the main content view.

As all activities in an Android project, add it to manifest:

<activity android:name=".ReactActivity"   android:label="@string/app_name"   android:theme="@style/Theme.AppCompat.Light.NoActionBar"> </activity>

Add a startActivity method on the button listener located atMainActivity , the final version should look like this:

Enable Hermes

Hermes is a JavaScript engine optimized for fast start-up of RN apps on Android. It features ahead-of-time static optimization and compact bytecode.

Also, we gonna define an NDK for each buildType in order to make React Native apps run in emulators since flutter accepts this kind of ABI in debug mode.

Your app build.gradle should look like this in order to enable Hermes and buildTypes:

Now run in your terminal in android folder:

./gradlew clean

Test our integration

Now, run this line on the terminal or command prompt on the project root:

IMPORTANT NOTE FOR RUNNING IN RELEASE MODE: Since flutter only supports ARM arch on release mode, you should ensure that you emulator supports it, consider to run the project with your own device for test release versions :D

yarn start

Build the app on the android studio and wait for it appears.

When the app runs you will see this screen:

This occurs because React has a debug screen that is displayed over the current app (In release mode this not occurs!)

IT WORKS!!!

This is just the start, but we’ve got the beginnings of the app set up and we’re ready to really start adding functionality (Navigation and Modularization). Stay tuned and we’ll keep publishing updates & adding walkthroughs. Again, hit me up if you have any suggestions or thoughts. We’d love to get some feedback!

Git repo with some navigation and multiple bundle examples:
https://github.com/linzera/brownfield

--

--

Italo "Lin" Lino
The Startup

Software Engineer at @jusbrasil | React, React Native, Swift & Kotlin ❤