ANTIGRAVITY LABJP
Articles/App Development
App Development/2026-06-27Advanced

Before AI Studio's Gradle and AGP Versions Quietly Break Your Existing App

When you drop an AI Studio-generated Kotlin/Compose screen into an existing Android app, the AGP, Kotlin, and library versions drift and the build breaks silently. Here is how to pin a single source of truth with a version catalog and add a gate that inspects the generated declarations at the import boundary, with measurements and code.

antigravity394ai-studio3android25gradle2jetpack-compose3

Premium Article

The generated screen ran flawlessly in the emulator. The trouble started the moment I copied its folder into the module of my wallpaper app. ./gradlew assembleRelease passed, but the debug build alone died with Unsupported class file major version. It took me half a day to reach the cause, and the culprit was neither my code nor the generated code — it was the AGP and Kotlin versions declared inside the build.gradle.kts that AI Studio had shipped along with the screen.

Now that AI Studio can generate a Kotlin and Jetpack Compose app from text and carry it from emulator run to physical-device transfer to a Google Play internal test track on a single screen, the distance between prototyping and shipping has shrunk considerably for solo developers. But the output is only coherent as a new app. It is not built on the assumption that you will mix it into an app that already runs. That is where the quiet trap lives.

This article covers a design that prevents the version drift you hit when importing generated Compose into an existing app: pin a version catalog as the single source of truth, and add a gate that mechanically inspects the generated declarations at the import boundary. I will center it on a failure I hit running my own Android wallpaper apps, and the concrete setup that stopped it.

Why "code that ran" breaks inside an existing app

AI Studio output declares dependencies pinned to whatever was the latest stable at generation time. A long-running app, on the other hand, carries its own version constellation that you raised slowly while verifying compatibility. Merge the two naively and Gradle, during resolution, biases toward the newer side — so a version you were deliberately holding back gets silently bumped the moment you import.

Here is how that broke for me, organized by blast radius.

Drifted elementWhat the generated side tends to declareWhat happened in the existing app
AGP (Android Gradle Plugin)Latest major (e.g. 9.x line)NoClassDefFoundError in an older library using Java 8 APIs; desugaring became a prerequisite
Kotlin / Compose compilerLatest stableDrift from the Compose-compiler-to-Kotlin matrix; stopped with Compose Compiler requires Kotlin version
Core libraries (image, DI, etc.)LatestAPI signature changes broke compilation on the existing screens
minSdk / targetSdkGeneration-time recommendationminSdk rose, dropping older devices that had been in the supported set

The AGP bump was the nastiest. My app's image library internally referenced Java 8's Supplier, and after the AGP major update, devices on the Android 6.0.1 band started crashing right at launch. The fix itself was one line — enable coreLibraryDesugaringEnabled and add the desugaring library — but the real problem was that the causal chain "mixing in generated code raised my crash rate" was nearly invisible. I wrote up the triage steps for this desugaring behavior in the record of stopping Java 8-derived crashes on old devices with desugaring.

This is where the idea of a single source of truth earns its keep. Instead of scattering versions across each module's build.gradle.kts, concentrate them in one place — the gradle/libs.versions.toml version catalog — and any version a generated artifact declares on its own must be reconciled against the catalog at import time.

Pin the version catalog as the single source of truth

First, move the whole project's versions into the version catalog. The starting point is to eliminate the inline declarations the output writes, like implementation("androidx.compose...:1.x.x"), and route everything through catalog references.

# gradle/libs.versions.toml — the only place versions are declared in the project
[versions]
agp = "9.0.1"                # hold here. don't bump even if the output declares 9.1.x
kotlin = "2.2.20"
composeCompiler = "2.2.20"   # match kotlin (Kotlin 2.x needs no composeOptions, but manage explicitly)
composeBom = "2026.05.01"
coreDesugar = "2.1.5"        # guard against Java 8-derived crashes after the AGP major update
minSdk = "23"                # Android 6.0. keep it even if the output raises it
targetSdk = "36"
 
[libraries]
compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" }
compose-ui = { group = "androidx.compose.ui", name = "ui" }
compose-material3 = { group = "androidx.compose.material3", name = "material3" }
desugar-jdk-libs = { group = "com.android.tools", name = "desugar_jdk_libs", version.ref = "coreDesugar" }
 
[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }

Then the modules declare no versions at all and reference only the catalog aliases. Compose aligns its versions through the BOM, so the key is to attach no version to the individual libraries.

// app/build.gradle.kts — write no versions. use only catalog aliases
plugins {
    alias(libs.plugins.android.application)
    alias(libs.plugins.kotlin.android)
    alias(libs.plugins.kotlin.compose)
}
 
android {
    compileSdk = libs.versions.targetSdk.get().toInt()
    defaultConfig {
        minSdk = libs.versions.minSdk.get().toInt()
        targetSdk = libs.versions.targetSdk.get().toInt()
    }
    compileOptions {
        // keep desugaring always on so old devices don't fall over after an AGP major bump
        isCoreLibraryDesugaringEnabled = true
        sourceCompatibility = JavaVersion.VERSION_11
        targetCompatibility = JavaVersion.VERSION_11
    }
}
 
dependencies {
    val composeBom = platform(libs.compose.bom)
    implementation(composeBom)
    implementation(libs.compose.ui)
    implementation(libs.compose.material3)
    coreLibraryDesugaring(libs.desugar.jdk.libs)  // always pair this with the TOML entry above
}

Now "the version, seen from any module, lives in one place in the catalog." When you mix a generated artifact into this project, any version it inline-declared does not flow through the catalog as-is, so you can detect it at the import boundary.

Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
If your build broke whenever you pulled an AI Studio-generated Compose screen into your existing app, you can implement a pinning strategy today that stops dependency conflicts before they land
You will be able to bind AGP, Kotlin, the Compose compiler, and key libraries into one version catalog as a single source of truth, and build a gate that inspects generated declarations at the import boundary
You can mix generated screens into a production app that is mid-staged-rollout without re-introducing Android 6.0-era crashes or missing density buckets
Secure payment via Stripe · Cancel anytime

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
Share

Thank You for Reading

Antigravity Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • Copy-paste ready implementation code
  • New advanced guides published daily
  • $5/mo or $10 for lifetime access
View Membership →

Related Articles

App Dev2026-04-25
Gemma 4 × Antigravity — Complete Guide to On-Device AI for iOS and Android
A hands-on implementation guide for integrating Gemma 4 into both iOS (Core ML) and Android (TensorFlow Lite / AI Core) with Antigravity. Covers model conversion, quantization, battery management, and app store approval.
App Dev2026-03-29
Building Android UI with Antigravity and Jetpack Compose: A Practical Guide
Learn how to supercharge your Android UI development by combining Antigravity IDE with Jetpack Compose. From AI-generated composables and Material 3 theming to performance-optimized lists and type-safe navigation.
App Dev2026-06-24
Three Paths Were Each Picking Their Own Number — Deriving versionCode From a Single Source So Releases Stop Stalling
Now that AI Studio can generate an app from one prompt and push it straight to Play's internal testing track, three paths — you, CI, and the agent — each allocate versionCode on their own and collide. Here's how to derive the number from a single source and add a pre-upload guard, with working code.
📚RECOMMENDED BOOKS
Build a Large Language Model (From Scratch)
Sebastian Raschka
LLM Dev
Prompt Engineering for LLMs
Berryman & Ziegler
Prompting
AI Engineering
Chip Huyen
AI Eng
* Contains affiliate links
See all →