Skip to content

Audit flutter for compatibility AGP 9.0.0 #175688

Closed

Description

@bc-lee

As of today (2025-09-20), the Android Gradle Plugin (AGP) is at version 9.0.0-alpha05. This marks the first major release since AGP 8.0, which was introduced more than two years ago(2023-04).

According to the AGP Preview Release Notes, AGP 9.0.0 introduces a significant number of breaking changes. The Android community is still in the process of identifying migration strategies, with resources such as AGP Status Tracker documenting compatibility issues and workarounds.

Because Flutter supports a wide range of AGP versions, one key challenge will be ensuring compatibility. A likely near-term mitigation strategy is to apply:

android.newDsl=false

This would allow projects to defer adoption of the new DSL until much later in the AGP 9.x cycle (expected around next year), minimizing disruption while maintaining forward compatibility.

Activity

  1. added
    in triagePresently being triaged by the triage team
    platform-androidAndroid applications specifically
    team-androidOwned by Android platform team
    t: gradle"flutter build" and "flutter run" on Android
    toolAffects the "flutter" command-line tool. See also t: labels.
    and removed
    in triagePresently being triaged by the triage team
    on Sep 22, 2025
  2. reidbaker commented on Sep 22, 2025

    @reidbaker
    Contributor

    @jesswrd FYI, We have been working on Gradle 9 support let look into AGP 9 and see what work needs to be migrated.

    @bc-lee Do you have a specific request here (like a flutter tool command that fails to work that we need to fix or having flutter add android.newDsl=false to project templates?) or were you filing an issue to let us know, which is still helpful.

  3. added
    P1High-priority issues at the top of the work list
    on Sep 22, 2025
  4. reidbaker commented on Sep 22, 2025

    @reidbaker
    Contributor
  5. bc-lee commented on Sep 23, 2025

    @bc-lee
    ContributorAuthor

    Do you have a specific request here (like a flutter tool command that fails to work that we need to fix or having flutter add android.newDsl=false to project templates?) or were you filing an issue to let us know, which is still helpful.

    No, I just wanted to remind you of the incompatibilities so the Flutter team doesn’t forget. I suggested using android.newDsl=false since that seems like the easiest way to handle them, but it’s up to the Flutter Android team. 🙂

  6. reidbaker commented on Sep 23, 2025

    @reidbaker
    Contributor

    OK thank you for the issue. We will investigate if/where we break with the new dsl.

  7. changed the title [-]AGP 9.0.0 Incompatibility[/-] [+]Audit flutter for compatibility with the AGP 9.0.0 new dsl[/+] on Sep 23, 2025
  8. AbdeMohlbi commented on Sep 24, 2025

    @AbdeMohlbi
    Member

    so after some debugging and testing , this is what i have found :
    creating a project using flutter create and updating these build files :

    • app/build.gradle.kts
    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 {
        namespace = "com.example.demo_agp"
        compileSdk = flutter.compileSdkVersion
        ndkVersion = flutter.ndkVersion
    
        compileOptions {
            sourceCompatibility = JavaVersion.VERSION_11
            targetCompatibility = JavaVersion.VERSION_11
        }
    
        kotlinOptions {
            jvmTarget = JavaVersion.VERSION_11.toString()
        }
    
        defaultConfig {
            // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
            applicationId = "com.example.demo_agp"
            // You can update the following values to match your application needs.
            // For more information, see: https://flutter.dev/to/review-gradle-config.
            minSdk = flutter.minSdkVersion
            targetSdk = flutter.targetSdkVersion
            versionCode = flutter.versionCode
            versionName = flutter.versionName
        }
    
        buildTypes {
            release {
                // TODO: Add your own signing config for the release build.
                // Signing with the debug keys for now, so `flutter run --release` works.
                signingConfig = signingConfigs.getByName("debug")
            }
        }
    }
    
    flutter {
        source = "../.."
    }
    • gradle-wrapper.properties
    distributionBase=GRADLE_USER_HOME
    distributionPath=wrapper/dists
    zipStoreBase=GRADLE_USER_HOME
    zipStorePath=wrapper/dists
    distributionUrl=https\://services.gradle.org/distributions/gradle-9.0.0-all.zip
    • settings.gradle.kts
    pluginManagement {
        val flutterSdkPath =
            run {
                val properties = java.util.Properties()
                file("local.properties").inputStream().use { properties.load(it) }
                val flutterSdkPath = properties.getProperty("flutter.sdk")
                require(flutterSdkPath != null) { "flutter.sdk not set in local.properties" }
                flutterSdkPath
            }
    
        includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
    
        repositories {
            google()
            mavenCentral()
            gradlePluginPortal()
        }
    }
    
    plugins {
        id("dev.flutter.flutter-plugin-loader") version "1.0.0"
        id("com.android.application") version "9.0.0-alpha05" apply false
        id ("org.jetbrains.kotlin.android") version "2.2.10" apply false
    }
    
    include(":app")

    will result the build o fail with the error :

    Build file 'C:\Users\Abdessalem\Desktop\demo_agp\android\app\build.gradle.kts' line: 1
    
    An exception occurred applying plugin request [id: 'kotlin-android']
    > Failed to apply plugin 'kotlin-android'.
       > Cannot add extension with name 'kotlin', as there is an extension already registered with that name.
    
    * Try:
    > Run with --info or --debug option to get more log output.
    > Run with --scan to generate a Build Scan (Powered by Develocity).
    > Get more help at https://help.gradle.org.

    which is expected because according to the link given by OP Switch to [built-in Kotlin](https://developer.android.com/build/releases/agp-preview#android-gradle-plugin-built-in-kotlin): The org.jetbrains.kotlin.android plugin is not compatible with the new DSL. which means that id("kotlin-android") in app/build.gradle.kts should be removed but when doing that will break the FGP because // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins. with the error:

    An exception occurred applying plugin request [id: 'dev.flutter.flutter-gradle-plugin']
    > Failed to apply plugin 'dev.flutter.flutter-gradle-plugin'.
       > java.lang.NullPointerException (no error message)
    

    another issue is that AGP 9 introduced breaking changes for some API , the most important and related to our current issue being ApkVariant and here is the lint that appears 'interface ApkVariant : BaseVariant, InstallableVariant, AndroidArtifactVariant, InternalBaseVariant' is deprecated. Deprecated in Java. , here is the lint from android studio:
    Image
    so apparently issue 166550 needs to be fixed before moving forward on this, there are other changes to to other APIs like ApplicationExtension which we currently use but it seems a bit simpler then the first one .

    for the opt out :

    adding android.newDsl=false to gradle.properties file and removing this bloc from app/build.gradle.kts:

    kotlinOptions {
        jvmTarget = JavaVersion.VERSION_11.toString()
    }

    results another error: The project is using an incompatible version (AGP 9.0.0-alpha05) of the Android Gradle plugin. Latest supported version is AGP 8.13.0 which is more confusing because i was able to start some tasks before with AGP 9.0.0-alpha05 but now it fails the moment i start any task.
    after doing what android studio suggests and using AGP 8.13.0 the app starts as expected
    CC @reidbaker

  9. bc-lee commented on Sep 24, 2025

    @bc-lee
    ContributorAuthor

    results another error: The project is using an incompatible version (AGP 9.0.0-alpha05) of the Android Gradle plugin. Latest supported version is AGP 8.13.0

    This error indicates a mismatch between your AGP version and your Android Studio version. You can still build using the Gradle CLI.

    If you want to use an AGP alpha version, you also need to use the corresponding Canary build of Android Studio. For example, the latest AGP 9.0.0-alpha06 is only compatible with Android Studio Otter | 2025.2.1 Canary 1, which was released on 2025-09-22 (just two days ago).

  10. 19 remaining items

  11. amrgetment commented on Jan 26, 2026

    @amrgetment

    I have around 100 packages in my pubspec, so having a good report showing which packages are not compatible would be really helpful. if no time then no problem I know you're busy with more important issues.

  12. reidbaker commented on Jan 26, 2026

    @reidbaker
    Contributor

    At this point I would expect that most packages (or at least the ones that use kotlin) are not compatible. AGP 9 Came out the second week of january and kotlin usage is pretty popular. This particular import I expect to be widely used.

    But to answer your question we dont have a flutter doctor for plugins or anything like that. The best advice I can give you is to regex your pub cache for build.gradle

  13. reidbaker commented on Jan 26, 2026

    @reidbaker
    Contributor

    find ~/.pub-cache -name build.gradle -print0 | xargs -0 grep org.jetbrains.kotlin.android that will work on your whole pub cache which is machine wide but it should give you an idea.

    On my working machine of 378 packages in my pub cache I have 94 entries that contain org.jetbrains.kotlin.android but most appear to be example apps. Hope that helps.

  14. amrgetment commented on Jan 26, 2026

    @amrgetment

    Thanks a lot, I made a python script that make a decorated report for my project if someone need that

    Python script for the Gradle 9 compatibility report check_gradle9_compat.py
    #!/usr/bin/env python3
    import json
    import os
    import re
    import sys
    from pathlib import Path
    from typing import List, Tuple, Dict
    
    # ------------------ ANSI Colors ------------------
    RESET = "\033[0m"
    BOLD = "\033[1m"
    DIM = "\033[2m"
    
    RED = "\033[31m"
    YELLOW = "\033[33m"
    GREEN = "\033[32m"
    BLUE = "\033[34m"
    CYAN = "\033[36m"
    MAGENTA = "\033[35m"
    
    def supports_color() -> bool:
        if os.name == "nt":
            # Windows terminals vary; keep it simple:
            return False
        return sys.stdout.isatty()
    
    USE_COLOR = supports_color()
    
    def c(txt: str, color: str) -> str:
        if not USE_COLOR:
            return txt
        return f"{color}{txt}{RESET}"
    
    def badge(sev: str) -> str:
        # sev: "HIGH" | "WARN" | "INFO"
        if sev == "HIGH":
            return c("🚨 HIGH", RED) if USE_COLOR else "🚨 HIGH"
        if sev == "WARN":
            return c("⚠️  WARN", YELLOW) if USE_COLOR else "⚠️  WARN"
        return c("ℹ️  INFO", BLUE) if USE_COLOR else "ℹ️  INFO"
    
    # ------------------ Helpers ------------------
    def read_text(p: Path) -> str:
        try:
            return p.read_text(encoding="utf-8", errors="ignore")
        except Exception:
            return ""
    
    def find_files(root: Path, patterns: Tuple[str, ...]) -> List[Path]:
        out: List[Path] = []
        for pat in patterns:
            out.extend(root.rglob(pat))
        # de-dup
        seen = set()
        uniq = []
        for p in out:
            if p not in seen:
                uniq.append(p)
                seen.add(p)
        return uniq
    
    def short_path(p: Path, project_root: Path) -> str:
        """Make paths shorter for nicer output."""
        try:
            rel = p.relative_to(project_root)
            return str(rel)
        except Exception:
            s = str(p)
            # shrink pub cache long paths
            s = s.replace(str(Path.home()), "~")
            return s
    
    def divider(char="─", width=72) -> str:
        return char * width
    
    def title_box(text: str) -> None:
        print()
        print(c(divider("━"), MAGENTA))
        print(c(f"  {text}", MAGENTA))
        print(c(divider("━"), MAGENTA))
        print()
    
    def section(text: str) -> None:
        print(c(f"\n{text}", CYAN))
    
    def get_pub_cache_dir() -> Path:
        env = os.environ.get("PUB_CACHE")
        if env:
            return Path(env).expanduser()
        if os.name == "nt":
            local = os.environ.get("LOCALAPPDATA")
            if local:
                return Path(local) / "Pub" / "Cache"
            return Path.home() / "AppData" / "Local" / "Pub" / "Cache"
        return Path.home() / ".pub-cache"
    
    # ------------------ Rules ------------------
    # (title, pattern, advice, sev)
    RULES: List[Tuple[str, re.Pattern, str, str]] = [
        ("Deprecated 'maven' plugin", re.compile(r"apply\s+plugin:\s*['\"]maven['\"]"),
         "Replace with 'maven-publish'.", "HIGH"),
    
        ("Deprecated 'compile' configuration", re.compile(r"(^|\s)compile(\s|\()", re.MULTILINE),
         "Use 'implementation' or 'api'.", "HIGH"),
    
        ("Deprecated 'provided' configuration", re.compile(r"(^|\s)provided(\s|\()", re.MULTILINE),
         "Use 'compileOnly'.", "HIGH"),
    
        ("JCenter repository", re.compile(r"jcenter\s*\("),
         "Remove jcenter(); use mavenCentral() + google().", "HIGH"),
    
        ("Kotlin Android Extensions plugin", re.compile(r"kotlin-android-extensions"),
         "Removed; switch to ViewBinding / kotlin-parcelize.", "HIGH"),
    
        ("Old AGP classpath (<8)", re.compile(r"classpath\s+['\"]com\.android\.tools\.build:gradle:(\d+)\."),
         "Upgrade AGP to 8+ (Gradle 9 readiness).", "HIGH"),
    
        ("Old Kotlin plugin (<1.9)", re.compile(r"classpath\s+['\"]org\.jetbrains\.kotlin:kotlin-gradle-plugin:(\d+)\.(\d+)"),
         "Consider Kotlin 1.9+ depending on AGP.", "WARN"),
    
        ("Legacy buildscript{} style", re.compile(r"^\s*buildscript\s*\{", re.MULTILINE),
         "Not wrong, but modern projects prefer plugins{} / version catalogs.", "INFO"),
    ]
    
    WRAPPER_RE = re.compile(r"distributionUrl=.*gradle-([0-9.]+)-")
    
    def scan_text_for_rules(text: str) -> List[Tuple[str, str, str]]:
        hits = []
        for title, pattern, advice, sev in RULES:
            m = pattern.search(text)
            if not m:
                continue
    
            # Version threshold checks
            if title.startswith("Old AGP classpath"):
                major = int(m.group(1))
                if major >= 8:
                    continue
    
            if title.startswith("Old Kotlin plugin"):
                major = int(m.group(1))
                minor = int(m.group(2))
                if (major > 1) or (major == 1 and minor >= 9):
                    continue
    
            hits.append((sev, title, advice))
    
        return hits
    
    def parse_plugins(project_root: Path) -> List[Dict]:
        f = project_root / ".flutter-plugins-dependencies"
        if not f.exists():
            return []
    
        try:
            data = json.loads(read_text(f))
        except Exception:
            return []
    
        plugins = data.get("plugins", {}).get("android", []) or []
        out = []
        for p in plugins:
            name = p.get("name")
            path = p.get("path")
            if name and path:
                out.append({"name": name, "path": path})
        return out
    
    # ------------------ Output "Cards" ------------------
    def print_file_card(file_path: str, issues: List[Tuple[str, str, str]]):
        # file header
        print(c("  ┌──────────────────────────────────────────────────────────────", DIM))
        print(f"  │ 📄 {c(file_path, BOLD if USE_COLOR else '')}")
        print(c("  ├──────────────────────────────────────────────────────────────", DIM))
    
        for sev, title, advice in issues:
            print(f"  │ {badge(sev)}  {title}")
            print(f"  │    ↳ {c(advice, DIM)}")
    
        print(c("  └──────────────────────────────────────────────────────────────", DIM))
        print()
    
    def print_plugin_card(plugin_name: str, plugin_path: str, total_issues: int):
        label = f"🔌 {plugin_name}"
        right = f"{total_issues} issue(s)"
        print(c(divider("─"), DIM))
        print(f"{c(label, BOLD if USE_COLOR else '')}  {c('•', DIM)}  {c(plugin_path, DIM)}")
        print(f"{c('↳', DIM)} {c(right, YELLOW if USE_COLOR else '')}")
        print(c(divider("─"), DIM))
    
    # ------------------ Main ------------------
    def main():
        root = Path.cwd()
        if not (root / "pubspec.yaml").exists():
            print("❌ Run this script from your Flutter project root (pubspec.yaml).")
            sys.exit(1)
    
        android_dir = root / "android"
        if not android_dir.exists():
            print("❌ No android/ folder found.")
            sys.exit(1)
    
        title_box("🧪 Gradle 9 Compatibility Report (Flutter Android)")
    
        # ----- Project scan -----
        project_findings = []
        android_files = find_files(
            android_dir,
            ("*.gradle", "*.gradle.kts", "gradle.properties", "settings.gradle", "settings.gradle.kts")
        )
        for f in android_files:
            txt = read_text(f)
            hits = scan_text_for_rules(txt)
            if hits:
                project_findings.append((f, hits))
    
        wrapper = android_dir / "gradle" / "wrapper" / "gradle-wrapper.properties"
        wrapper_version = None
        if wrapper.exists():
            m = WRAPPER_RE.search(read_text(wrapper))
            if m:
                wrapper_version = m.group(1)
    
        # ----- Plugin scan -----
        plugin_findings = []
        plugins = parse_plugins(root)
        pub_cache = get_pub_cache_dir()
    
        for p in plugins:
            name = p["name"]
            base = Path(p["path"]).expanduser()
    
            if not base.exists():
                # best-effort fallback
                candidates = list(pub_cache.rglob(f"{name}-*"))
                if candidates:
                    base = candidates[0]
    
            android_sub = base / "android"
            if not android_sub.exists():
                continue
    
            plugin_files = find_files(
                android_sub,
                ("*.gradle", "*.gradle.kts", "gradle.properties", "settings.gradle", "settings.gradle.kts")
            )
    
            hits_per_file = []
            for f in plugin_files:
                hits = scan_text_for_rules(read_text(f))
                if hits:
                    hits_per_file.append((f, hits))
    
            if hits_per_file:
                plugin_findings.append((name, base, hits_per_file))
    
        # ----- Summary -----
        total_project = sum(len(h) for _, h in project_findings)
        total_plugins = sum(len(hits) for _, _, files in plugin_findings for _, hits in files)
        total = total_project + total_plugins
    
        print("📌 Summary")
        print(f"  {c('•', DIM)} Project findings : {c(str(total_project), YELLOW if total_project else GREEN)}")
        print(f"  {c('•', DIM)} Plugin findings  : {c(str(total_plugins), YELLOW if total_plugins else GREEN)}")
        print(f"  {c('•', DIM)} Total            : {c(str(total), RED if total else GREEN)}")
        if wrapper_version:
            print(f"  {c('•', DIM)} Gradle wrapper   : {c(wrapper_version, CYAN)}")
        print()
    
        # ----- Print project -----
        section("🏗️ Project (android/)")
        if not project_findings:
            print(c("✅ Looks clean. No obvious Gradle 9 blockers found in project files.\n", GREEN))
        else:
            for f, hits in project_findings:
                print_file_card(short_path(f, root), hits)
    
        # ----- Print plugins -----
        section("🧩 Flutter Plugins")
        if not plugin_findings:
            print(c("✅ No obvious Gradle 9 issues found inside plugins.\n", GREEN))
        else:
            for plugin_name, plugin_path, files in plugin_findings:
                plugin_path_str = str(plugin_path).replace(str(Path.home()), "~")
                total_issues = sum(len(h) for _, h in files)
    
                print_plugin_card(plugin_name, plugin_path_str, total_issues)
    
                for f, hits in files:
                    # shorten file path by showing only last parts
                    fp = str(f).replace(str(Path.home()), "~")
                    print_file_card(fp, hits)
    
        # ----- Exit code -----
        print(c("✅ Next steps:", CYAN))
        print("  1) Update flagged plugins (pub.dev).")
        print("  2) If plugin is abandoned → replace/fork.")
        print("  3) Test build with Gradle 9 wrapper:")
        print("     cd android && ./gradlew tasks")
        print()
    
        sys.exit(1 if total > 0 else 0)
    
    if __name__ == "__main__":
        main()
    Image
  15. AbdeMohlbi commented on Jan 26, 2026

    @AbdeMohlbi
    Member

    @reidbaker does this mean all our packages needs to do this also ?

  16. amrgetment commented on Jan 26, 2026

    @amrgetment

    yes 😅, all packages need migration but The Gradle stays on the same version for 2 years
    What I would suggest is that they make a sub-agent or a an agent skill called flutter-gradle-9-migration which will make this so easy for everyone instead of just a migration document.
    Anyway they are doing amazing work to make Flutter up to date ❤️

    Image
  17. amrgetment commented on Jan 27, 2026

    @amrgetment

    The main issue is that all packages must be migrated. If even a single package is migrated—like file_picker—the app won’t compile on Android. It’s essentially a trap 😭. I have to pin file_picker to 10.3.8 even there is a security fix on 10.3.9 😭
    That's why a sub-agent or a an agent skill for migration is needed so all the community can add it faster

    🚧🚧Any library upgrade to the Gradle 9, the users won't able to use it unless all packages are migrated 🚧🚧

    Image Image
  18. hugues1277 commented on Jan 27, 2026

    @hugues1277

    i changed in gradle-wrapper.properties but no results
    distributionUrl=https://services.gradle.org/distributions/gradle-9.3.0-all.zip

    try :
    Java -> VERSION_17

    flutter clean
    flutter pub get
    flutter build apk

    What is the method to follow to solve this problem ?

  19. amrgetment commented on Jan 27, 2026

    @amrgetment

    @hugues1277 if you are referring to the file_picker you can't solve it until all the packages are migrated to The Gradle 9, or do you refer to a different issue?
    You can use my script on your project root path to know how many packages need migration in your pubspec
    #175688 (comment)
    for the libraries that crash change it like this

    Image
  20. jesswrd commented on Feb 2, 2026

    @jesswrd
    Contributor

    Before updating the Flutter plugins to support AGP 9 by removing built-in Kotlin, I'm auditing to see if the migration is backwards compatible with older versions of AGP here.

  21. changed the title [-]Audit flutter for compatibility with the AGP 9.0.0 new dsl[/-] [+]Audit flutter for compatibility AGP 9.0.0[/+] on Mar 31, 2026
  22. jmagman commented on May 12, 2026

    @jmagman
    Member

    The remaining work here is being tracked separately in #180137 and #181557, please follow along there!
    If you discover additional problems with AGP 9, please file an issue with specific repro steps and we'll take a look!

  23. github-actions commented on May 26, 2026

    @github-actions

    This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new bug, including the output of flutter doctor -v and a minimal reproduction of the issue.

  24. locked as resolved and limited conversation to collaborators on May 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

P1High-priority issues at the top of the work listplatform-androidAndroid applications specificallyt: gradle"flutter build" and "flutter run" on Androidteam-androidOwned by Android platform teamtoolAffects the "flutter" command-line tool. See also t: labels.

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions