Table of Contents

    Book an Appointment

    How Did We Discover the Flutter AGP 9.1.0 Build Error in Production?

    While working on a recent enterprise FinTech application, our engineering team was tasked with clearing out Play Store optimization warnings ahead of a major compliance release. To ensure maximum performance and meet Google’s latest Android ecosystem requirements, we initiated an upgrade to Flutter 3.47.0. This upgrade inherently pushed our Android Gradle Plugin (AGP) version to 9.1.0 and Kotlin (KGP) to 2.4.0.

    Because enterprise applications rely heavily on environment separation, we utilize flavoring alongside Flutter Version Management (FVM). When we attempted to build our staging environment, the CI pipeline halted abruptly. An unexpected build exception derailed the deployment, blocking the QA cycle. This is a common hurdle when maintaining long lifecycle applications and it underscores the necessity of having experienced engineering teams at the helm. When companies hire app developer to create a mobile app, they often underestimate the maintenance complexity that arises from aggressive open-source release cycles.

    This technical challenge inspired this article. By sharing our diagnostic process and resolution for this specific Kotlin Gradle Plugin conflict, we aim to help other CTOs and architects navigate similar modernization roadblocks.

    Why Do Flutter 3.47.0 and AGP 9.1.0 Cause Kotlin Plugin Conflicts?

    At an architectural level, the Android build system has undergone a fundamental shift with AGP 9.0+. Historically, whenever a project needed Kotlin support, engineers (and third-party package authors) had to explicitly declare and apply the Kotlin Gradle Plugin (KGP) via apply plugin: 'kotlin-android' in their build.gradle files.

    Starting with AGP 9.0, Android introduced a streamlined approach via the android.builtInKotlin=true flag. This delegates Kotlin compilation directly to the Android Gradle Plugin, rendering the standalone org.jetbrains.kotlin.android plugin obsolete. In fact, declaring it manually alongside built-in Kotlin triggers a fatal conflict.

    The core issue surfaced because the Flutter 3.47.0 migration script aggressively modernizes the host app’s configuration by enabling android.builtInKotlin=true. However, the wider Flutter package ecosystem on pub.dev does not always move at the same speed. Several critical packages in our dependency tree were still statically applying the legacy Kotlin plugin, resulting in a fractured build environment.

    What Went Wrong When Applying the Kotlin-Android Plugin?

    When initiating our standard staging build via fvm flutter run --flavor staging -t lib/main_staging.dart, the process failed in approximately 15 seconds. The terminal yielded a stark diagnostic error:

    * What went wrong:
    An exception occurred applying plugin request [id: 'kotlin-android']
    > Failed to apply plugin 'kotlin-android'.
       > Failed to apply plugin 'org.jetbrains.kotlin.android'
         The 'org.jetbrains.kotlin.android' plugin is no longer required for Kotlin support since AGP 9.0.
         Solution: Remove the 'org.jetbrains.kotlin.android' plugin from this project's build file: ~/.pub-cache/hosted/pub.dev/google_sign_in_android-7.2.11/android/build.gradle.kts.
         See https://kotl.in/gradle/agp-built-in-kotlin for more details.
    

    Further inspection of the build logs revealed a warning explicitly flagging our dependency tree. The system identified multiple essential plugins—such as firebase_remote_config, google_sign_in_android, shared_preferences_android and url_launcher_android—that were statically applying KGP.

    Our gradle.properties file showed that the Flutter migrator had automatically configured the modern standard:

    android.useAndroidX=true
    android.enableJetifier=true
    android.builtInKotlin=true
    android.newDsl=false
    

    The contradiction was clear: our root project demanded built-in Kotlin, while our foundational third-party packages demanded the legacy KGP injection. Since these packages reside in the local pub cache (or on CI runners), editing them directly is an anti-pattern that violates ephemeral build principles.

    How Did We Approach Resolving the AGP Built-In Kotlin Issue?

    Diagnosing an issue involving third-party supply chain bottlenecks requires a structured approach. Our immediate goal was to unblock the staging release, but our architectural goal was to ensure the CI/CD pipeline remained deterministic and secure. This is the caliber of problem-solving expected when organizations hire software developer teams for dedicated delivery.

    What Alternative Workarounds Did We Consider for Gradle?

    Before settling on our final implementation, we evaluated several technical paths to mitigate the build failure:

    • Direct Cache Modification: We considered writing a pre-build shell script to execute a sed replacement across the ~/.pub-cache/ directory, stripping out the apply plugin: 'kotlin-android' line. We discarded this because modifying external caches introduces silent, flaky behaviors in remote CI/CD pipelines.
    • Git Forking and Dependency Overrides: We evaluated forking the offending repositories (like google_sign_in and firebase_remote_config), updating their build.gradle files and using Flutter’s dependency_overrides in our pubspec.yaml. While robust, maintaining forks for heavily updated Firebase libraries introduces severe technical debt and security patch lag.
    • Gradle Lifecycle Hook Injection: We investigated using Gradle’s projectsEvaluated hook within the root build.gradle to intercept and dynamically strip the Kotlin plugin from subprojects. Unfortunately, AGP 9.0+ validates plugin requests very early in the configuration phase, making interception highly complex and brittle.

    How Did We Finally Implement the Fix for Flutter Build Errors?

    After weighing the architectural trade-offs, we determined that the safest, most deterministic path was to leverage a temporary feature toggle provided by the Android ecosystem, while enforcing strict version tracking for future package updates.

    We deliberately disabled the built-in Kotlin feature by adjusting the android/gradle.properties file. Since the migration to AGP 9.0+ does not strictly mandate builtInKotlin=true until a future AGP 10+ release, setting it to false acts as a highly effective, non-destructive bridge.

    Step 1: Reconfiguring Gradle Properties

    We updated our android/gradle.properties to safely bypass the conflict:

    # Temporarily disabled due to third-party plugin incompatibility with AGP 9.1
    android.builtInKotlin=false
    android.useAndroidX=true
    android.enableJetifier=true
    android.newDsl=false
    

    Step 2: Dependency Monitoring & Technical Debt Tracking

    Simply flipping a boolean is not an architectural solution—it is a deferral. To ensure this didn’t become permanent technical debt, we established a tracking epic in our project management system. We pinned our current package versions and utilized a CI scheduled job to notify us when upstream repositories (like Google’s Flutter packages) release patches explicitly resolving KGP AGP 9+ compatibility.

    Step 3: Validating the Staging Build

    After adjusting the property and running fvm flutter clean followed by our flavor-specific build command, the assembleStagingDebug task completed successfully within 45 seconds. The QA team resumed testing without facing optimization warnings on the Play Store, achieving the initial business objective.

    What Are the Key Engineering Lessons for Upgrading Flutter Apps?

    When you hire mobile developers for enterprise modernization, you expect proactive infrastructure management. Here are the actionable insights from this scenario:

    • Never Blindly Trust Migration Scripts: Automated migrators (like Flutter’s AGP upgrade scripts) apply ideal-state configurations. They lack context regarding your specific dependency tree’s compatibility.
    • Understand the Gradle Lifecycle: Knowing the difference between root project configuration and subproject evaluation is critical when dealing with transient dependency failures.
    • Use Dependency Overrides Cautiously: Forking packages to fix build errors should be an absolute last resort due to the ongoing maintenance burden it shifts onto your team.
    • Document Temporary Toggles: Setting a flag like builtInKotlin=false must be heavily commented in the code and tracked as technical debt in Jira/Linear to prevent future build-breaking surprises when AGP 10 is eventually enforced.
    • Maintain Ephemeral CI Integrity: Resist the urge to patch local cache files. Your build must remain reproducible across any developer machine and CI runner.

    How Can You Modernize Your Enterprise Mobile Architecture?

    Upgrading foundational SDKs like Flutter and AGP is rarely a straight line. Modernization requires navigating complex dependency matrices, understanding deep Gradle build lifecycles and knowing when to apply strategic workarounds without compromising the long-term integrity of the codebase. By analyzing the root cause of the Kotlin plugin failure and systematically evaluating our options, we kept the delivery pipeline moving while safeguarding against future technical debt.

    At WeblineGlobal, we provide structural delivery practices and pre-vetted dedicated teams who treat your product’s architecture with this level of scrutiny. If you need to augment your team with engineers capable of managing complex mobile infrastructures, contact us.

    Social Hashtags

    #Flutter #FlutterDev #AndroidDevelopment #AGP9 #Kotlin #Gradle #MobileAppDevelopment #FlutterDevelopment #AndroidDev #DevOps #CICD #SoftwareEngineering

    Frequently Asked Questions