You open an existing Flutter project, perhaps one you haven't touched in a few months, or clone a repo that hasn't been updated recently. You hit "Run," and instead of the familiar Gradle build tasks, the console crashes immediately with a cryptic error:
java.lang.IllegalArgumentException: Unsupported class file major version 61
Or perhaps:
Could not create service of type ScriptPluginFactory using BuildScopeServices.createScriptPluginFactory().
> Unsupported class file major version 65
This error is not a bug in your Dart code. It is an infrastructure collision between your IDE, the Java Development Kit (JDK), and the Gradle build tool.
The Root Cause: Bytecode Mismatch
The error "Unsupported class file major version" is the JVM's way of saying, "I am trying to read a class file compiled with a newer version of Java than I support."
Here is the mapping you need to know:
- Version 55 = Java 11
- Version 61 = Java 17
- Version 65 = Java 21
The collision happens due to the evolution of Android Studio. Recent versions (Flamingo, Giraffe, Iguana, Koala) bundle JDK 17 (and lately JDK 21) as the default Gradle daemon runtime.
However, older Flutter projects usually ship with a gradle-wrapper.properties file pointing to Gradle 7.x (or older).
The incompatibility matrix:
- Gradle 7.2 (and below) cannot run on JDK 17.
- Gradle 7.5 (and below) cannot run on JDK 20/21.
When you press run, Android Studio launches the Gradle Daemon using its internal JDK (Java 17/21). That daemon attempts to spin up the Gradle Wrapper defined in your project (e.g., Gradle 7.0). The wrapper crashes because it doesn't understand the JVM version running it.
The Solution: Modernize the Build Chain
While you can downgrade the JDK used by Android Studio, that is a temporary patch. The correct engineering solution is to upgrade your project's build infrastructure to support Java 17.
This involves three specific files in your android directory.
1. Upgrade the Gradle Wrapper
First, tell the project to use a version of Gradle that supports modern Java. We will use Gradle 8.5, which is stable and performant.
File: android/gradle/wrapper/gradle-wrapper.properties
Update the distributionUrl to point to version 8.5.
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
# Change this line:
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-all.zip
2. Upgrade the Android Gradle Plugin (AGP)
The AGP version acts as the bridge between Gradle and the Android build system. Gradle 8.5 requires AGP version 8.1.0 or higher.
File: android/build.gradle (Project Level)
Locate the buildscript block or the plugins block (depending on your project age) and update the dependency version.
buildscript {
ext.kotlin_version = '1.9.22' // Ensure Kotlin is also reasonably modern
repositories {
google()
mavenCentral()
}
dependencies {
// Update to 8.1.0 or higher
classpath 'com.android.tools.build:gradle:8.2.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
allprojects {
repositories {
google()
mavenCentral()
}
}
rootProject.buildDir = '../build'
subprojects {
project.buildDir = "${rootProject.buildDir}/${project.name}"
}
subprojects {
project.evaluationDependsOn(':app')
}
tasks.register("clean", Delete) {
delete rootProject.buildDir
}
3. Configure Java 17 Compatibility & Namespace
Finally, you must explicitly tell the compiler to generate bytecode compatible with Java 17. Additionally, AGP 8.0+ enforces the removal of the package attribute from the AndroidManifest.xml in favor of a namespace declaration in the build file.
File: android/app/build.gradle (Module Level)
plugins {
id "com.android.application"
id "kotlin-android"
// The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
id "dev.flutter.flutter-gradle-plugin"
}
android {
// 1. Define the namespace (Replaces 'package' in AndroidManifest.xml)
namespace "com.example.your_app_name"
compileSdk 34 // Target Android 14
defaultConfig {
applicationId "com.example.your_app_name"
minSdk 21 // Flutter default minimum
targetSdk 34
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
}
// 2. Configure Compile Options for Java 17
compileOptions {
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
}
// 3. Configure Kotlin Options
kotlinOptions {
jvmTarget = '17'
}
buildTypes {
release {
signingConfig signingConfigs.debug
}
}
}
flutter {
source '../..'
}
4. Verification
After making these changes, perform a clean build to flush the cache of the old incompatible artifacts.
Run the following in your terminal:
cd android
./gradlew clean
cd ..
flutter pub get
flutter run
Why This Fix Works
- Gradle 8.5 Alignment: By upgrading the wrapper to 8.5, you use a build tool version that is natively compiled against and tested with JDK 17+. It no longer crashes when the Android Studio daemon (running JDK 17) invokes it.
- AGP Compatibility: The Android Gradle Plugin version must always move in lockstep with the Gradle version. AGP 8.x is designed for Gradle 8.x and utilizes the R8 full-mode compiler by default, resulting in smaller APK sizes.
- JVM Target 17: Setting
sourceCompatibilityandjvmTargetensures that when your Android/Kotlin code is compiled, the resulting bytecode header contains the major version "61". This matches the runtime environment provided by modern Android devices and the build environment provided by Android Studio.
By aligning your toolchain (IDE JDK), your build orchestrator (Gradle), and your compiler settings (AGP), you eliminate the version drift that causes the "Unsupported class file major version" error.