--- name: mobile-apk-flutter-build description: "Flutter-APK auf headless VPS bauen: Setup + AGP-Pitfalls." version: 1.0.0 author: mehlzoerwer platforms: [linux] --- # Mobile APK bauen (Flutter, headless Linux-VPS) Klasse: Eine Flutter-App als Android-APK auf einem headless Linux-VPS bauen und zum Download bereitstellen. Verifiziert 2026-08-07 für PersoEnt (VPS netcup, Ubuntu 24.04). Hardware-Notiz: `flutter doctor` meldet "Android Studio not installed" und fehlendes Chrome/Linux-Toolchain — **beide sind für APK-Build nicht nötig**. Android toolchain ✓ reicht. ## 1. Flutter SDK installieren ```bash cd /opt curl -sL -o /tmp/flutter.tar.xz "https://storage.googleapis.com/flutter_infra_release/releases/stable/linux/flutter_linux_3.24.5-stable.tar.xz" tar xf /tmp/flutter.tar.xz # → /opt/flutter echo 'export PATH="$PATH:/opt/flutter/bin"' >> /root/.bashrc export PATH="$PATH:/opt/flutter/bin" flutter --version # prüft Dart-Version (3.5.x) ``` ## 2. 🔥 git safe.directory — DER "0.0.0-unknown"-Fix (PITFALL) Wenn `flutter --version` / `flutter pub` meldet **"Flutter SDK version 0.0.0-unknown"** oder `fatal: detected dubious ownership in repository at '/opt/flutter'`, dann ist Flutters git-Versionserkennung gebrochen. Fix: ```bash git config --global --add safe.directory /opt/flutter ``` Ohne diesen Schritt schlägt **jeder** `pub get` an Flutters eigener Versionsprüfung fehl ("The current Flutter SDK version is 0.0.0-unknown ... version solving failed"). Das ist NICHT ein echtes Versionsproblem — nur ownership. Nach dem Fix zeigt `flutter --version` wieder `3.24.5 • stable`. ## 3. Android SDK + Java ```bash apt-get install -y openjdk-17-jdk unzip # cmdline-tools: mkdir -p /opt/android-sdk/cmdline-tools curl -sL -o /tmp/cmdtools.zip "https://dl.google.com/android/repository/commandlinetools-linux-11076708_latest.zip" unzip -q cmdtools.zip -d /opt/android-sdk/cmdline-tools/ mv /opt/android-sdk/cmdline-tools/cmdline-tools /opt/android-sdk/cmdline-tools/latest # 'latest' zwingend! export ANDROID_HOME=/opt/android-sdk export PATH="$PATH:/opt/android-sdk/cmdline-tools/latest/bin" yes | sdkmanager --licenses sdkmanager "platforms;android-34" "build-tools;34.0.0" "platform-tools" ``` Gradle lädt fehlende Build-Tools (z.B. 33.0.1) automatisch nach. Android-Studio NICHT nötig. ## 4. Projekt anlegen + Dependencies ```bash flutter create --platforms=android --org de.persoent --project-name persoent_app persoent_app cd persoent_app ``` Statt manuelle Versionen in pubspec zu erraten → `flutter pub add ` (löst passende Version selbst, inkl. `:^0.0.0` bei 0.x-Paketen): ```bash flutter pub add pocketbase google_sign_in http shared_preferences ``` ## 5. 🔥 AGP `namespace`-Konflikt mit älteren Plugins (PITFALL) Symptom: `flutter build apk` → *"Could not create ... LibraryVariantBuilderImpl. > Namespace not specified. Specify a namespace in the module's build file."* (im Plugin, z.B. `:health_connect`). Ursache: AGP 8.1 (im Projekt, `android/settings.gradle`) **erzwingt `namespace`**, aber ältere Plugin-Pakete (z.B. `health_connect ^0.x`) haben keinen. Optionen: - **Einfachster robuster Weg:** das brechende Plugin `flutter pub remove ` **vorerst entfernen**, das Grundgerüst (Login+Dashboard) bauen → **erst funktionierendes APK, dann Health-Integration gezielt lösen**. - Oder AGP 8.1 → 7.3 zurückstufen (matcht Flutters Gradle), riskant. **Bau-Strategie (empfohlen):** erst minimales APK, das kompiliert; native Plugins (Health Connect usw.) danach einzeln, um Fehlerursachen zu isolieren. ## 5b. 🔥 `health_connect` auf pub.dev ist ein LEERES 0.0.0-Paket (PITFALL) Symptom: `flutter pub add health_connect` liefert `health_connect-0.0.0` (im `~/.pub-cache/.../health_connect-0.0.0` nur ein leeres Verzeichnis), und der Build bricht mit dem AGP-`namespace`-Fehler. Fakt: **Es existiert KEIN offizielles, funktionsfähiges `health_connect`-Flutter-Paket auf pub.dev.** Googles Health-Connect-Integration ist ein **GitHub-Beispiel**, das das Android-SDK nativ bundelt — nicht als veröffentlichtes pub-Paket gepflegt. `health_connect`/`health_connect_android` auf pub.dev sind leere Platzhalter. Konsequenz für PersoEnt: Google-Gesundheitsdaten sind NICHT über ein magisches pub.dev-Paket abrufbar. Der echte Weg erfordert nativen Android-Health-Connect-Code (SDK-Binding) — das skizzierte der Nutzer als langfristige "PersoEnt als App"-Entscheidung; vorerst liefert die APK Login+Dashboard, Health-Import ist separat zu bauen. Siehe auch `references/persoent-google-health-strategy.md` (Google-Fit-REST deprecated, Health Connect nur Android-nativ, kein Server-REST-Sync). ## 6. Gate + Build ```bash flutter analyze # vorher: fängt unbenutzte Imports / falsche Klassennamen flutter build apk --release ``` - APK: `build/app/outputs/flutter-apk/app-release.apk` (~22MB) - Erster Gradle-Build dauert lange (Downloads). Nutze langen Timeout. - `test/widget_test.dart` muss zum neuen Code passen (Default referenziert `MyApp`). ## 7. Zum Download bereitstellen (VPS) ```bash mkdir -p /var/www//html/app cp build/app/outputs/flutter-apk/app-release.apk /var/www//html/app/-Android.apk chown -R www-data:www-data /var/www//html/app/ # Test: curl -s -o /dev/null -w "%{http_code}" "https:///app/-Android.apk" # 200 ``` ## 8. 🔥 Google-Sign-In Fehlercode 10 (PITFALL) Symptom in der App (Nutzerscreenshot): `PlatformException(sign_in_failed, [google_sign_in] 10: null, null)`. Fehlercode **10 = OAuth-Client-Konfiguration fehlt/unverifiziert**: - Dem Google-Cloud-Projekt fehlt der **Android-OAuth-Client**, oder - der **SHA-1-Fingerprint** der App ist dort nicht hinterlegt. Fix (Nutzer-Einbindung nötig, nur deren Konto kann das): 1. SHA-1 aus der gebauten APK extrahieren und eintragen: ```bash keytool -printcert -jarfile app-release.apk | grep SHA1 ``` 2. Google-Cloud → Credentials → OAuth-Client "Android" mit diesem SHA-1 anlegen. 3. Die generierte **Client-ID** in `android/app/src/main/AndroidManifest.xml` als ``-Block eintragen, APK neu bauen. ## 9. 📸 Nutzer-Screenshots auswerten (Handy-Foto) ohne vision-Tool Wenn der Nutzer einen Screenshot schickt und **kein vision_analyze-Tool** verfügbar ist, lässt sich der Text (Fehlermeldungen) per **Tesseract-OCR** auslesen: ```bash tesseract /root/.hermes/cache/images/.jpg - 2>/dev/null # Text auf stdout ``` Weg: `browser_get_images` liefert keine lokalen Dateien → stattdessen `file ` (Maße bestätigen) + `tesseract` zum Extrahieren von Fehlertext. Liefert z.B. `PlatformException(...)`-Zeilen — genug, um die Diagnose (Code 10, etc.) zu stellen. Hinweis: tesseract ist auf Ubuntu oft vorinstalliert (`/usr/bin/tesseract`). ## Referenzen - `references/persoent-apk-2026-08-07.md` — Session-Transcript: voller Verlauf, Versionen, Fehlerbilder - `references/persoent-google-health-strategy.md` — PersoEnt: Google-Gesundheitsdaten-Strategie (Fit-REST deprecated, Health-Connect nur nativ; Detektiv-Korrelations-Analytik) - `references/persoent-cost-tracking.md` — **PersoEnt/Mesh-Kosten-Tracking + Budget-BREMSE (OpenRouter)**: mesh-cost-track.sh (stündlich, je Agent) + mesh-cost-break.py (kill_switches bei Deckel) + Agent-Poller-Guard (PAUSED → keine Task-Übernahme), E2E-getestet. PITFALLs: DREI OpenRouter-Kostenquellen unterscheiden (`/auth/key usage`≠Credits, `/credits total_usage`=USD verbindlich, Body-`usage.cost`); Usage KUMULATIV → **tag-übergreifendes Delta** (letzter-heute − letzter-gestern, max(0,·) gegen Reset), sonst endlos PAUSED; DeepSeek V4 Pro = 64% (Bremsen auf Modell-Ebene via `hermes config set model.providers.openrouter.blocked_models`, Singular `model.`); Master-exempt von PAUSED. **FINALER NUTZER-COST-MODEL (ab 08-08 ersetzt Monats-Cap): 1$/Tag/Agent hart, solange Credits da, KEIN Team-Monats-Stopp, Guthaben-0→Stopp.** - `references/hermes-config-ops.md` — **Hermes-System-Operation (VPS/Master)**: Config NIE via patch — immer `hermes config set`; `approvals.mode=auto` (allowlist bleibt hart); **BACKUP-ONLY-Regel** (`.bak` vor jeder Änderung → keine extra Freigabe); Memory-Limits (4400/2750, verdoppelt); **„Wir, nicht du" Team-Pflicht** bei Kernsystemen.