Flutter-app publiceren in Google Play met GitHub Actions
Je Flutter-app is klaar en je wilt hem in de Google Play Store krijgen, liefst zonder bij elke update met de hand een bundle te bouwen en te uploaden. In deze handleiding zet je de app in de Play Console en bouw je een pipeline in GitHub Actions die bij elke release een gesigneerde app bundle (AAB) bouwt en uploadt naar de testtrack Intern testen. Naar productie promoot je daarna zelf in de Play Console.
Ik schreef deze stappen op tijdens de publicatie van een echte app (oktober 2026), inclusief de valkuilen waar ik zelf in trapte. Vervang de voorbeeldwaarden (Mijn App, com.voorbeeld.mijn_app, src/mijn_app, …) door die van je eigen project.
Read this article in English: How to publish a Flutter app to Google Play with GitHub Actions.
Ook een iOS-versie nodig? Lees dan Flutter-app naar TestFlight zonder Mac (stap voor stap).
Legenda: 💶 = kost geld · ⏳ = wachttijd · ⚠️ = valkuil die we echt tegenkwamen
Inhoud
- Overzicht en benodigdheden
- Beslissingen vooraf
- Google Play-ontwikkelaarsaccount
- Flutter-project voorbereiden
- Upload-keystore en signing
- App aanmaken en eerste upload (met de hand)
- App-content en store-listing invullen
- Service account voor uploads vanuit CI
- GitHub-secrets
- CI op elke pull request
- Release-workflow: bouwen en uploaden naar Play
- Optioneel: versies automatisch met release-please
- Testers en naar productie
- Problemen en oplossingen
- Korte checklist
0. Overzicht en benodigdheden
| Wat | Waarom | Kost |
|---|---|---|
| Google-account | Eigenaar van het ontwikkelaarsaccount | gratis |
| Google Play Console-account | Apps publiceren | 💶 $25 eenmalig |
| GitHub-repo met het Flutter-project | Broncode + GitHub Actions | gratis (publieke repo of binnen de gratis minuten) |
| Android-toestel | Testen via de testtrack | — |
| JDK 17+ en Android SDK | Lokaal bouwen (zit in Android Studio) | gratis |
| Publieke privacybeleid-URL | Verplicht in de Play Console (zeker met advertenties) | — |
1. Beslissingen vooraf
| Beslissing | Voorbeeld | Opmerking |
|---|---|---|
| Application ID | com.voorbeeld.mijn_app | ⚠️ Na de eerste upload nooit meer te wijzigen. Underscores mogen op Android (op iOS niet: kies eventueel een vorm zonder _ als je beide platformen wilt gelijktrekken). |
| App-naam (Play Store) | Mijn App | Max. 30 tekens. |
| Accounttype | Persoonlijk | Organisatie vraagt een D-U-N-S-nummer. |
| Productietoegang | — | ⚠️ Nieuwe persoonlijke accounts moeten eerst een gesloten test doen met minstens 12 testers gedurende 14 dagen voor ze naar productie mogen. Zie stap 12.3. |
| Releasetrack voor CI | internal | Snelst (geen review), max. 100 testers. |
2. Google Play-ontwikkelaarsaccount
- Ga naar https://play.google.com/console/signup en meld aan met het Google-account dat eigenaar wordt.
- Kies persoonlijk of organisatie en betaal 💶 $25.
- ⏳ Identiteitsverificatie (ID-kaart, adres, telefoonnummer). Kan enkele dagen duren.
- Zet tweestapsverificatie aan op het Google-account.
3. Flutter-project voorbereiden
3.1 Application ID en namespace
In android/app/build.gradle.kts:
android {
namespace = "com.voorbeeld.mijn_app"
compileSdk = 36
defaultConfig {
applicationId = "com.voorbeeld.mijn_app"
minSdk = flutter.minSdkVersion
targetSdk = 36 // minstens wat Google Play op dat moment eist
versionCode = flutter.versionCode // = buildnummer uit pubspec.yaml
versionName = flutter.versionName // = versie uit pubspec.yaml
}
} - Verplaats ook
MainActivity.ktnaar de map die bij de nieuwe package hoort (android/app/src/main/kotlin/com/voorbeeld/mijn_app/) en pas depackage-regel aan. - ⚠️ Target API level: Google verhoogt jaarlijks de minimale
targetSdkvoor nieuwe apps en updates. Controleer de actuele eis op Target API level requirements.
3.2 Versie en buildnummer
In pubspec.yaml:
version: 1.0.0+1 # versionName 1.0.0, versionCode 1 ⚠️ Google Play weigert elke upload met een versionCode die al gebruikt is. Verhoog het getal na de + bij elke upload.
3.3 Appnaam en iconen
-
Naam onder het icoon:
android:label="Mijn App"inandroid/app/src/main/AndroidManifest.xml. -
Iconen: flutter_launcher_icons met een 1024×1024 PNG:
# flutter_launcher_icons.yaml flutter_launcher_icons: android: true ios: false image_path: "assets/icon/app_icon.png" adaptive_icon_background: "#FFFFFF" adaptive_icon_foreground: "assets/icon/app_icon_foreground.png"flutter pub add --dev flutter_launcher_icons dart run flutter_launcher_icons -
Store-icoon voor de listing: 512×512 PNG (apart te uploaden in stap 6).
3.4 Advertenties (alleen bij AdMob)
-
AdMob → Apps → App toevoegen → Android. Staat de app nog niet in Google Play, kies dan Nee bij "staat de app in een ondersteunde app store?".
-
Noteer het app-ID (
ca-app-pub-…~…) en maak een banner-advertentieblok (ca-app-pub-…/…). -
In
AndroidManifest.xml, binnen<application>:<meta-data android:name="com.google.android.gms.ads.APPLICATION_ID" android:value="ca-app-pub-XXXXXXXXXXXXXXXX~XXXXXXXXXX"/> -
Gebruik in debug- en profile-builds altijd Google's test-banner (
ca-app-pub-3940256099942544/6300978111), echte ID's alleen alskReleaseModetrue is. ⚠️ Eigen impressies of kliks met echte ID's kunnen je AdMob-account laten schorsen. -
GDPR: Google eist een gecertificeerd toestemmingsplatform in de EER/VK/CH. Gebruik de UMP SDK (zit in
google_mobile_ads), start Mobile Ads pas alscanRequestAds()true is, en maak in AdMob → Privacy & messaging een GDPR-bericht aan. Zonder dat bericht toont UMP niets. -
Zet een
app-ads.txtop de website die je als ontwikkelaarswebsite opgeeft in Play.
3.5 In de app zelf
- Link naar het privacybeleid (bv. in het scherm "Over").
- Bij ads met UMP: een optie "Privacy Settings" in de instellingen (
ConsentForm.showPrivacyOptionsForm), alleen tonen alsgetPrivacyOptionsRequirementStatus()required is.
3.6 Controleren
dart format --set-exit-if-changed lib test
flutter analyze
flutter test
flutter build apk --debug 4. Upload-keystore en signing
Google Play gebruikt Play App Signing: Google bewaart de echte app signing key; jij signeert uploads met een eigen upload-key. Raak je de upload-key kwijt, dan kan Google hem resetten (⏳ via support). Raak je hem niet kwijt: dat is veel makkelijker.
4.1 Keystore aanmaken
Op Windows met de JDK van Android Studio (pad kan verschillen):
& "C:\Program Files\Android\Android Studio\jbr\bin\keytool.exe" -genkey -v `
-keystore "$env:USERPROFILE\upload-keystore.jks" `
-storetype JKS -keyalg RSA -keysize 2048 -validity 10000 -alias upload Op macOS/Linux:
keytool -genkey -v -keystore ~/upload-keystore.jks -storetype JKS \
-keyalg RSA -keysize 2048 -validity 10000 -alias upload - Kies een sterk wachtwoord voor de keystore en voor de key.
- ⚠️ Back-up: het
.jks-bestand + beide wachtwoorden + de alias in je wachtwoordbeheerder. - ⚠️ Bewaar de keystore buiten de repo en commit hem nooit.
4.2 android/key.properties (alleen lokaal)
storePassword=<keystore-wachtwoord>
keyPassword=<key-wachtwoord>
keyAlias=upload
storeFile=C:\\Users\\<jij>\\upload-keystore.jks Voeg toe aan android/.gitignore (staat er bij nieuwe Flutter-projecten meestal al):
key.properties
**/*.jks
**/*.keystore 4.3 Signing in android/app/build.gradle.kts
Leest de waarden uit key.properties of uit omgevingsvariabelen (voor CI). Zonder key valt de build terug op de debug-key, zodat CI op pull requests ook zonder secrets werkt. Google Play weigert zo'n build, dus je kunt hem nooit per ongeluk publiceren.
import java.io.FileInputStream
import java.util.Properties
val keystoreProperties = Properties()
val keystorePropertiesFile = rootProject.file("key.properties")
if (keystorePropertiesFile.exists()) {
keystoreProperties.load(FileInputStream(keystorePropertiesFile))
}
// Lokaal uit key.properties; in CI uit omgevingsvariabelen.
fun signingValue(propertyKey: String, envKey: String): String? =
keystoreProperties.getProperty(propertyKey) ?: System.getenv(envKey)
val releaseStoreFile = signingValue("storeFile", "ANDROID_KEYSTORE_PATH")
val hasReleaseSigning = releaseStoreFile != null
android {
// … namespace, defaultConfig, …
signingConfigs {
if (hasReleaseSigning) {
create("release") {
keyAlias = signingValue("keyAlias", "ANDROID_KEY_ALIAS")
keyPassword = signingValue("keyPassword", "ANDROID_KEY_PASSWORD")
storeFile = file(releaseStoreFile!!)
storePassword = signingValue("storePassword", "ANDROID_KEYSTORE_PASSWORD")
}
}
}
buildTypes {
release {
signingConfig = if (hasReleaseSigning) {
signingConfigs.getByName("release")
} else {
logger.warn("No upload key found: release build is signed with the debug key.")
signingConfigs.getByName("debug")
}
}
}
} 4.4 Lokaal een gesigneerde bundle bouwen
flutter build appbundle --release Resultaat: build/app/outputs/bundle/release/app-release.aab.
5. App aanmaken en eerste upload (met de hand)
⚠️ De Google Play-API kan geen nieuwe app aanmaken en de eerste bundle niet uploaden. Doe dit één keer met de hand; daarna neemt CI het over.
- Play Console → App maken.
- App-naam, standaardtaal, App (geen game), Gratis.
- Bevestig de verklaringen → App maken.
- Testen → Intern testen → Nieuwe release maken.
- Play App Signing: accepteer Door Google gegenereerde sleutel gebruiken (standaard).
- Upload
app-release.aabuit stap 4.4. - Releasenaam en releasenotities invullen → Volgende → Opslaan en publiceren.
- Testers (tabblad bij Intern testen): maak een e-maillijst met minstens jezelf.
Controleer daarna bij Testen en releasen → App-integriteit dat Play App Signing actief is.
6. App-content en store-listing invullen
Het dashboard van de app toont welke taken nog open staan. Vul ze allemaal in; zonder deze gegevens kun je niet naar productie (en voor sommige testtracks ook niet).
Beleid → App-content:
| Onderdeel | Wat invullen |
|---|---|
| Privacybeleid | De publieke URL |
| App-toegang | "Alle functionaliteit is beschikbaar zonder speciale toegang" (tenzij je een login hebt) |
| Advertenties | "Ja, mijn app bevat advertenties" als je AdMob gebruikt |
| Contentclassificatie | Vragenlijst (IARC) eerlijk invullen |
| Doelgroep | Leeftijdsgroepen; ⚠️ kies je kinderen, dan gelden strenge regels voor ads |
| Gegevensveiligheid | Met AdMob: apparaat- of andere ID's, app-interacties, diagnostiek; gedeeld met Google voor advertenties; versleuteld onderweg |
| Advertentie-ID | "Ja", gebruikt voor advertenties (met AdMob) |
| Overheidsapps, financiële functies, gezondheid, … | Meestal "Nee" |
Groeien → Winkelvermelding (store-listing):
| Onderdeel | Eisen |
|---|---|
| App-naam | Max. 30 tekens |
| Korte beschrijving | Max. 80 tekens |
| Volledige beschrijving | Max. 4000 tekens |
| App-icoon | 512×512 PNG |
| Feature graphic | 1024×500 PNG/JPEG |
| Screenshots telefoon | Min. 2, bv. 1080×1920 |
| Categorie, contactgegevens | E-mail verplicht, website optioneel |
7. Service account voor uploads vanuit CI
7.1 In Google Cloud
- Ga naar de Google Cloud Console → maak een project (bv.
play-publisher) of kies een bestaand. - APIs & Services → Library → zoek Google Play Android Developer API → Enable.
- IAM & Admin → Service Accounts → Create service account: naam bv.
github-play-upload→ Create and continue → geen rol nodig → Done. - Open het service account → Keys → Add key → Create new key → JSON → het bestand wordt gedownload. ⚠️ Dit is een geheim: meteen in je wachtwoordbeheerder, nooit in git.
- Kopieer het e-mailadres van het service account (
…@….iam.gserviceaccount.com).
7.2 In de Play Console
- Gebruikers en rechten → Nieuwe gebruikers uitnodigen → plak het e-mailadres van het service account.
- App-rechten → App toevoegen → kies je app, met minstens:
- Releases naar testtracks
- Releases beheren voor testtracks (de exacte naam verschilt per taal en versie van de Console)
- Gebruiker uitnodigen.
- ⏳ Rechten kunnen tot 24–36 uur nodig hebben voor de API ze herkent.
Eén service account kan voor al je apps dienen: voeg per nieuwe app alleen de app-rechten toe.
8. GitHub-secrets
GitHub → repo → Settings → Secrets and variables → Actions → New repository secret:
| Secret | Inhoud |
|---|---|
ANDROID_KEYSTORE_BASE64 | De keystore, base64-gecodeerd (zie hieronder) |
ANDROID_KEYSTORE_PASSWORD | Wachtwoord van de keystore |
ANDROID_KEY_ALIAS | Alias, bv. upload |
ANDROID_KEY_PASSWORD | Wachtwoord van de key |
PLAY_SERVICE_ACCOUNT_JSON | Volledige inhoud van het JSON-bestand uit stap 7.1 |
Keystore naar base64 en meteen op het klembord:
# Windows (PowerShell)
[Convert]::ToBase64String([IO.File]::ReadAllBytes("$env:USERPROFILE\upload-keystore.jks")) | Set-Clipboard # macOS
base64 -i ~/upload-keystore.jks | pbcopy
# Linux
base64 -w 0 ~/upload-keystore.jks 9. CI op elke pull request
.github/workflows/ci.yml: controleert elke PR, zonder secrets (de release-build valt terug op de debug-key, zie 4.3).
name: CI
on:
pull_request:
branches: [master]
push:
branches: [master]
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true
permissions:
contents: read
env:
FLUTTER_VERSION: 3.47.3 # vastleggen: lokaal en CI dezelfde versie
defaults:
run:
working-directory: src/mijn_app # weglaten als het project in de root staat
jobs:
checks:
name: Format, analyze and test
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v7
- uses: subosito/flutter-action@v2
with:
flutter-version: ${{ env.FLUTTER_VERSION }}
channel: stable
cache: true
- run: flutter pub get
- run: dart format --output=none --set-exit-if-changed lib test
- run: flutter analyze
- run: flutter test
build-android:
name: Build Android (debug)
runs-on: ubuntu-latest
needs: checks
timeout-minutes: 30
steps:
- uses: actions/checkout@v7
- uses: actions/setup-java@v6
with:
distribution: temurin
java-version: 17
cache: gradle
cache-dependency-path: |
src/mijn_app/android/*.gradle.kts
src/mijn_app/android/gradle/wrapper/gradle-wrapper.properties
- uses: subosito/flutter-action@v2
with:
flutter-version: ${{ env.FLUTTER_VERSION }}
channel: stable
cache: true
- run: flutter pub get
- run: flutter build apk --debug Branch protection (aanrader): GitHub → Settings → Branches → Add rule voor master → Require a pull request + Require status checks: "Format, analyze and test" en "Build Android (debug)". Die checks kun je pas kiezen nadat ze één keer gedraaid hebben.
10. Release-workflow: bouwen en uploaden naar Play
.github/workflows/release.yml: start bij het pushen van een tag v* (bv. v1.0.1).
name: Release
on:
push:
tags: ["v*"]
permissions:
contents: read
jobs:
android:
name: Android → Play internal testing
runs-on: ubuntu-latest
timeout-minutes: 45
defaults:
run:
working-directory: src/mijn_app
steps:
- uses: actions/checkout@v7
- uses: actions/setup-java@v6
with:
distribution: temurin
java-version: 17
cache: gradle
cache-dependency-path: |
src/mijn_app/android/*.gradle.kts
src/mijn_app/android/gradle/wrapper/gradle-wrapper.properties
- uses: subosito/flutter-action@v2
with:
flutter-version: 3.47.3
channel: stable
cache: true
- run: flutter pub get
- name: Restore upload keystore
env:
ANDROID_KEYSTORE_BASE64: ${{ secrets.ANDROID_KEYSTORE_BASE64 }}
run: |
if [ -z "$ANDROID_KEYSTORE_BASE64" ]; then
echo "::error::Secret ANDROID_KEYSTORE_BASE64 is missing."
exit 1
fi
echo "$ANDROID_KEYSTORE_BASE64" | base64 --decode > "$RUNNER_TEMP/upload-keystore.jks"
echo "ANDROID_KEYSTORE_PATH=$RUNNER_TEMP/upload-keystore.jks" >> "$GITHUB_ENV"
- name: Build signed app bundle
env:
ANDROID_KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }}
ANDROID_KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }}
ANDROID_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }}
run: flutter build appbundle --release
- name: Upload to Google Play (internal testing)
uses: r0adkll/upload-google-play@v1
with:
serviceAccountJsonPlainText: ${{ secrets.PLAY_SERVICE_ACCOUNT_JSON }}
packageName: com.voorbeeld.mijn_app
releaseFiles: src/mijn_app/build/app/outputs/bundle/release/app-release.aab
mappingFile: src/mijn_app/build/app/outputs/mapping/release/mapping.txt
track: internal
status: completed
releaseName: ${{ github.ref_name }} mappingFilegeeft Play de R8-mapping, zodat crashrapporten leesbaar zijn. Bestaat het bestand niet (R8 uit), laat de regel weg.- Releasenotities ("Wat is er nieuw"): voeg
whatsNewDirectory: <map>toe met een bestandwhatsnew-en-US(max. 500 tekens) per taal.
Een release maken (zonder release-please):
# 1. versie in pubspec.yaml verhogen, bv. 1.0.1+2, committen en mergen
# 2. tag maken en pushen
git tag v1.0.1
git push origin v1.0.1 Volg de run in GitHub → Actions → Release. Na ±10 minuten staat de build in de Play Console onder Intern testen.
11. Optioneel: versies automatisch met release-please
release-please leest je commitberichten (Conventional Commits: feat:, fix:, …) en houdt een release-PR bij met de nieuwe versie in pubspec.yaml (buildnummer +1) en een CHANGELOG.md. Merge je die PR, dan maakt het een tag en een GitHub Release.
11.1 Configuratie
release-please-config.json in de root:
{
"$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json",
"include-component-in-tag": false,
"packages": {
"src/mijn_app": {
"release-type": "dart",
"package-name": "mijn_app",
"changelog-path": "CHANGELOG.md"
}
}
} .release-please-manifest.json met de huidige versie:
{ "src/mijn_app": "1.0.0" } Bestaat het project al met releases, voeg dan "bootstrap-sha": "<commit van de laatste release>" toe, anders neemt release-please de hele geschiedenis mee in de eerste changelog.
11.2 Workflow
⚠️ Tags die release-please met de standaard GITHUB_TOKEN maakt, starten geen andere workflows. De tag-trigger uit stap 10 werkt dan niet. Oplossing: bouw in dezelfde workflow, als job die alleen draait als er een release gemaakt is.
name: Release
on:
push:
branches: [master]
permissions:
contents: write
pull-requests: write
jobs:
release-please:
runs-on: ubuntu-latest
outputs:
release_created: ${{ steps.release.outputs['src/mijn_app--release_created'] }}
tag_name: ${{ steps.release.outputs['src/mijn_app--tag_name'] }}
version: ${{ steps.release.outputs['src/mijn_app--version'] }}
steps:
- uses: googleapis/release-please-action@v5
id: release
with:
config-file: release-please-config.json
manifest-file: .release-please-manifest.json
android:
needs: release-please
if: needs.release-please.outputs.release_created == 'true'
# … dezelfde stappen als in stap 10, met:
# releaseName: ${{ needs.release-please.outputs.version }} Staat het project in een submap, dan heten de outputs <pad>--release_created enz. Staat het in de root, dan gewoon release_created, tag_name en version.
11.3 GitHub-instellingen
- Settings → Actions → General → Allow GitHub Actions to create and approve pull requests aan.
- Settings → General → Pull Requests: alleen squash merging, Default to PR title aan. De PR-titel wordt dan de commit op
master, dus die moet Conventional Commits volgen. Controleer dat met action-semantic-pull-request. - ⚠️ Op een release-PR draaien geen CI-checks (zelfde
GITHUB_TOKEN-beperking). Met branch protection blijven verplichte checks dan op Expected — Waiting for status staan. Kies:- als admin mergen met "Merge without waiting for requirements to be met (bypass branch protections)"; laat daarvoor Do not allow bypassing the above settings uit, of
- een fine-grained personal access token of GitHub App als
token:voor release-please (checks draaien dan wel, maar de token moet je vernieuwen).
12. Testers en naar productie
12.1 Intern testen (max. 100 testers, geen review)
- Testen → Intern testen → Testers → e-maillijst met Google-accounts van de testers.
- Kopieer de link voor deelname en stuur die naar de testers.
- Tester opent de link op het toestel → Tester worden → installeert via de Play Store. (De eerste keer kan het enkele minuten duren voor de app zichtbaar is.)
12.2 Gesloten en open testen
- Gesloten testen: grotere groepen (e-maillijsten of Google Groups), met review van Google.
- Open testen: iedereen kan meedoen via de Play Store-pagina.
- Een release naar een andere track: open de release in Intern testen → Release promoten.
12.3 Nieuwe persoonlijke accounts: gesloten test verplicht
⚠️ Persoonlijke ontwikkelaarsaccounts die na november 2023 zijn aangemaakt, krijgen pas productietoegang na een gesloten test met minstens 12 testers die 14 dagen achter elkaar meedoen. Daarna vraag je productietoegang aan via het dashboard (met een korte vragenlijst). Details: Testvereisten voor nieuwe persoonlijke accounts.
12.4 Naar productie
- Testen → Intern testen → release → Release promoten → Productie.
- Kies landen (Productie → Landen/regio's) en eventueel een gefaseerde uitrol (bv. 20 %).
- Ter review verzenden → ⏳ review door Google (uren tot enkele dagen).
- Controleer na de release Android vitals en het pre-launch-rapport op crashes.
13. Problemen en oplossingen
| Melding / symptoom | Oorzaak | Oplossing |
|---|---|---|
| Upload via API: Package not found | App bestaat nog niet of had nog geen enkele upload | Stap 5: app aanmaken en eerste bundle met de hand uploaden |
| The caller does not have permission | Service account mist app-rechten, of rechten nog niet actief | Stap 7.2 controleren; ⏳ tot 36 uur wachten |
| Version code X has already been used | Buildnummer niet verhoogd | pubspec.yaml: getal na + verhogen |
| You uploaded an APK or Android App Bundle that was signed in debug mode | Geen upload-key gevonden tijdens de build | Secrets (stap 8) of key.properties controleren |
| signed with the wrong key | Andere keystore dan bij de eerste upload | Juiste keystore gebruiken; kwijt → upload-key resetten via de Play Console (App-integriteit) |
| Release-workflow start niet na een release-please-tag | Tags via GITHUB_TOKEN triggeren geen workflows | Bouwen in dezelfde workflow (stap 11.2) |
| Release-PR blijft geblokkeerd door branch protection | Geen checks op PR's van GITHUB_TOKEN | Als admin bypassen of een PAT/GitHub App (stap 11.3) |
| Workflow faalt: secret missing | Secret niet aangemaakt vóór de eerste release | Secret aanmaken → Actions → run → Re-run failed jobs |
| Play: targetSdkVersion te laag | Nieuwe jaarlijkse eis van Google | targetSdk verhogen (stap 3.1) |
| Play: ondersteuning voor 16 KB-paginagrootte ontbreekt | Oude NDK/AGP of native libraries | Recente Flutter, AGP en NDK; controleer in App-bundleverkenner |
Windows: flutter build vraagt symlink-ondersteuning | Ontwikkelaarsmodus uit | start ms-settings:developers → Ontwikkelaarsmodus aan |
| Kotlin-compilatie faalt met different roots | Project en Pub-cache op een andere schijf (bv. D: en C:) | kotlin.incremental=false in android/gradle.properties |
| Gradle: Unsupported class file major version | Te nieuwe of te oude JDK voor de Gradle-versie | Gradle/AGP bijwerken of de JDK van Android Studio gebruiken |
| Geen advertenties in de EU | Geen GDPR-bericht in AdMob of geen UMP-consent | Stap 3.4 punt 5 |
14. Korte checklist
Eenmalig per account (bij een volgende app overslaan):
- [ ] Google Play Console-account 💶, identiteit geverifieerd ⏳
- [ ] Google Cloud-project met Google Play Android Developer API
- [ ] Service account + JSON-sleutel bewaard
- [ ] (AdMob) account en
app-ads.txtop je website
Per app:
- [ ] Application ID, naam, privacybeleid-URL gekozen
- [ ]
build.gradle.kts: namespace, applicationId, targetSdk, signing viakey.propertiesof env - [ ] Iconen, appnaam, privacylink in de app
- [ ] (AdMob) app-ID in het manifest, test-ID's in debug, UMP-consent, GDPR-bericht
- [ ] Upload-keystore aangemaakt + back-up
- [ ] App aangemaakt in de Play Console + eerste AAB met de hand naar Intern testen
- [ ] App-content en store-listing ingevuld
- [ ] Service account uitgenodigd met app-rechten
- [ ] GitHub-secrets (5 stuks)
- [ ]
ci.yml+ branch protection - [ ]
release.yml(tag-trigger of release-please) - [ ] Eerste automatische release staat in Intern testen
- [ ] Testers uitgenodigd, getest, gepromoot naar productie
Vragen of liep je ergens anders vast? Laat het me gerust weten, dan vul ik deze handleiding aan.