PHASE 3: Consumer Mobile App (Flutter)
Trigger: "Execute Phase 3 as per masterplan."
Goal: A functional Flutter mobile app for members: register, browse memberships, purchase, view payment history, and use QR code for check-in. 32-language support with language switcher including RTL.
Prerequisite: Phase 2 is completed.
Step 3.0 — Technical Conception
Create doc/developer/conception/phase-3-conception.md.
(reference: Chapter 09 — Frontend Strategy, Chapter 08 — User Journeys)
Content:
- Scope: Flutter mobile app, 6 screens (login, register, catalog, purchase, QR, profile)
- Component Design: Feature-based architecture with Riverpod providers
- API Integration: OpenAPI codegen vs. manual Dio client (decision + rationale)
- Offline Strategy: Token caching, QR code offline display, graceful degradation
- RTL Layout: Arabic layout verification checklist, Directionality widget
- Design Chapter References: Ch08 (Journey 1), Ch09
Result: Technical conception for Phase 3.
Step 3.1 — App Scaffold and Design System
(reference: Chapter 09 — Frontend Strategy, 10 UX/UI Design Principles)
- Architecture:
lib/core/,lib/features/,lib/shared/,lib/l10n/ - Design system: Material 3, MEMBERSHIP colors, dark mode, typography, components
- Riverpod, GoRouter, Dio with JWT interceptor
- Apply Gestalt Laws: proximity, similarity, continuity
Step 3.2 — Authentication Flow
(reference: Chapter 13 — Security, Chapter 08 — Journey 1)
Screens:
-
Login Screen: - Email + password fields, "Remember me" toggle, "Forgot password?" link - Biometric login (fingerprint/face) after initial login (flutter_secure_storage for token) - Error handling: invalid credentials, account locked (brute-force), email not verified - Auto-refresh: Dio interceptor detects 401, refreshes token via
/api/auth/refresh, retries request -
Registration Screen: - Fields: first name, last name, email, password (strength indicator), confirm password, accept terms checkbox - Validation: real-time email format, password min 8 chars + uppercase + number - On submit:
POST /api/auth/register→ success screen with "Check your email" message -
Email Verification: Deep link handler (
membership://verify?token=...), success → auto-login -
Forgot Password Flow: - Screen 1: enter email →
POST /api/auth/forgot-password- Screen 2: enter code (6-digit OTP from email) + new password →POST /api/auth/reset-password -
Token Storage: -
flutter_secure_storagefor JWT access token (15min) and refresh token (30 days) - Encrypted keychain (iOS) / encrypted shared preferences (Android) - Auto-logout on refresh token expiry -
Widget tests: login success, login error, registration validation, token refresh interceptor
Step 3.3 — Membership Browsing and Purchase
(reference: Chapter 08 — Journey 1: Member Registration and First Purchase)
Screens:
-
Catalog Screen: - Card list of available membership templates (from
GET /api/membership-templates/catalog/{entityId}) - Each card: template name, price, billing cycle, description (truncated), features list, "Select" button - Filter by category (if applicable), sort by price -
Membership Detail Screen: - Full description, features (checklist), price with VAT breakdown, terms (minimum duration, cancellation notice) - "Purchase" button → starts purchase flow
-
Purchase Flow (3-step wizard): - Step 1 — Bank Account: enter IBAN (auto-format), account holder, SEPA mandate consent checkbox, date auto-filled - Step 2 — Terms: display contract terms (scrollable), cancellation policy, "I agree" checkbox - Step 3 — Sign: digital signature (tap-to-sign), IP + user agent recorded, summary of what is being signed - Confirmation: success animation, contract PDF download link, "View your QR code" CTA
-
Widget tests: catalog loading, purchase wizard navigation, IBAN validation, signature recording
Step 3.4 — QR Code Access
(reference: Chapter 08 — Journey 13: QR Door Access, Chapter 12 — QR Access)
Screens:
-
QR Code Screen (bottom navigation item): - Full-screen QR code display (high contrast, max brightness auto-set) - QR content: encrypted JWT containing memberId, entityId, contractId, timestamp, hash - Time-limited: QR regenerates every 60 seconds (countdown timer shown) - Immutable after generation (security requirement: QR content cannot be modified by member) - Offline capable: last valid QR cached locally, valid for 5 minutes without network - Pull-to-refresh for manual regeneration
-
Backend validation endpoint:
POST /api/checkin/qr-validate- Decrypt QR token, verify signature, check: contract active, not suspended, access zone allowed - Return: GRANTED / DENIED + reason -
Widget tests: QR generation, countdown timer, offline fallback, brightness control
Step 3.5 — Payment History and Profile
(reference: Chapter 08 — Journey 1)
Screens:
-
Transaction History: - List view: date, description, amount (green = paid, red = overdue, gray = pending), status chip - Filter by date range, status - Tap → detail: full transaction info, linked contract, payment method, download invoice PDF
-
Profile Screen: - Personal data: name, email, phone, address, birthday, photo (camera/gallery picker) - Active contracts: list with status, renewal date - Bank accounts: list with IBAN (masked: DE89 * 3000), default indicator - Notification preferences: email on/off, push on/off, per category - Language selector (32 languages, native names, flag icons) - GDPR Actions:*
- "Export my data" button →
POST /api/members/me/data-export→ JSON download (Art. 20) - "Delete my account" button → confirmation dialog →
POST /api/members/me/deletion-request(Art. 17, 30-day grace period) - Logout, app version info
- "Export my data" button →
-
Widget tests: transaction list loading, profile edit, GDPR export trigger, language switch
Step 3.6 — Internationalization (i18n)
(reference: Chapter 09 — Frontend Strategy, Rule 11, Rule 18)
Approach:
- ARB Files: 32
app_{locale}.arbfiles inlib/l10n/: - EU (24): app_bg, app_hr, app_cs, app_da, app_nl, app_en, app_et, app_fi, app_fr, app_de, app_el, app_hu, app_ga, app_it, app_lv, app_lt, app_mt, app_pl, app_pt, app_ro, app_sk, app_sl, app_es, app_sv - Non-EU (8): app_tr, app_ar, app_ru, app_uk, app_sr, app_sq, app_zh, app_he - All strings viaAppLocalizations.of(context).key - Language Switcher:
- Bottom sheet with all 32 languages, displayed in native names (e.g., "Deutsch", "العربية", "עברית")
- Persist selection in shared_preferences + send to backend via
PUT /api/users/me/locale- BackendAccept-Languageheader for API error messages - RTL Support:
-
Directionalitywidget wraps app root, auto-detects from locale (Arabic, Hebrew) - All screens tested in RTL: login, catalog, purchase wizard, QR, transactions, profile - RTL-specific fixes: icon mirroring, text alignment, padding reversal, navigation direction - Date/Time/Currency Formatting:
-
intlpackageDateFormatper locale - Currency formatting:NumberFormat.currency(locale: locale, symbol: currency)- Relative dates: "2 days ago", "in 3 hours" per locale
Validation: flutter gen-l10n && flutter analyze && flutter test --dart-define=FORCE_RTL=true
Step 3.7 — Phase 3 Documentation
doc/enduser/consumer-manual.md(mobile app guide)doc/developer/frontend-guide.md(Flutter architecture)
Step 3.8 — Update Intranet
Run python doc/intranet/build.py.
Phase 3 — Quality Gate
| # | Check | Target |
|---|---|---|
| 1 | Conception | exists |
| 2 | flutter analyze |
0 issues |
| 3 | flutter test |
all pass |
| 4 | Full flow | register -> verify -> login -> browse -> purchase -> QR -> transactions |
| 5 | German switch | all UI text changes |
| 6 | Arabic switch | RTL layout correct |
| 7 | 32 ARB files | all keys present |
| 8 | Documentation | consumer-manual, frontend-guide exist |
| 9 | Intranet | regenerated |
| 10 | CLAUDE.md | updated |
Report: "Phase 3 completed."