# Cahier des Charges — Application Utilisateur Mobile & Web ## Projet : Laverie Connectée — App Flutter **Version :** 1.0 **Date :** 2026-06-27 **Stack principale :** Flutter 3.x (Android / iOS / Web) --- ## 1. Contexte & Objectifs ### 1.1 Contexte Application grand public permettant aux utilisateurs de : - Localiser les laveries disponibles - Gérer leur porte-monnaie électronique - Réserver un créneau machine - Déclencher un lavage par QR code - Suivre leurs lavages et consulter leur historique ### 1.2 Cibles plateformes | Plateforme | Version minimale | Distribution | |------------|-----------------|--------------| | Android | 8.0 (API 26) | Google Play Store | | iOS | 15.0 | Apple App Store | | Web | Chrome 100+, Safari 15+, Firefox 100+ | PWA hébergée | ### 1.3 Contraintes - L'app doit rester **utilisable hors-ligne** pour les fonctions de consultation (solde en cache, historique) - Respect strict du **RGPD** : collecte minimale, consentements explicites, droit à l'effacement - L'application doit rester **connectée en arrière-plan** pour recevoir les notifications push --- ## 2. Stack Technique | Composant | Technologie | Version | Justification | |-----------|-------------|---------|---------------| | Framework UI | Flutter | 3.x (stable) | Codebase unique Android/iOS/Web | | Langage | Dart | 3.x | Null-safety, typage fort | | State management | Riverpod | 2.x | Réactivité, testabilité, pas de BuildContext dependency | | Navigation | GoRouter | 13.x | Deep links, routing déclaratif | | HTTP Client | Dio | 5.x | Intercepteurs JWT, retry automatique | | Cache local | Hive | 2.x | Clé-valeur rapide, offline-first | | Auth persistence | flutter_secure_storage | 9.x | Stockage sécurisé tokens (Keychain / Keystore) | | Notifications push | firebase_messaging | 15.x | FCM Android/Web + APNs iOS | | QR Code scan | mobile_scanner | 5.x | Caméra native, performant | | Paiement in-app | flutter_stripe | 10.x | Stripe SDK officiel | | Géolocalisation | geolocator | 13.x | Localisation établissements | | Cartes | flutter_map + OpenStreetMap | — | Affichage carte sans coût API Google | | Internationalisation | flutter_localizations | (Flutter) | fr / en au minimum | | Tests | flutter_test + Mockito | — | Unit + widget + integration tests | | CI/CD | GitHub Actions + Fastlane | — | Build, test, déploiement stores | | Monitoring | Firebase Crashlytics | — | Crashes prod | | Analytics | Firebase Analytics | — | Entonnoirs, rétention | --- ## 3. Architecture de l'Application ### 3.1 Pattern architectural : Clean Architecture + Feature-first ``` lib/ ├── core/ │ ├── api/ │ │ ├── api_client.dart # Instance Dio configurée │ │ ├── interceptors/ │ │ │ ├── auth_interceptor.dart # Injection Bearer token │ │ │ └── retry_interceptor.dart # Retry sur 401 avec refresh │ │ └── api_exception.dart │ ├── cache/ │ │ └── hive_service.dart # Abstraction cache local │ ├── router/ │ │ └── app_router.dart # GoRouter centralisé │ ├── theme/ │ │ ├── app_theme.dart │ │ └── app_colors.dart │ └── utils/ │ ├── currency_formatter.dart │ └── date_formatter.dart │ ├── features/ │ ├── auth/ │ │ ├── data/ │ │ │ ├── auth_repository.dart │ │ │ └── models/auth_token.dart │ │ ├── domain/ │ │ │ └── providers/auth_provider.dart │ │ └── presentation/ │ │ ├── login_screen.dart │ │ └── register_screen.dart │ │ │ ├── wallet/ │ │ ├── data/ │ │ ├── domain/ │ │ └── presentation/ │ │ ├── wallet_screen.dart │ │ ├── transaction_history_screen.dart │ │ └── top_up_screen.dart │ │ │ ├── establishments/ │ │ ├── data/ │ │ ├── domain/ │ │ └── presentation/ │ │ ├── map_screen.dart │ │ ├── establishment_list_screen.dart │ │ └── establishment_detail_screen.dart │ │ │ ├── machines/ │ │ ├── data/ │ │ ├── domain/ │ │ └── presentation/ │ │ ├── machine_card_widget.dart │ │ └── machine_detail_screen.dart │ │ │ ├── booking/ │ │ ├── data/ │ │ ├── domain/ │ │ └── presentation/ │ │ ├── slot_picker_screen.dart │ │ ├── booking_confirm_screen.dart │ │ └── my_bookings_screen.dart │ │ │ ├── wash/ │ │ ├── data/ │ │ ├── domain/ │ │ └── presentation/ │ │ ├── qr_scanner_screen.dart │ │ ├── wash_confirm_screen.dart │ │ └── wash_active_screen.dart │ │ │ ├── notifications/ │ │ └── notification_service.dart │ │ │ └── profile/ │ └── presentation/ │ ├── profile_screen.dart │ └── gdpr_settings_screen.dart │ └── main.dart ``` ### 3.2 Flux d'authentification ``` App start └── AuthProvider.check() ├── Token valide → HomeScreen ├── Token expiré → RefreshToken() │ ├── Succès → HomeScreen │ └── Échec → LoginScreen └── Pas de token → OnboardingScreen ``` ### 3.3 Gestion du token JWT - `AuthInterceptor` (Dio) : inject `Authorization: Bearer ` sur chaque requête - Sur 401 : appel automatique à `/auth/refresh` → retry de la requête originale - Tokens stockés dans `flutter_secure_storage` (Keychain iOS / Keystore Android) - Sur Web : stockage en `sessionStorage` chiffré (pas de `localStorage` pour les tokens) --- ## 4. Écrans & Parcours Utilisateur ### 4.1 Navigation principale (Bottom Navigation Bar) ``` ┌─────────────────────────────────────────────┐ │ 🗺 Carte │ 💰 Wallet │ 📅 Réservations │ 👤 Profil │ └─────────────────────────────────────────────┘ ``` ### 4.2 Onboarding & Inscription **Écran 1 — Bienvenue** - Logo + baseline - CTA : "Se connecter" / "Créer un compte" **Écran 2 — Inscription (multi-étapes)** - Étape 1 : Email + mot de passe (+ confirmation) - Étape 2 : Prénom, nom, téléphone, date de naissance - Étape 3 : Consentements RGPD (cases à cocher individuelles, non pré-cochées) - Traitement des données (obligatoire) - Communications marketing (optionnel) - Analyse d'utilisation (optionnel) - Étape 4 : Code parrainage (optionnel) - Confirmation email requise avant accès complet **Règles de validation :** - Email : format RFC 5322 - Mot de passe : 8 caractères minimum, 1 majuscule, 1 chiffre - Téléphone : format E.164 (`+33XXXXXXXXX`) - Date de naissance : 16 ans minimum ### 4.3 Carte & Établissements **Écran Carte (`MapScreen`)** - Carte OpenStreetMap centrée sur la position utilisateur - Marqueurs pour chaque établissement (couleur selon disponibilité) - 🟢 Machines disponibles - 🟡 Toutes occupées, créneaux libres - 🔴 Toutes occupées, aucun créneau proche - Tap sur marqueur → sheet inférieure avec résumé - Bouton liste (switch vue liste / carte) **Écran Détail Établissement** - Nom, adresse, horaires - Liste des machines avec statut temps réel (polling 30s ou WebSocket si dispo) - Pour chaque machine : type, statut, prochain créneau libre, tarif actuel - CTA : "Réserver" ou "Scanner & Lancer" ### 4.4 Porte-monnaie **Écran Wallet** - Solde affiché en grand (avec animation de mise à jour) - Bouton "Recharger" bien visible - Historique des 10 dernières transactions - Lien "Voir tout" **Écran Historique** - Liste paginée (infinite scroll) - Filtres : Type (débit/crédit), Période - Chaque entrée : icône type, montant coloré, source, date/heure, solde après - Export CSV (optionnel, v2) **Parcours Rechargement** 1. Saisie du montant (suggestions : 10€, 20€, 50€) 2. Sélection du fournisseur de paiement (si plusieurs disponibles) 3. Redirection vers Stripe Checkout (WebView sécurisée) ou SDK natif 4. Retour sur l'app via deep link `laverie://wallet/topup-result` 5. Confirmation animée + mise à jour du solde 6. Notification push de confirmation **Contraintes :** - Montant minimum : 5€, maximum : 150€ par rechargement - Affichage du solde en cache si hors-ligne ### 4.5 Réservation d'un Créneau **Écran Sélection de Créneau (`SlotPickerScreen`)** - Sélecteur de date (calendrier scrollable, 14 jours maximum) - Pour la date sélectionnée : grille horaire avec créneaux libres/occupés - Mise en évidence du prix par créneau (heure creuse / heure pleine) - Information : "Supplément réservation : +1.00€" **Écran Confirmation Réservation** - Récapitulatif : machine, date/heure, durée, prix total - Solde actuel et solde après débit - Mention pénalité no-show - Bouton "Confirmer et débiter X.XX€" **Écran Mes Réservations** - Onglets : À venir / Passées - Chaque réservation : statut badge, machine, créneau, montant - Actions possibles : "Annuler", "Déplacer" (si > 2h) - Countdown pour les réservations imminentes **Règles métier UI :** - Créneau non réservable si solde insuffisant → CTA "Recharger d'abord" - Annulation : popup de confirmation avec montant remboursé affiché - Déplacement : ouvre SlotPickerScreen avec le même contexte machine ### 4.6 Déclenchement d'un Lavage par QR Code **Écran Scanner QR (`QrScannerScreen`)** - Vue caméra plein écran avec cadre de scan - Feedback visuel à la détection (vibration + flash vert) - Fallback : saisie manuelle du code machine **Flux post-scan :** 1. Identification de la machine via le QR 2. Appel API → vérification statut + tarif 3. Affichage récap : machine, programme, prix estimé, solde après 4. Bouton "Lancer le lavage" 5. Animation de confirmation (spinner → ✓) 6. Écran "Lavage en cours" avec countdown **Écran Lavage Actif (`WashActiveScreen`)** - Machine + établissement - Barre de progression avec temps restant (mise à jour polling API 30s) - Montant débité - Notification push à la fin du cycle ### 4.7 Notifications Push **Configuration :** - Demande de permission notifications à l'inscription (après onboarding) - Sur iOS : `UNUserNotificationCenter.requestAuthorization` - Sur Android 13+ : `POST_NOTIFICATIONS` permission - Token FCM/APNs envoyé à l'API à chaque démarrage de l'app (mise à jour si changé) **Gestion en foreground :** - Affichage d'une bannière in-app (snackbar stylisée) - Tap → navigation vers l'écran pertinent **Deep links depuis notification :** | Notification | Deep link | |---|---| | Rechargement confirmé | `laverie://wallet` | | Réservation confirmée | `laverie://bookings/{uuid}` | | Rappel créneau | `laverie://bookings/{uuid}` | | Lavage terminé | `laverie://washes/{uuid}` | | Promo disponible | `laverie://establishments/{uuid}` | ### 4.8 Profil & Paramètres **Écran Profil** - Informations personnelles (modifiables) - Mes statistiques : nombre de lavages, total dépensé, économies promos - Code parrainage personnel (copiable, partageable) - Gestion notifications (toggle par type) - Paramètres RGPD - Déconnexion **Écran Paramètres RGPD** - Visualisation des consentements donnés (avec date) - Toggle individuel pour chaque consentement - Bouton "Télécharger mes données" (export JSON, v2) - Bouton "Supprimer mon compte" (confirmation en deux étapes + délai 30 jours) --- ## 5. Gestion de l'État (Riverpod) ### 5.1 Providers principaux ```dart // Auth final authProvider = StateNotifierProvider // Wallet final walletProvider = FutureProvider final transactionsProvider = StateNotifierProvider // Establishments final establishmentsProvider = FutureProvider.family // by uuid final nearbyEstablishmentsProvider = FutureProvider> // Machines final machineStatusProvider = StreamProvider.family // polling 30s // Bookings final bookingsProvider = StateNotifierProvider final slotAvailabilityProvider = FutureProvider.family, SlotQuery> // Active wash final activeWashProvider = StreamProvider // null si aucun lavage en cours ``` ### 5.2 Stratégie offline-first (Hive) | Donnée | Cache | TTL | |--------|-------|-----| | Solde wallet | Oui | Jusqu'au prochain refresh | | Historique transactions | Oui (50 dernières) | 24h | | Établissements (liste) | Oui | 1h | | Disponibilités machines | Non | — | | Réservations | Oui | 30 min | En cas d'absence de réseau : affichage des données en cache avec badge "Données hors-ligne". --- ## 6. Sécurité ### 6.1 Stockage - Tokens JWT : `flutter_secure_storage` (AES-256 sur Android, Keychain sur iOS) - Aucune donnée sensible dans `SharedPreferences` - Sur Web : pas de localStorage, session uniquement ### 6.2 Réseau - Certificate pinning en production (via `dio` custom `HttpClient`) - Timeout : 10s connexion, 30s réception - Toutes les requêtes en HTTPS (TLS 1.3) ### 6.3 QR Code - Vérification côté serveur (le QR code seul ne suffit pas à déclencher un lavage) - Rate limiting API sur `/washes/start` : 3 req/min/user --- ## 7. Performance & UX - **Splash screen** natif (Android 12 Splash API, iOS UILaunchScreen) - **Skeleton loaders** sur toutes les listes et cartes pendant le chargement - **Optimistic UI** : affichage anticipé des mises à jour (ex: annulation réservation) - **Pull-to-refresh** sur toutes les listes - **Pagination infinie** sur l'historique des transactions - **Animations** : transitions de pages fluides (Hero, Fade), animation solde wallet --- ## 8. Tests | Type | Outil | Portée | |------|-------|--------| | Tests unitaires | `flutter_test` | Providers Riverpod, formatters, validators | | Tests widgets | `flutter_test` | Tous les écrans principaux | | Tests d'intégration | `integration_test` | Parcours critiques (inscription, rechargement, scan QR) | | Tests golden | `golden_toolkit` | Composants UI clés (cohérence visuelle) | **Parcours critiques à couvrir en intégration :** 1. Inscription complète → vérification email → connexion 2. Rechargement porte-monnaie (mock Stripe) 3. Réservation créneau → annulation → vérification remboursement 4. Scan QR → lancement lavage → fin de cycle --- ## 9. CI/CD ### 9.1 Pipeline GitHub Actions ```yaml # Sur chaque PR : - flutter analyze # Linting strict - flutter test # Tests unitaires + widgets - flutter build apk --release # Vérification build Android - flutter build ios --release # Vérification build iOS (macOS runner) - flutter build web # Vérification build Web # Sur merge main : - Fastlane → TestFlight (iOS) - Fastlane → Play Store Internal Track (Android) - Deploy Web → hébergement (Firebase Hosting ou VPS) ``` ### 9.2 Flavors | Flavor | API URL | Firebase projet | Build suffix | |--------|---------|-----------------|--------------| | dev | `http://localhost:8000` | laverie-dev | `.dev` | | staging | `https://api-staging.laverie.app` | laverie-staging | `.staging` | | production | `https://api.laverie.app` | laverie-prod | — | --- ## 10. Livrables attendus - [ ] Code source Flutter (GitHub, repository dédié) - [ ] Flavors configurés (dev / staging / prod) - [ ] `README.md` avec instructions setup complet - [ ] Fichiers de configuration Firebase (`google-services.json`, `GoogleService-Info.plist`) - [ ] Fichiers de signature Android (`.keystore`) documentés - [ ] Screenshots pour les stores (5 par plateforme minimum) - [ ] Suite de tests (couverture > 70% sur la logique métier) - [ ] Build release Android (`.aab`) + iOS (`.ipa`) prêts à soumettre