`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
4.8 KiB
flutter-android-template
Starting point for Flutter Android apps on halogen. Generated by flutter create and
then annotated — the value here is not the code, it is the notes on the parts that go
wrong six months later.
Generated 2026-08-23 against Flutter 3.47.1, verified --debug and --release.
Use it
Gitea template repo — Use this template on the web UI, or:
tea repos create --owner android-stuff --name my-app
# then, locally:
flutter create --org com.novoyuuparosk --project-name my_app --platforms android .
After cloning, before anything else:
flutter-android-preflight . # on halogen; see below
flutter pub get
flutter build apk --debug
android/local.properties is gitignored and regenerated per machine. Do not commit it.
Why this repo exists
Two apps in this org became unbuildable without a line of their code changing. They had been pinned to Gradle 8.12 / AGP 8.9.1 / Kotlin 2.1.0 — fine when written, and by 2026-08-23 all three were below the hard floors Flutter refuses to build under.
Flutter carries those floors as source constants inside the SDK, and moves them every release:
packages/flutter_tools/gradle/src/main/kotlin/DependencyVersionChecker.kt |
error + warn floors for Gradle / AGP / Kotlin / Java / minSdk |
packages/flutter_tools/lib/src/android/gradle_utils.dart |
template defaults, compileSdk, NDK |
Read those two files instead of recalling version numbers. Any remembered number is wrong on a long enough timeline, including one you read in a blog post last week.
The rule that keeps this from happening again
Track Flutter's template defaults, not its floors.
Flutter promotes its warn floor to the error floor in the very next release — its own
source says so. A project sitting just above the hard floor has one release of runway
before it breaks. A project on the template defaults has the full cycle.
That is why this template is on Gradle 9.3.1 / AGP 9.1.0 / Kotlin 2.4.0 / Java 17 rather than the lower combo that merely builds today.
/usr/local/bin/flutter-android-preflight on halogen automates the comparison: it parses
the floors out of the installed SDK, checks a project against them, and --fix rewrites
the project. Run it after every flutter upgrade, not when a build breaks. /opt/flutter
tracks the stable branch, so it moves on its own.
Traps, none of which are in a changelog
- ⚠ Never edit
~/.pub-cache. It is shared between users, unversioned, invisible togit status, and wiped by the nextpub get. An edit there poisons every build on the machine and leaves no trace in any repo. If a plugin needs different build settings, override them fromandroid/build.gradle.kts— there is a worked example, commented out, in that file. Damage is repaired withdart pub cache repair --all. - ⚠
kotlinOptions {}is a script COMPILATION ERROR under Kotlin ≥ 2.3, not a deprecation warning. Usekotlin { compilerOptions { jvmTarget = ... } }, asandroid/app/build.gradle.ktshere already does. - ⚠ AGP 8.x cannot run on Gradle ≥ 9.6. Gradle 9.6.0 removed
InternalProblems, which every AGP 8.x builds against; Gradle 9.7 fails at configuration time. While on AGP 8.x, Gradle is capped at 9.5. Moot on AGP 9, but it is what makes "just use the newest Gradle" the wrong instinct. - ⚠ Built-in Kotlin is a dead end on Flutter 3.47.1. Flutter warns that applying the
Kotlin Gradle Plugin "will cause build failures in future versions" and points at the
migration guide. Do not follow it yet: with
android.builtInKotlin=true, AGP 9.1.0 supplies Kotlin 2.2.10, which is below Flutter's own 2.2.20 floor — Flutter rejects the Kotlin that Flutter told you to adopt. KeepbuiltInKotlin=falseand apply KGP explicitly until AGP ships a Kotlin at or above the SDK's floor. android.enableJetifieris obsolete and removed outright in AGP 9. It is absent here deliberately; do not let an old snippet reintroduce it.- Abandoned plugins are the residual risk. The toolchain is now easy to keep current;
a pub package last published in 2022 is not. Check
flutter pub outdatedbefore adopting a dependency that looks quiet.
Shared toolchain on halogen
/opt/flutter and /opt/android-sdk are group flutterdev (mikkeli, hermes) with
default ACLs granting the group write. Gradle compiles the Flutter Gradle plugin in
place inside the SDK, so without those ACLs whichever user builds first locks the other
out — and the resulting error mentions nothing about permissions:
IllegalStateException: The settings are not yet available for build.
If that appears, check ownership under
/opt/flutter/packages/flutter_tools/gradle/{build,.gradle} before believing anything else.