# mobile/ — Flutter

Ce fichier complète le CLAUDE.md racine. Stack : Flutter (Dart 3 null-safe).
Deux profils dans la même app : voyageur et agent (gating par rôle après
login).

## Commandes

```bash
flutter pub get
flutter run
flutter test
flutter analyze
```

## Structure attendue

```
mobile/lib/
├── core/
│   ├── api_client.dart       # Dio/http configuré (baseURL, intercepteurs, auth header)
│   └── env.dart               # config par environnement (dev/prod)
├── models/                    # classes générées/maintenues depuis le contrat
│   ├── departure.dart
│   ├── booking.dart
│   └── ticket.dart
├── repositories/               # un repository par ressource métier
│   ├── departure_repository.dart
│   ├── booking_repository.dart
│   └── payment_repository.dart
├── features/
│   ├── traveler/               # recherche, réservation, paiement, mes billets
│   └── agent/                  # scan QR, validation embarquement
└── state/                      # Riverpod providers (ou ton choix de state mgmt)
```

## Conventions de code

- **Jamais d'appel HTTP direct dans un widget.** Tout passe par un
  repository dans `repositories/`, qui utilise `core/api_client.dart`.
- Les modèles (`models/`) reflètent exactement les champs de
  `../contracts/openapi.yaml` — `fromJson`/`toJson` explicites, pas de
  `dynamic` qui traîne jusqu'à l'UI.
- Gestion des montants : reçus en centimes FCFA depuis l'API, formatés en
  FCFA uniquement dans la couche de présentation.
- Séparation stricte des features `traveler/` et `agent/` : un widget agent
  ne doit jamais être accessible si le rôle de l'utilisateur connecté n'est
  pas `agent` (vérifié via le provider d'auth, pas par convention UI
  seulement).

## Offline & billet

- Le billet (QR + code SMS) doit être lisible **sans réseau** une fois
  acheté : persistance locale immédiate (ex. `sqflite` ou `hive`) dès
  confirmation du paiement, pas seulement tenu en mémoire.
- Le scan QR côté agent (`features/agent/`) doit fonctionner même si la
  validation serveur est temporairement indisponible : prévoir un état
  "scanné localement, à synchroniser" plutôt qu'un blocage total.

## Quand on te demande d'intégrer un nouvel endpoint

1. Vérifie le endpoint dans `../contracts/openapi.yaml`.
2. Crée/étends le modèle dans `models/` et le repository dans
   `repositories/`.
3. Branche-le dans le provider d'état puis le widget concerné, dans la
   feature `traveler/` ou `agent/` selon le cas.
4. Si le contrat n'existe pas encore pour cet endpoint, dis-le explicitement
   plutôt que d'inventer la forme de la réponse.
