How to Integrate Flutter module (AAR) into Your Android App

Sanket Kumbhare
4 min readMar 28, 2024

--

In this tutorial, we’ll demonstrate how to seamlessly integrate screens designed in Flutter into your existing Android app. By following these steps, you can leverage the power of Flutter’s UI toolkit within your Android project.

Step 1: Generate Android Archive from the Flutter Module

Before generating the Android Archive (AAR), ensure you’ve defined an entry point function in your Flutter module’s main.dart file. This function will be invoked by the native Android app.

@pragma("vm:entry-point")
void loadFromNative() async {
WidgetsFlutterBinding.ensureInitialized();
runApp(const MyApp());
}

To generate the AAR, navigate to your Flutter module directory and run:

flutter build aar

Step 2: Configuring and Integrating Android Archive (AAR) in Android Project

Create a new Android application with Kotlin DSL build configuration.

Create a lib folder in your Android project directory. After that copy and paste your AAR directory inside it.

Note: Please make sure the folder structure of the AAR library should be lib/build/host/.. for the host app to find these files.

Configure repositories in your app’s build.gradle to locate the AAR files.

Ensure you have the repositories configured for the host app to find the AAR files and add the following code to app level build.gradle (<host>/app/build.gradle)

repositories{
val storageUrl = System.getenv("FLUTTER_STORAGE_BASE_URL") ?: "https://storage.googleapis.com"
repositories {
google()
mavenCentral()
maven {
setUrl("../lib/build/host/outputs/repo")
}
maven {
setUrl("$storageUrl/download.flutter.io")
}
}
}


dependencies {
debugImplementation ("com.example.home_derma:flutter_debug:1.0")
profileImplementation ("com.example.home_derma:flutter_profile:1.0")
releaseImplementation ("com.example.home_derma:flutter_release:1.0")
}

Update the settings.gradle.kts file dependencyResolutionManagement to fix the compile issue.

dependencyResolutionManagement {
repositories {
google()
mavenCentral()
maven { url = uri("jitpack.io") }
}
}

If all the steps for generating the AAR and configuring the gradle and settings files are correct, you should be able to build the project successfully.

Step 3: Open the Flutter View

In your MainActivity class, create an instance of the Flutter Engine to initiate Flutter screens.

package com.example.fluttermodulenativeapp
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.embedding.engine.FlutterEngineCache
import io.flutter.embedding.engine.dart.DartExecutor


const val FLUTTER_ENGINE_NAME = "flutter_engine"


class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val flutterEngine = FlutterEngine(this)
flutterEngine.dartExecutor.executeDartEntrypoint(
DartExecutor.DartEntrypoint.createDefault()
)
FlutterEngineCache.getInstance().put(FLUTTER_ENGINE_NAME, flutterEngine)


...
}
}


val flutterEngine = FlutterEngine(this)

This line creates a new instance of a Flutter Engine named flutterEngine. A Flutter Engine is responsible for running Flutter code within an Android application.

flutterEngine.dartExecutor.executeDartEntrypoint(DartExecutor.DartEntrypoint.createDefault())

This line of code sets up the Flutter Engine to execute the default Dart entry point. It starts running the Flutter code within the engine effectively.

FlutterEngineCache.getInstance().put(FLUTTER_ENGINE_NAME, flutterEngine)

This line stores the flutterEngine instance in the FlutterEngineCache with the name defined by FLUTTER_ENGINE_NAME. It is used for caching and managing Flutter engines within the application.

Add the below-mentioned activity tag in your AndroidManifest.xml file:

<activity
android:name="io.flutter.embedding.android.FlutterActivity"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize"
/>

The <activity> element we have provided is configuring an Android activity named FlutterActivity from the Flutter framework. It specifies how this activity should handle certain configuration changes, enables hardware acceleration for improved graphics performance, and sets the soft input mode to adjust the window size when the soft keyboard is displayed. These settings are commonly employed when integrating Flutter-based UI components into an Android app.

Update your MainActivity class and add a button to open the Flutter module when it’s clicked.

const val FLUTTER_ENGINE_NAME = "flutter_engine"


class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val flutterEngine = FlutterEngine(this)
flutterEngine.dartExecutor.executeDartEntrypoint(
DartExecutor.DartEntrypoint.createDefault()
)
FlutterEngineCache.getInstance().put(FLUTTER_ENGINE_NAME, flutterEngine)
setContent {
// A surface container using the 'background' color from the theme
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
LaunchFlutterModuleButton()
}
}
}
}
@Composable
fun LaunchFlutterModuleButton() {
val context = LocalContext.current
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
ElevatedButton(onClick = {
context.startActivity(
FlutterActivity.withCachedEngine(FLUTTER_ENGINE_NAME).build(context))
}) {
Text(text = "Flutter Activity")
}
}
}

Note: Please ensure that you modify the compileSdk version to 34 in the app level build.gradle file.

When the ElevatedButton is clicked, it triggers the creation and launch of a FlutterActivity using a cached Flutter engine named FLUTTER_ENGINE_NAME created within our MainActivity class. This allows the app to run Flutter code within the context of this activity.

We have now successfully integrated the Flutter screens into your Native Android application.

--

--