Fix: BootstrapGate als Overlay statt Baum-Austausch (markNeedsBuild during build)
Das Umschalten vom Warte-Bildschirm auf die App remountete den ganzen App-Baum, während Riverpod noch Provider-Abhängigkeiten nachzog — das löste die 'setState() or markNeedsBuild() called during build'-Warnung aus. Der Splash liegt jetzt als deckende Stack-Ebene über der durchgehend gemounteten App. Handoff: Bug vom Nutzer als behoben bestätigt. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
0cc39f69b1
commit
51a304e986
2 changed files with 28 additions and 18 deletions
|
|
@ -27,27 +27,22 @@ Private Flutter-App (iOS+Android) zur Pflanzenpflege für Haushalt + Pflanzen-Si
|
|||
|
||||
## Offen / Nächstes
|
||||
|
||||
### 🟡 Apple-Login-Bug: Fix implementiert, wartet auf Gerätetest durch den Nutzer
|
||||
### ✅ Apple-Login-Bug behoben und vom Nutzer bestätigt (2026-07-23)
|
||||
|
||||
**Der Bug:** Mit einem neuen Apple-Account schlug das Anlegen einer Pflanze fehl („Speichern fehlgeschlagen"). Ursache (per Code-Analyse bestätigt, Firestore-Sichtung stand aus): Der Haushalts-Bootstrap hing an `signInWithApple` **nach** `signInWithProvider` — der Router leitet aber schon bei „angemeldet" weiter (redirect prüft nur `currentUser != null`). Wirft der Bootstrap oder wird die App vorher beendet, bleibt der Nutzer dauerhaft ohne `users`-Doc + Haushalt, und die (korrekten) Rules lehnen jeden Schreibzugriff ab.
|
||||
**Der Bug in Kurzform:** Mit einem neuen Apple-Account schlug jedes Speichern fehl. Zwei Ursachen-Schichten:
|
||||
1. Der Haushalts-Bootstrap hing an `signInWithApple` — der Router leitet aber schon bei „angemeldet" weiter; scheiterte der Bootstrap danach, blieb der Account dauerhaft ohne Haushalt (Rules lehnen dann korrekt jeden Write ab).
|
||||
2. **Die eigentliche Falle:** Die Push-Registrierung (`_saveToken`, `set(..., merge: true)`) hatte das `users`-Dokument beim ersten Apple-Login längst angelegt (fcmTokens/timezone, aber ohne `householdId`). Eine Reparatur, die nur auf „users-Doc existiert" prüft, hielt den Account deshalb fälschlich für fertig.
|
||||
|
||||
**Der Fix (2026-07-23 implementiert, Option „reaktiver Bootstrap"):**
|
||||
- `householdBootstrapProvider` (`lib/features/auth/data/household_bootstrap_provider.dart`): beobachtet das `users/{uid}`-Dokument; bei „angemeldet, aber kein Profil" legt er Profil + Haushalt an. Deckt **alle** Login-Wege ab (auch künftig Google).
|
||||
- `BootstrapGate` (`lib/features/auth/presentation/bootstrap_gate.dart`, eingehängt in `app.dart` builder): hält die App zurück, bis das Profil existiert; zeigt Warte-Bildschirm bzw. Fehler mit „Erneut versuchen".
|
||||
- `AuthRepository`: Bootstrap jetzt öffentlich (`ensureHouseholdBootstrap`, idempotent + In-Flight-Schutz); `signInWithApple` ruft ihn nicht mehr selbst auf.
|
||||
- **Selbstheilend:** Der bereits kaputte Apple-Account wird beim nächsten App-Start automatisch repariert (Gate sieht „kein Profil" → legt Haushalt an).
|
||||
- Analyzer grün, alle 9 Tests grün (neuer Test: „Erster Login ohne Profil → Haushalt wird reaktiv angelegt").
|
||||
|
||||
**Nachbesserung (gleicher Tag, nach erstem Fehlversuch des Nutzers):** Mit dem Gate-Build schlug Speichern **weiter** fehl, und zwar ohne dass der Einrichtungs-Bildschirm je erschien (mit und ohne Foto). Befund: Die **Push-Registrierung** (`_saveToken`, `set(..., merge: true)`) hatte das `users`-Dokument beim ersten Apple-Login längst angelegt (fcmTokens/timezone, aber ohne `householdId`) — Gate und Bootstrap prüften nur die **Dokument-Existenz** und hielten den Account deshalb für fertig. Fix: Überall ist jetzt **`householdId` vorhanden** der Maßstab (Gate, Bootstrap-Provider, `_bootstrapHouseholdIfNeeded`), und der Bootstrap schreibt das users-Dokument mit **merge**, damit Push-Felder erhalten bleiben. Außerdem zeigt die Speichern-Fehlermeldung jetzt den konkreten Firebase-Fehlercode an (plugin/code statt Generik). Neuer Test stellt exakt den kaputten Zustand nach (users-Doc nur mit fcmTokens) — alle 10 Tests grün.
|
||||
|
||||
**Was der Nutzer jetzt testen sollte:**
|
||||
1. App auf dem iPhone neu bauen/starten, mit dem **bestehenden Apple-Account** anmelden → jetzt sollte kurz „Dein Haushalt wird eingerichtet …" erscheinen (diesmal wirklich, da das Gate jetzt auf `householdId` prüft), danach Pflanze-Anlegen mit und ohne Foto.
|
||||
2. Optional Härtetest „echter Erst-Login": unter <https://appleid.apple.com> die Freigabe für „LeafItToMe" entfernen → nächster Login ist wieder „erster Login" → Ende-zu-Ende inkl. Pflanze mit Foto.
|
||||
3. Falls es **weiter** fehlschlägt: Die Fehlermeldung nennt jetzt den Code — `firebase_storage/…` heißt Foto-Upload/Storage-Rules/IAM, `cloud_firestore/…` heißt Plant-Write/Firestore-Rules. Den Code melden, dann gezielt weiter.
|
||||
**Der Fix (alles committet und gepusht):**
|
||||
- `householdBootstrapProvider` (`lib/features/auth/data/household_bootstrap_provider.dart`): reaktiver Bootstrap — legt Profil + Haushalt an, sobald „angemeldet, aber **kein `householdId`** im users-Doc". Maßstab ist überall `householdId`, nie die Dokument-Existenz. Schreibt per **merge** (Push-Felder bleiben erhalten). Deckt alle Login-Wege ab (auch künftig Google).
|
||||
- `BootstrapGate` (`lib/features/auth/presentation/bootstrap_gate.dart`, in `app.dart` eingehängt): legt bis dahin einen deckenden Warte-Bildschirm **über** die App (Stack, kein Austausch des App-Baums — ein Remount mitten im Provider-Update löste „markNeedsBuild during build" aus); Fehler werden mit „Erneut versuchen" sichtbar.
|
||||
- `AuthRepository`: `ensureHouseholdBootstrap` öffentlich, idempotent, In-Flight-Schutz; `signInWithApple` ruft ihn nicht mehr selbst auf. Selbstheilend für bereits kaputte Accounts.
|
||||
- Speicher-Fehler in `plant_form_screen` zeigen jetzt den Firebase-Fehlercode (plugin/code) statt einer Generik.
|
||||
- **Vom Nutzer auf dem iPhone bestätigt:** Anmeldung + Pflanze anlegen funktionieren mit dem Apple-Account.
|
||||
|
||||
### Zuletzt erledigt
|
||||
|
||||
- **2026-07-23 (2):** Reaktiver Haushalts-Bootstrap + BootstrapGate implementiert (Fix für den Apple-Login-Bug, siehe oben); Architektur-Doku um die Entscheidung ergänzt.
|
||||
- **2026-07-23 (2):** Apple-Login-Bug diagnostiziert und in zwei Schritten behoben (reaktiver Bootstrap + householdId-Maßstab, siehe oben); Riverpod-„markNeedsBuild during build"-Warnung per Overlay-Gate beseitigt; Architektur-Doku um die Bootstrap-Entscheidung ergänzt.
|
||||
- **2026-07-23 (1):** Apple-Login **implementiert und committet** (`signInWithApple` via eingebautem `AppleAuthProvider`, „oder"-Trenner + Button im Login, iOS-Entitlement `com.apple.developer.applesignin`, l10n-Strings, Doku `firebase-einrichtung.md` Schritt 7). Konfiguration vom Nutzer erledigt, Login funktioniert.
|
||||
- **2026-07-20:** iOS-Push eingerichtet (Apple Developer Account freigeschaltet, APNs/Firebase/Xcode). V1 damit auch auf iOS vollständig.
|
||||
|
||||
|
|
|
|||
|
|
@ -6,10 +6,12 @@ import '../../../l10n/generated/app_localizations.dart';
|
|||
import '../data/household_bootstrap_provider.dart';
|
||||
|
||||
/// Hält die App zurück, solange zum angemeldeten Nutzer noch kein
|
||||
/// users-Dokument (und damit kein Haushalt) existiert.
|
||||
/// Haushalt existiert.
|
||||
///
|
||||
/// Beim ersten Login (z. B. mit Apple) legt [householdBootstrapProvider]
|
||||
/// beides an; bis dahin gibt es einen Warte-Bildschirm. Schlägt der
|
||||
/// Profil + Haushalt an; bis dahin liegt ein deckender Warte-Bildschirm
|
||||
/// ÜBER der App (Stack statt Austausch — ein Remount des App-Baums mitten
|
||||
/// im Provider-Update löst „markNeedsBuild during build“ aus). Schlägt der
|
||||
/// Bootstrap fehl, wird der Fehler hier sichtbar — vorher konnte man in
|
||||
/// der App landen, deren Schreibzugriffe dann alle an den Rules scheiterten.
|
||||
class BootstrapGate extends ConsumerWidget {
|
||||
|
|
@ -30,6 +32,19 @@ class BootstrapGate extends ConsumerWidget {
|
|||
profile.data()?['householdId'] != null;
|
||||
if (profileReady) return child;
|
||||
|
||||
return Stack(
|
||||
children: [
|
||||
child,
|
||||
_BootstrapSplash(),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Deckender Warte-/Fehler-Bildschirm, solange der Bootstrap läuft.
|
||||
class _BootstrapSplash extends ConsumerWidget {
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final bootstrap = ref.watch(householdBootstrapProvider);
|
||||
final l10n = AppLocalizations.of(context);
|
||||
final theme = Theme.of(context);
|
||||
|
|
|
|||
Loading…
Reference in a new issue