Initial template: Flutter 3.47.1 Android app, verified building

`flutter create --platforms android` output, unmodified except for docs and one
commented-out escape hatch. The generated toolchain is already what we want --
Gradle 9.3.1 / AGP 9.1.0 / Kotlin 2.4.0 / Java 17 -- because Flutter's template
defaults ARE the policy: they sit above the warn floor, where the floors-only
combo sits one release from breaking.

The point of this repo is the notes, not the code. README.md records why two
apps in this org stopped building without their code changing, where Flutter
keeps its version floors (read them, do not recall them), and four traps that
appear in no changelog: the pub-cache edit that hides from git, kotlinOptions{}
being a compilation error rather than a deprecation under Kotlin >= 2.3, AGP 8.x
being unable to run on Gradle >= 9.6, and Built-in Kotlin being a dead end while
AGP ships a Kotlin below Flutter's own floor.

android/build.gradle.kts carries a commented-out subprojects{} override for
abandoned pub plugins whose declared compileSdk their own AndroidX dependencies
now reject. Off by default: turn it on when a build fails, not speculatively.

Verified: flutter build apk --debug and --release against Flutter 3.47.1.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016dKAqFo9Uj3mPZWvDzj3G8
This commit is contained in:
2026-08-23 12:02:51 +09:00
commit 557b2d92a5
28 changed files with 995 additions and 0 deletions
+70
View File
@@ -0,0 +1,70 @@
allprojects {
repositories {
google()
mavenCentral()
}
}
val newBuildDir: Directory =
rootProject.layout.buildDirectory
.dir("../../build")
.get()
rootProject.layout.buildDirectory.value(newBuildDir)
subprojects {
val newSubprojectBuildDir: Directory = newBuildDir.dir(project.name)
project.layout.buildDirectory.value(newSubprojectBuildDir)
}
// ---------------------------------------------------------------------------------------
// Escape hatch for abandoned pub plugins. Commented out on purpose -- turn it on only
// when a build actually fails, never speculatively.
//
// Symptom it fixes:
//
// Dependency 'androidx.something:1.2.3' requires libraries and applications that
// depend on it to compile against version 34 or later of the Android APIs.
// :some_plugin is currently compiled against android-31.
//
// A pub plugin that has not been republished in a couple of years still declares an old
// compileSdk, while the AndroidX artifacts it resolves to have moved on and now refuse to
// be consumed by it. You cannot fix that in the plugin: it lives in ~/.pub-cache, which is
// shared between users on this box, unversioned, invisible to git, and rewritten by the
// next `pub get`. Editing it there poisons every build on the machine and silently
// disappears. Force the value from here instead, where it is committed and reviewable.
//
// Reflection is used because AGP's types are not on the root build script classpath.
//
// NOTE: this block must be registered BEFORE evaluationDependsOn(":app") below, which
// evaluates subprojects eagerly -- registered after it, afterEvaluate throws
// "Cannot run Project.afterEvaluate(Action) when the project is already evaluated."
//
// The real fix is dropping or replacing the unmaintained plugin. This is a holding action.
//
// subprojects {
// afterEvaluate {
// val androidExt = extensions.findByName("android") ?: return@afterEvaluate
// runCatching {
// androidExt.javaClass
// .getMethod("setCompileSdkVersion", Int::class.javaPrimitiveType)
// .invoke(androidExt, 36)
// }
// runCatching {
// val defaultConfig = androidExt.javaClass
// .getMethod("getDefaultConfig")
// .invoke(androidExt)
// defaultConfig.javaClass
// .getMethod("setMinSdkVersion", Int::class.javaPrimitiveType)
// .invoke(defaultConfig, 24)
// }
// }
// }
// ---------------------------------------------------------------------------------------
subprojects {
project.evaluationDependsOn(":app")
}
tasks.register<Delete>("clean") {
delete(rootProject.layout.buildDirectory)
}