A step-by-step walkthrough of how to correctly configure Gradle to use a local .AAR file in a Flutter plugin for Android, based on our direct experience.
At Tinisoft, we often push the boundaries of what’s possible with Flutter, which sometimes means integrating powerful, custom native Android libraries. If you’ve ever had a local .AAR
file—perhaps from a specialized SDK or your own in-house Android team—and tried to add it to a Flutter plugin, you’ve likely run into a wall of confusing Gradle errors.
The seemingly simple path of just adding the AAR as a dependency doesn’t work. This guide will walk you through the common pitfalls and provide the definitive, production-ready method to make it work, ensuring your builds are stable and predictable.
The Intuitive Approach (And Why It Fails) 🔗
Your Flutter plugin has an android
folder, and inside, a build.gradle
file. Your first instinct is to treat the AAR like any other dependency. You create a libs
folder, place your custom-native-library.aar
inside, and add the following line to your plugin’s build.gradle
:
// In my_plugin/android/build.gradle
dependencies {
// This seems right, but it's a trap!
implementation files('libs/custom-native-library.aar')
}
You run flutter run
in your example app, and the build immediately fails with an error that looks like this:
Direct local .aar file dependencies are not supported when building an AAR.
...The resulting AAR would be broken...
This error is the heart of the problem. Your Flutter plugin is itself packaged into an AAR file (my_plugin.aar
). Gradle is telling you that you cannot package an AAR file inside another AAR file. It doesn’t know how to merge the code, resources, and manifests, which would lead to a broken library.
The Correct Architecture: Separating Compilation from Packaging 🔗
The solution lies in understanding the difference between what your plugin needs to compile and what your final app needs to package.
-
The Plugin’s Role (
compileOnly
): Your plugin’s Kotlin/Java code only needs to know about the classes and methods inside the AAR to compile successfully. It does not need to bundle the entire AAR within itself. For this, we use thecompileOnly
configuration. -
The App’s Role (
implementation
): The final application (yourexample/
app or the app that eventually consumes your plugin) is responsible for packaging everything needed to run. It must bundle the Flutter engine, your plugin’s code, and your custom AAR into the final APK. For this, we use the standardimplementation
configuration.
With this separation of concerns, we can create a clean and robust build process.
Step-by-Step: The Production-Ready Solution 🔗
Let’s fix the build. Follow these two steps carefully.
Step 1: Configure Your Plugin’s build.gradle
🔗
First, we tell the plugin to only use the AAR for compiling.
- Place your
custom-native-library.aar
inside themy_plugin/android/libs/
directory. - Open your plugin’s
build.gradle
file atmy_plugin/android/build.gradle
. - Change the dependency from
implementation
tocompileOnly
.
// In my_plugin/android/build.gradle
dependencies {
// Correct: Use compileOnly for the plugin
compileOnly files('libs/custom-native-library.aar')
// ... other dependencies like kotlin-stdlib
}
Now, your plugin will compile correctly, but if you try to build the app, it will crash at runtime because the AAR’s code is still missing from the final APK. Let’s fix that.
Step 2: Configure Your App’s build.gradle
🔗
Next, we make the main application responsible for packaging the AAR. The most robust way to do this is to have the app copy the AAR from the plugin’s directory into its own libs
folder before the build begins. This avoids any tricky relative pathing issues with the Android build system.
- Open your example app’s
build.gradle
file, located atmy_plugin/example/android/app/build.gradle
. - Add the following
copy
task at the top of the file, right after theplugins
block. This task finds the AAR in your plugin and copies it locally.
// In my_plugin/example/android/app/build.gradle
apply plugin: 'com.android.application'
// ...
// Add this entire block
task copyLocalAar(type: Copy) {
from '../../../android/libs' // Path from the app to the plugin's libs folder
into 'libs'
include 'custom-native-library.aar'
}
preBuild.dependsOn copyLocalAar
- Finally, add the
implementation
dependency in the same file, pointing to the file that the task just copied.
// In my_plugin/example/android/app/build.gradle
dependencies {
// This line tells the app to package the AAR into the final APK
implementation files('libs/custom-native-library.aar')
// ... other dependencies
}
Now, run flutter clean
and flutter run
. Your application will build successfully.
Conclusion 🔗
Integrating local AARs in Flutter plugins requires a deeper understanding of how Gradle handles dependencies. By separating the plugin’s compile-time needs from the app’s packaging-time needs, we create a stable and maintainable build process.
The key takeaways are:
- Use
compileOnly
in the plugin to reference the AAR without bundling it. - Use a
preBuild
copy task and animplementation
dependency in the final app to package the AAR.
This method ensures your builds are reliable and frees you to focus on what matters: building amazing native-powered features for your Flutter apps.
Happy coding