Der Konsolen-Hinweis betrifft nur den nativen google_sign_in-Flow; unser Browser-Flow braucht kein SHA-1. Debug-SHA-1/SHA-256 trotzdem per firebase apps:android:sha:create registriert (für später, z. B. App Check). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
19 KiB
Firebase-Einrichtung — Schritt für Schritt
Firebase ist Googles „Backend als Dienst": Es liefert uns Login (Auth), Datenbank mit Echtzeit-Sync (Firestore), Foto-Speicher (Storage), serverseitigen Code (Cloud Functions) und Push-Nachrichten (FCM) — ohne dass wir einen eigenen Server betreiben.
Stand: Schritt 1–3 erledigt, Blaze aktiv (18.07.2026). Der Code für Firestore (Pflanzen, Stellplätze, Haushalte) und Login (E-Mail+Passwort) ist eingebaut — offen ist Schritt 4 (Firestore-Datenbank anlegen + E-Mail-Login aktivieren, machst du in der Console) und danach das Rules-Deployment. Dieses Dokument wird mit jedem Baustein erweitert.
Was wofür? (Überblick)
| Baustein | Aufgabe in LeafItToMe |
|---|---|
| Authentication | Login mit E-Mail+Passwort, Google und Apple |
| Cloud Firestore | Datenbank: Haushalte, Pflanzen, Stellplätze, Bestätigungen — synchronisiert in Echtzeit auf alle Geräte des Haushalts |
| Storage | Speichert die Pflanzen- und Stellplatz-Fotos |
| Cloud Functions | Unser Server-Code: ruft PlantNet/Claude auf (damit API-Keys nie in der App stecken) und verschickt die täglichen Erinnerungen |
| Cloud Messaging (FCM) | Stellt die Push-Nachrichten auf dem Sperrbildschirm zu |
Schritt 1: Firebase-Projekt anlegen (machst du, ~10 Minuten)
- Öffne https://console.firebase.google.com und melde dich mit deinem Google-Konto an.
- „Projekt hinzufügen" → Name:
leafittome→ Google Analytics kannst du deaktivieren (brauchen wir nicht). - Nach dem Anlegen bist du in der Projekt-Übersicht. Fertig für heute — Apps registrieren wir gemeinsam per CLI (Schritt 3).
Schritt 2: Auf den Blaze-Tarif umstellen (machst du, ~5 Minuten)
Cloud Functions (unser Server-Code) setzen den Blaze-Tarif („Pay as you go") voraus. Keine Sorge vor Kosten:
- Die großzügigen Gratis-Kontingente bleiben auch im Blaze-Tarif bestehen (z. B. 2 Mio. Function-Aufrufe/Monat, 50.000 Firestore-Lesezugriffe/Tag). Bei ~5 Nutzern bleiben wir weit darunter → real ~0 €/Monat.
- In der Console: Zahnrad → Nutzung und Abrechnung → Tarif ändern → Blaze → Kreditkarte hinterlegen.
Kostenschutz einrichten (wichtig, machen wir direkt mit):
- In der Google Cloud Console (https://console.cloud.google.com, gleiches Projekt) → Abrechnung → Budgets und Benachrichtigungen.
- Budget anlegen: z. B. 5 €/Monat, E-Mail-Alarm bei 50 %, 90 % und 100 %.
- So bekommst du eine Mail, lange bevor irgendetwas teuer wird.
Schritt 3: App mit Firebase verbinden (machen wir gemeinsam per CLI)
Wenn Schritt 1–2 erledigt sind, läuft das so (führe ich mit dir aus, hier zur Doku):
# Firebase-CLI installieren und einloggen (öffnet den Browser)
npm install -g firebase-tools
firebase login
# FlutterFire-CLI installieren
dart pub global activate flutterfire_cli
# Verbindet das Flutter-Projekt mit dem Firebase-Projekt:
# registriert die iOS- und Android-App und erzeugt lib/firebase_options.dart
flutterfire configure
flutterfire configure erzeugt lib/firebase_options.dart — darin stehen die Projekt-Kennungen (keine Geheimnisse, darf ins Git). Die eigentlichen API-Keys (Anthropic, PlantNet) leben nur in der Cloud-Functions-Umgebung, niemals in der App.
Was dabei konkret passiert ist (zum Nachvollziehen):
- In der Firebase Console tauchen unter Projekteinstellungen → Meine Apps jetzt zwei Apps auf (iOS und Android, jeweils
dev.leafittome.app). android/app/google-services.jsonundios/Runner/GoogleService-Info.plistsind die plattformspezifischen Verbindungsdateien;firebase.jsonmerkt sich die Zuordnung fürs CLI.- In den Android-Gradle-Dateien wurde das
google-services-Plugin eingetragen (verarbeitet die JSON-Datei beim Build). lib/main.dartruft beim StartFirebase.initializeApp(...)auf — ab jetzt können wir nach und nach Auth, Firestore, Storage und FCM andocken.
Schritt 4: Firestore und Login aktivieren (machst du, ~5 Minuten)
Der App-Code für Datenbank und Login ist fertig — zwei Schalter musst du in der Console einmalig umlegen:
- Firestore-Datenbank anlegen: Firebase Console → Build → Firestore Database → „Datenbank erstellen" → Standort
eur3(Europa) wählen → Produktionsmodus (die strengen Startregeln werden gleich durch unsere eigenen ersetzt). Das aktiviert auch die Firestore-API des Projekts. - E-Mail/Passwort-Login aktivieren: Firebase Console → Build → Authentication → „Jetzt starten" → Tab Sign-in method → E-Mail/Passwort aktivieren. (Google- und Apple-Login rüsten wir in einem späteren Block nach — Apple braucht den Developer Account.)
Danach werden die Sicherheitsregeln aus dem Repo deployt (macht Claude per CLI):
firebase deploy --only firestore --project leaf-it-to-me-app
Das Datenmodell und die Sicherheitsregeln (zum Verständnis)
users/{uid} → E-Mail, householdId (= aktiver Haushalt), fcmTokens, reminderTime
(nur der Nutzer selbst)
households/{id} → Name, ownerUid (Besitzer), memberUids,
members {uid → Rolle+E-Mail}
households/{id}/plants/{id} → Pflanze: Art, Intervalle, lastWatered(+By), ...
households/{id}/locations/{id} → Stellplatz: Name
invites/{code} → Einladung: householdId, Rolle; nur Erstellen erlaubt,
Einlösen ausschließlich über die Function joinHousehold
Rollen (V2): member = volle Rechte, sitter = Pflanzen-Sitter (sieht alles, darf aber nur Aufgaben bestätigen — die Rules erlauben Sittern an Pflanzen ausschließlich Änderungen der Bestätigungs-Felder). Der Beitritt per Einladungscode läuft über die Cloud Function joinHousehold, weil der Beitretende laut Rules noch kein Mitglied ist.
Mehrere Haushalte (V2.1): Ein Nutzer kann Mitglied beliebig vieler Haushalte sein — maßgeblich ist, in welchen memberUids er steht. users/{uid}.householdId zeigt nur, welcher Haushalt in der App gerade aktiv ist; gewechselt wird im Haushalts-Screen unter „Meine Haushalte“ oder über den Wechsler oben im Menü (die App fragt die eigenen Haushalte per where('memberUids', arrayContains: uid) ab — das erlauben die bestehenden Rules bereits). Beim Beitritt per Code wechselt die App in den neuen Haushalt, alle bisherigen Mitgliedschaften bleiben bestehen. Die tägliche Sammel-Push (sendDailyReminders) und die Tages-Checkliste sammeln fällige Aufgaben aus allen Haushalten des Nutzers (in der Checkliste mit Zwischenüberschrift je Haushalt).
Besitzer, Austreten, Entfernen (V2.2): Jeder Haushalt hat einen Besitzer (ownerUid = Ersteller; Alt-Haushalte ohne das Feld: erster Eintrag in memberUids, denn Beitretende werden hinten angehängt). Der Besitzer wird in der App ausgewiesen und kann als Einziger Mitglieder entfernen; er kann seinen eigenen Haushalt nicht verlassen. Umbenennen dürfen alle vollen Mitglieder — die Rules erlauben Clients am Haushalts-Dokument seitdem nur noch das Namensfeld; jede Mitglieder-Verwaltung läuft über Cloud Functions mit Admin-Rechten:
leaveHousehold— selbst austreten (auch als Sitter, der laut Rules das Dokument nicht ändern dürfte)removeMember— ein Mitglied entfernen (nur der Besitzer)
Beide biegen, falls nötig, den aktiven Zeiger (users/{uid}.householdId) des Betroffenen auf einen seiner übrigen Haushalte um; bleibt keiner übrig, legen sie ihm einen frischen eigenen Haushalt an — so steht die App nie ohne Haushalt da.
E-Mail-Nachtrag für Alt-Haushalte: Haushalte aus der V1-Zeit haben für ihren Ersteller keinen Eintrag in der members-Map (die kam erst mit V2) — die Besitzer-Anzeige blieb dadurch leer. Die App trägt deshalb beim Start die eigene E-Mail in allen eigenen Haushalten nach (memberEntrySyncProvider). Die Rules erlauben dafür eine eng begrenzte Ausnahme: Jedes Mitglied (auch Sitter) darf genau seinen eigenen members-Eintrag ändern, die eigene Rolle aber nicht — sonst könnte sich ein Sitter selbst zum Mitglied befördern. Bis der Betroffene seine App einmal geöffnet hat, zeigen andere Geräte „?“ statt der E-Mail.
Die Regeln in firestore.rules setzen das Haushalts-Prinzip durch:
- Dein Nutzerprofil (
users/{uid}) kannst nur du selbst lesen/schreiben. - Einen Haushalt sieht und ändert nur, wer in dessen
memberUidssteht. Anlegen darf man einen Haushalt nur, wenn man sich dabei selbst als Mitglied einträgt (passiert automatisch bei der Registrierung). - Alle Unterdaten (Pflanzen, Stellplätze) erben diese Regel: voller Zugriff für Mitglieder, für niemanden sonst. V2 (Sitter einladen) wird damit nur „weitere UID in
memberUidsaufnehmen".
Wichtig zu verstehen: Die App spricht direkt mit Firestore — die Regeln laufen auf Googles Servern und sind die eigentliche Zugriffskontrolle. Selbst eine manipulierte App könnte fremde Haushalte nicht lesen.
Schritt 5: Foto-Erkennung — Storage, Functions und API-Keys
Der Code ist fertig: Beim Anlegen einer Pflanze kannst du fotografieren, die Cloud Function identifyPlant bestimmt die Art (PlantNet, bei Unsicherheit Claude als Zweitmeinung) und Claude erstellt das deutsche Pflegeprofil samt Intervall-Vorschlägen. Das Foto landet in Firebase Storage im Haushalts-Ordner.
Was du einmalig tun musst:
- Storage aktivieren: Firebase Console → Build → Storage → „Jetzt starten" → Standort
eur3(falls gefragt) → Produktionsmodus. (Die Zugriffsregeln kommen danach per Deploy ausstorage.rules— gleiches Haushalts-Prinzip wie bei Firestore.) - PlantNet-API-Key holen (kostenlos): Auf https://my.plantnet.org registrieren → unter Settings/API den Key kopieren. Free-Tier: 500 Erkennungen/Tag — mehr als genug.
- Beide API-Keys als Secrets hinterlegen (im Terminal, jeweils Key einfügen, Enter):
firebase functions:secrets:set PLANTNET_API_KEY --project leaf-it-to-me-app
firebase functions:secrets:set ANTHROPIC_API_KEY --project leaf-it-to-me-app
Die Keys liegen damit im Google Secret Manager — verschlüsselt, nur die Cloud Function kann sie lesen, sie tauchen nie im Code, im Git oder in der App auf.
Danach (macht Claude): firebase deploy --only functions,storage — deployt die Function nach europe-west3 (Frankfurt) und die Storage-Regeln.
Kosten: PlantNet kostenlos; Claude ca. 1–3 Cent pro Foto (Erkennungs-Fallback + Pflegeprofil); Functions/Storage bei eurer Nutzung im Gratis-Kontingent. Der Budget-Alarm aus Schritt 2 überwacht alles.
Schritt 6: Push-Erinnerungen (FCM + geplante Function)
So funktioniert es: Die App registriert nach dem Login das Gerät bei Firebase Cloud Messaging und speichert den Geräte-Token, die Zeitzone und die Erinnerungszeit im Nutzer-Dokument. Die geplante Function sendDailyReminders läuft alle 15 Minuten, prüft für jeden Nutzer „ist gerade seine Erinnerungszeit erreicht und gibt es offene Aufgaben?" und schickt dann eine Sammel-Push pro Tag („3 Pflanzen brauchen dich heute 🌱 — Gießen: Monstera, Orchidee · Düngen: Bogenhanf"). Ungültig gewordene Tokens (Gerät gewechselt, App gelöscht) werden automatisch aufgeräumt. Kosten: ~2.900 Läufe/Monat, tief im Gratis-Kontingent.
Android: funktioniert sofort — beim ersten App-Start nach diesem Update fragt die App die Benachrichtigungs-Berechtigung ab, fertig.
iOS braucht den Apple Developer Account (Push ist auf iOS ohne bezahlten Account nicht möglich). ✅ Erledigt am 2026-07-20 — iOS-Push ist eingerichtet und läuft auf dem echten Gerät. So wurde es gemacht:
- APNs-Schlüssel erzeugen: https://developer.apple.com → Certificates, Identifiers & Profiles → Keys → „+" → Namen vergeben, Apple Push Notifications service (APNs) ankreuzen → Continue → Register → .p8-Datei herunterladen (nur einmal möglich!) und die Key ID notieren; die Team ID steht oben rechts im Account.
- In Firebase hinterlegen: Firebase Console → Projekteinstellungen (Zahnrad) → Cloud Messaging → Abschnitt „Apple-App-Konfiguration" → APNs-Authentifizierungsschlüssel hochladen (.p8 + Key ID + Team ID).
- Push-Capability in Xcode:
ios/Runner.xcworkspaceöffnen → Ziel Runner → Signing & Capabilities → „+ Capability" → Push Notifications hinzufügen (und einmal Background Modes → Remote notifications anhaken). Dafür muss als Team der Developer Account gewählt sein.
Danach einmal neu bauen (flutter run) — die Erinnerungen landen dann auch auf dem iPhone-Sperrbildschirm.
Stolperfalle (teuer erkauft): „Push Notifications" fehlte in Xcode unter „+ Capability", obwohl „Background Modes" da war. Ursache: Es war noch das alte kostenlose „Personal Team" als Signing-Team gewählt — Gratis-Teams dürfen kein Push, deshalb blendet Xcode den Eintrag komplett aus. Fix: In Xcode → Settings → Accounts die Apple-ID mit der bezahlten Mitgliedschaft hinzufügen und in Signing & Capabilities das Team ohne den Zusatz
(Personal Team)wählen. Danach erscheint „Push Notifications" in der Liste.
Schritt 7: Anmelden mit Apple (iOS + Android)
So funktioniert es: Der Login-Screen hat neben E-Mail/Passwort einen Button „Mit Apple anmelden". Auf iOS öffnet sich der native Apple-Dialog, auf Android ein Browser-Fenster, das über den Firebase-Auth-Handler zu Apple und wieder zurück führt — beides über den in firebase_auth eingebauten AppleAuthProvider (kein Zusatz-Paket). Beim ersten Apple-Login legt die App automatisch Profil-Dokument und eigenen Haushalt an (wie bei der normalen Registrierung); bei „E-Mail verbergen“ wird die anonyme Apple-Relay-Adresse gespeichert. Der Code liegt in AuthRepository.signInWithApple().
Am Code ist nichts mehr zu tun — die folgende Konfiguration in Apple-Portal, Firebase-Konsole und Xcode macht den Button funktionsfähig:
-
App-ID-Capability (iOS): In Xcode (
ios/Runner.xcworkspace) → Runner → Signing & Capabilities → „+ Capability" → Sign in with Apple hinzufügen. Das Entitlementcom.apple.developer.applesigninist im Repo schon gesetzt; der Xcode-Schritt aktiviert die Capability zusätzlich an der App-IDdev.leafittome.appund frischt das Provisioning-Profil auf (mit „Automatically manage signing"). -
Services-ID (für Android/Web): https://developer.apple.com → Identifiers → „+" → Services IDs → z. B. Beschreibung „LeafItToMe Sign-In", Identifier
dev.leafittome.signin(frei wählbar, aber ≠ Bundle-ID). Nach dem Anlegen die Services-ID öffnen → Sign In with Apple aktivieren → Configure:- Primary App ID:
dev.leafittome.app - Domains and Subdomains:
leaf-it-to-me-app.firebaseapp.com - Return URLs:
https://leaf-it-to-me-app.firebaseapp.com/__/auth/handler
- Primary App ID:
-
Sign-in-Schlüssel (.p8): Keys → „+" → Sign in with Apple ankreuzen → Configure → Primary App ID
dev.leafittome.app→ Register → .p8 herunterladen (nur einmal möglich!) und Key ID notieren. (Eigener Schlüssel, nicht der APNs-Schlüssel aus Schritt 6.) -
Firebase-Konsole: Authentication → Sign-in method → Apple aktivieren. Für den Android/Web-Flow den Abschnitt „OAuth-Codeflow konfigurieren" ausfüllen:
- Services ID:
dev.leafittome.signin - Apple Team ID: (steht oben rechts im Developer-Account)
- Key ID + Inhalt der .p8-Datei
- Services ID:
Danach neu bauen (flutter run) und den Apple-Button auf iPhone und Android-Gerät testen. Erststart legt einen frischen Haushalt an — der wird in households sichtbar.
Hinweis: Apple liefert Name/E-Mail nur beim allerersten Login an eine App. Zum erneuten Testen des Erst-Login-Flows die App unter https://appleid.apple.com → Anmelden mit Apple → für „LeafItToMe" die Freigabe entfernen; beim nächsten Login fragt Apple wieder nach den Daten.
Schritt 8: Anmelden mit Google (iOS + Android)
So funktioniert es: Der Login-Screen hat unter dem Apple-Button einen Button „Mit Google anmelden". Auf beiden Plattformen öffnet sich ein Browser-Fenster, das über den Firebase-Auth-Handler zu Google und wieder zurück führt — über den in firebase_auth eingebauten GoogleAuthProvider, genau wie beim Apple-Login auf Android (kein Zusatz-Paket, kein google_sign_in). Beim ersten Google-Login legt der reaktive Haushalts-Bootstrap automatisch Profil + Haushalt an. Der Code liegt in AuthRepository.signInWithGoogle().
Am Code ist nichts mehr zu tun — im Repo sind Button, Fehlerbehandlung und das iOS-URL-Scheme (Encoded App ID in ios/Runner/Info.plist, der Rückweg aus dem Browser in die App) schon eingerichtet. Es fehlt nur ein Konsolen-Schritt:
- Firebase-Konsole: Authentication → Sign-in method → Google aktivieren. Dabei den öffentlichen App-Namen prüfen und als Support-E-Mail deine Adresse auswählen → Speichern. (Firebase legt die nötigen OAuth-Clients selbst an.)
Danach neu bauen (flutter run) und den Google-Button auf iPhone und Android testen — der erste Login mit einem Google-Konto muss kurz „Dein Haushalt wird eingerichtet …" zeigen und danach normal funktionieren.
Hinweise:
- Anders als bei Apple ist kein Entwickler-Portal, kein Schlüssel und keine Services-ID nötig — der eine Konsolen-Schalter genügt.
- Beim Aktivieren zeigt die Konsole den Hinweis „SHA-1-Releasefingerabdruck angeben". Der gilt für den nativen Google-Flow (
google_sign_in-Paket) — unser Browser-Flow braucht ihn nicht. Die Debug-Fingerabdrücke (SHA-1 + SHA-256 aus~/.android/debug.keystore) sind trotzdem per CLI im Projekt hinterlegt (firebase apps:android:sha:create …), damit später nichts fehlt (z. B. App Check). Vor einem App-Store-/Play-Store-Release muss zusätzlich der Fingerabdruck des Release-Schlüssels hinterlegt werden — den gibt es aber noch nicht.- Die
GoogleService-Info.plist/google-services.jsonmüssen nicht neu heruntergeladen werden — der Flow läuft über den Auth-Handler im Web, nicht über das native Google-SDK.
Schritt 9 und folgende (kommen mit den nächsten Blöcken)
- App Check — optionale Härtung vor dem App-Store-Release: bestätigt per Play Integrity (Android) / App Attest (iOS), dass Anfragen aus der echten App kommen. Bis dahin ist die Log-Warnung „No AppCheckProvider installed" harmlos und kann ignoriert werden — die Zugriffskontrolle leisten Login + Security Rules.
Begriffe kurz erklärt
- Projekt: Der Container für alles — eine App = ein Firebase-Projekt.
- Firestore-Dokument/Collection: Firestore ist keine Tabellen-Datenbank, sondern speichert JSON-artige Dokumente in Collections (Ordnern), z. B.
households/abc123/plants/xyz789. - Security Rules: Regeln auf dem Server, die festlegen, wer welche Dokumente lesen/schreiben darf — unser Schutz, obwohl die App direkt mit der Datenbank spricht.
- Cloud Function: Eine Funktion (bei uns TypeScript), die bei Ereignissen oder nach Zeitplan auf Google-Servern läuft.