You have just upgraded your Flutter SDK or dependencies, attempted a build, and hit a wall of red text. The logs are screaming about Incompatible Gradle version, asking you to specify a namespace, or throwing Unsupported class file major version 61 (or 65).
In the transition to 2025, the Android ecosystem has aggressively deprecated legacy build configurations. Android Gradle Plugin (AGP) 9.0 and the enforcement of Java 17+ (and increasingly Java 21) are no longer optional warnings—they are breaking changes.
This post dissects why your build is failing and provides the exact code changes required to modernize your Flutter project’s Android layer.
The Root Cause: Bytecode and Separation of Concerns
Two primary architectural shifts are causing these failures:
- The Manifest/Gradle Split (AGP 8.0 -> 9.0): Historically, the
AndroidManifest.xmlhandled both the application ID (for the Play Store) and the package name (for R.java class generation). AGP now strictly decouples these. Thepackageattribute in the Manifest is deprecated for builds and ignored by AGP 9.0. You must define thenamespacein your Gradle build files for code generation, whileapplicationIdremains the unique identifier for the app store. - JDK Bytecode Mismatch: AGP 9.0 requires the Gradle Daemon to run on JDK 17 or 21. If your
JAVA_HOMEpoints to JDK 11 (common in older setups) or JDK 1.8, Gradle cannot read the class files generated by the Android compiler. Conversely, if your Gradle version is too old, it cannot run on JDK 21.
The Solution: A Four-Step Migration
We will upgrade the Gradle Wrapper, enforce the correct JVM toolchain, and migrate the Manifest configurations.
Step 1: Upgrade the Gradle Wrapper
AGP 9.0 requires Gradle 8.10 or newer. Open android/gradle/wrapper/gradle-wrapper.properties and update the distribution URL.
# android/gradle/wrapper/gradle-wrapper.properties
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
# Ensure you are using at least 8.10.2 (or the latest stable 9.x if available)
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Step 2: Update Project-Level Build Configuration
You must align your Kotlin Gradle Plugin and AGP versions. In 2025, we use the declarative plugins block inside settings.gradle (or the legacy root build.gradle if you haven't migrated to the new plugin management system).
Here is the modern configuration for android/settings.gradle:
// android/settings.gradle
pluginManagement {
def flutterSdkPath = {
def properties = new Properties()
file("local.properties").withInputStream { properties.load(it) }
def flutterSdkPath = properties.getProperty("flutter.sdk")
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
return flutterSdkPath
}
settings.ext.flutterSdkPath = flutterSdkPath()
includeBuild("${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle")
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
plugins {
// Match the AGP version to your requirements (9.0.0-alpha or stable 8.7+)
id "com.android.application" version "8.7.0" apply false
// Kotlin version must be compatible with AGP
id "org.jetbrains.kotlin.android" version "2.0.20" apply false
}
}
include ":app"
If your project still uses the legacy root build.gradle for classpath dependencies, ensure com.android.tools.build:gradle is updated to the corresponding version defined above.
Step 3: Implement Namespace and Enforce Java 17
This is the most critical step. We will modify android/app/build.gradle. We need to remove the reliance on the manifest package attribute and enforce the Java 17 toolchain.
// android/app/build.gradle
plugins {
id "com.android.application"
// The Flutter plugin loader handles the integration automatically
id "kotlin-android"
id "dev.flutter.flutter-gradle-plugin"
}
android {
// 1. DEFINING THE NAMESPACE (Critical for AGP 8+)
// This replaces the 'package' attribute in AndroidManifest.xml for R.java generation
namespace "com.example.your_project_name"
// 2. COMPILE SDK
// Must be 34 or 35 (Android 14/15) to use modern AGP
compileSdk 35
defaultConfig {
// This remains your unique ID on the Play Store
applicationId "com.example.your_project_name"
minSdk 23 // Flutter default minimum is rising
targetSdk 35
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
}
// 3. JAVA COMPATIBILITY
// Enforce Java 17 (or 21) for compilation
compileOptions {
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
}
// 4. KOTLIN OPTIONS
// Ensure Kotlin compiles to the same JVM target
kotlinOptions {
jvmTarget = "17"
}
buildTypes {
release {
signingConfig signingConfigs.debug
}
}
}
flutter {
source "../.."
}
Step 4: Clean the Manifest
Open android/app/src/main/AndroidManifest.xml. You must remove the package attribute from the <manifest> tag.
Before:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.your_project_name">
<!-- ... -->
</manifest>
After:
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- The 'package' attribute is GONE.
It is now handled by 'namespace' in build.gradle. -->
<application
android:label="your_project_name"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher">
<!-- ... -->
</application>
</manifest>
Why This Works
Decoupling Identity
By removing package from the Manifest and adding namespace to Gradle, you resolve the ambiguity that AGP 9.0 forbids. Now, applicationId strictly controls the app's identity on the device and store, while namespace strictly controls the package name for the generated R class and BuildConfig class.
JVM Toolchains
Setting sourceCompatibility and jvmTarget to 17 ensures that the bytecode generated by the Kotlin compiler is compatible with the class file format expected by the Android runtime and the Gradle daemon. If you leave these at 1.8 (the old default), the build fails when it encounters modern library dependencies (like OkHttp 5 or the latest AndroidX libraries) which now ship with Java 17 bytecode.
Conclusion
The "Build Failed" errors accompanying the shift to AGP 9.0 and Java 17 are not bugs; they are the result of stricter build boundaries and modernization. By explicitly defining your namespace in Gradle and aligning your Java toolchains, you make your Flutter project robust, faster to build, and ready for the next generation of Android development.
Run flutter clean and flutter pub get immediately after applying these changes to flush the build cache.