Make an android app with Kotlin, Flutter and React Native.
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 yoursettings.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 permissionandroid.permission.SYSTEM_ALERT_WINDOW
enabled for the development build. You can check this withSettings.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 inonCreate()
method.
We need to add the android.permission.INTERNET
and android.permission.SYSTEM_ALERT_WINDOW
to 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