- Configuration complète Stripe pour les 3 environnements (DEV/REC/PROD) * DEV: Clés TEST Pierre (mode test) * REC: Clés TEST Client (mode test) * PROD: Clés LIVE Client (mode live) - Ajout de la gestion des bases de données immeubles/bâtiments * Configuration buildings_database pour DEV/REC/PROD * Service BuildingService pour enrichissement des adresses - Optimisations pages et améliorations ergonomie - Mises à jour des dépendances Composer - Nettoyage des fichiers obsolètes 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
12 KiB
TODO - Cache Web pour tuiles Mapbox
Contexte
Problème : En PROD, les admins travaillent intensivement sur la plateforme web et la carte. Actuellement, les tuiles Mapbox ne sont pas mises en cache sur le web, ce qui provoque :
- Téléchargements répétés des mêmes tuiles
- Consommation excessive de bande passante
- Navigation peu fluide
- Coûts API Mapbox potentiellement élevés
Cause : Mapbox envoie des headers Cache-Control: no-cache qui empêchent le cache navigateur par défaut, et flutter_map_cache ne supporte pas la plateforme web.
Solution : Implémenter un cache manuel des tuiles via IndexedDB en Dart, uniquement pour le web.
Architecture technique
Flux de données
flutter_map demande une tuile
↓
CachedWebTileProvider (nouveau)
↓
WebTileCacheService.getTile()
↓
Vérifie IndexedDB
↓
┌─────────────┴─────────────┐
│ │
Cache HIT Cache MISS
│ │
Retourne image Télécharge depuis Mapbox
depuis IndexedDB ↓
Stocke dans IndexedDB
↓
Retourne image
Composants
-
WebTileCacheService (Singleton)
- Gère IndexedDB pour stocker les tuiles
- Méthodes :
getTile(),storeTile(),clearCache(),clearExpiredTiles() - Durée de cache : 30 jours (aligné avec mobile/desktop)
-
CachedWebTileProvider (Custom TileProvider)
- Implémente
TileProviderdeflutter_map - Utilise
WebTileCacheServicepour récupérer/stocker - Fallback sur téléchargement direct si erreur
- Implémente
-
MapboxMap (Modifié)
- Utilise
CachedWebTileProvidersur web - Garde
HiveCacheStoresur mobile/desktop
- Utilise
Fichiers à créer/modifier
✅ Fichiers à créer
1. lib/core/services/web_tile_cache_service.dart
Service singleton pour gérer le cache IndexedDB des tuiles Mapbox.
Responsabilités :
- Initialiser la base IndexedDB
mapbox_tiles_cache - Stocker les tuiles avec clé unique (URL de la tuile)
- Récupérer les tuiles depuis le cache
- Gérer l'expiration (30 jours)
- Nettoyer les tuiles expirées
Structure IndexedDB :
Database: mapbox_tiles_cache
ObjectStore: tiles
- key: String (URL de la tuile)
- value: Object {
data: Uint8List (bytes de l'image)
timestamp: int (millisecondsSinceEpoch)
}
API publique :
class WebTileCacheService {
static final instance = WebTileCacheService._();
Future<void> initialize();
Future<Uint8List?> getTile(String url);
Future<void> storeTile(String url, Uint8List data);
Future<void> clearCache();
Future<void> clearExpiredTiles();
}
Dépendances :
dart:indexed_db(natif web)dart:typed_datapackage:flutter/foundation.dart(kIsWeb)
2. lib/presentation/widgets/cached_web_tile_provider.dart
TileProvider custom qui utilise le cache IndexedDB.
Responsabilités :
- Implémenter
TileProviderdeflutter_map - Vérifier le cache avant téléchargement
- Télécharger et stocker si absent du cache
- Fallback sur comportement par défaut si erreur
API publique :
class CachedWebTileProvider extends TileProvider {
final String accessToken;
final String styleId;
CachedWebTileProvider({
required this.accessToken,
required this.styleId,
});
@override
ImageProvider getImage(TileCoordinates coordinates, TileLayer options);
}
Logique :
- Construire l'URL de la tuile
- Appeler
WebTileCacheService.getTile(url) - Si trouvée →
MemoryImage(cachedData) - Sinon → Télécharger → Stocker →
MemoryImage(downloadedData)
Dépendances :
flutter_mapWebTileCacheServicedart:typed_datapackage:http/http.dart(ouDiosi préféré)
✅ Fichiers à modifier
3. lib/presentation/widgets/mapbox_map.dart
Modifier pour utiliser le nouveau provider sur web.
Modifications :
- Lignes 96-139 : Refactoriser
_initializeCache() - Utiliser
CachedWebTileProviderquandkIsWeb == true - Garder
HiveCacheStorepour mobile/desktop - Initialiser
WebTileCacheServiceau démarrage sur web
Avant (lignes 98-103) :
if (kIsWeb) {
// Pas de cache sur Web (non supporté)
setState(() {
_cacheInitialized = true;
});
return;
}
Après :
if (kIsWeb) {
// Cache manuel via IndexedDB
await WebTileCacheService.instance.initialize();
setState(() {
_cacheInitialized = true;
});
return;
}
TileLayer config (ligne ~180) :
TileLayer(
urlTemplate: 'https://api.mapbox.com/styles/v1/mapbox/$styleId/tiles/256/{z}/{x}/{y}@2x?access_token=$accessToken',
tileProvider: kIsWeb
? CachedWebTileProvider(
accessToken: accessToken,
styleId: styleId,
)
: (_cacheStore != null
? CachedTileProvider(store: _cacheStore!)
: NetworkTileProvider()),
// ...
),
Étapes d'implémentation
Phase 1 : Service de cache IndexedDB
Tâche 1.1 : Créer web_tile_cache_service.dart
- Créer la classe singleton
- Implémenter
initialize()avec création de la DB IndexedDB - Implémenter
getTile(url)avec vérification d'expiration - Implémenter
storeTile(url, data)avec timestamp - Implémenter
clearCache()pour vider toute la DB - Implémenter
clearExpiredTiles()pour le nettoyage automatique - Ajouter des logs de debug (
debugPrint)
Tâche 1.2 : Gestion des erreurs
- Try/catch sur toutes les opérations IndexedDB
- Fallback gracieux si IndexedDB indisponible
- Logs d'erreur explicites
Tâche 1.3 : Tests unitaires
- Tester
initialize()crée bien la DB - Tester
storeTile()+getTile()roundtrip - Tester expiration (mock timestamp)
- Tester
clearCache()
Phase 2 : TileProvider custom
Tâche 2.1 : Créer cached_web_tile_provider.dart
- Créer la classe qui extend
TileProvider - Implémenter
getImage()avec logique de cache - Gérer le téléchargement des tuiles manquantes
- Stocker les tuiles téléchargées dans le cache
- Ajouter des logs de performance (cache hit/miss)
Tâche 2.2 : Gestion des erreurs réseau
- Try/catch sur téléchargement
- Retry logic (3 tentatives max)
- Placeholder si échec total
- Logs d'erreur explicites
Tâche 2.3 : Tests
- Tester avec tuile en cache → Pas de requête réseau
- Tester avec tuile absente → Téléchargement + stockage
- Tester avec erreur réseau → Fallback gracieux
Phase 3 : Intégration dans MapboxMap
Tâche 3.1 : Modifier mapbox_map.dart
- Importer
CachedWebTileProvideretWebTileCacheService - Modifier
_initializeCache()pour initialiser le service web - Modifier
TileLayerpour utiliser le bon provider selon la plateforme - Tester sur web que le nouveau provider est bien utilisé
Tâche 3.2 : Validation visuelle
- Lancer l'app en mode web DEV
- Ouvrir DevTools → Console pour voir les logs
- Naviguer sur la carte → Vérifier logs "Cache MISS" au premier passage
- Revenir sur la même zone → Vérifier logs "Cache HIT"
- Vérifier dans DevTools → Application → IndexedDB que les tuiles sont stockées
Tâche 3.3 : Tests de performance
- Mesurer le temps de chargement initial (sans cache)
- Mesurer le temps de chargement avec cache
- Vérifier qu'il n'y a plus de requêtes réseau pour les tuiles en cache
- Tester sur une session longue (plusieurs déplacements sur la carte)
Phase 4 : Nettoyage et documentation
Tâche 4.1 : Nettoyage automatique
- Appeler
clearExpiredTiles()au démarrage de l'app (dansinitialize()) - Ajouter un bouton admin pour vider manuellement le cache (optionnel)
Tâche 4.2 : Logs et monitoring
- Ajouter des statistiques de cache (hit rate, taille totale)
- Logger les performances (temps de chargement moyen)
Tâche 4.3 : Documentation
- Commenter le code (dartdoc)
- Mettre à jour
FLOW-*.mdsi nécessaire - Ajouter section dans README technique
Tests de validation
Tests fonctionnels
-
Cache vide → Premier chargement
- Ouvrir l'app web en mode incognito
- Naviguer sur la carte
- Vérifier que les tuiles se chargent
- Vérifier les logs "Cache MISS" + "Storing tile"
-
Cache rempli → Rechargement
- Recharger la page (F5)
- Revenir sur la même zone de carte
- Vérifier que les tuiles se chargent instantanément
- Vérifier les logs "Cache HIT"
- Vérifier dans Network tab : pas de requêtes Mapbox pour les tuiles en cache
-
Navigation étendue
- Se déplacer sur plusieurs zones de la carte
- Revenir sur les zones précédentes
- Vérifier mix de "Cache HIT" et "Cache MISS"
-
Gestion de l'expiration
- Modifier temporairement l'expiration à 10 secondes (pour test)
- Charger des tuiles
- Attendre 15 secondes
- Recharger → Vérifier que les tuiles sont re-téléchargées
-
Gestion d'erreur réseau
- Désactiver le réseau en plein chargement
- Vérifier que les tuiles en cache s'affichent quand même
- Vérifier que les tuiles manquantes ne bloquent pas l'app
Tests de performance
-
Mesure de bande passante
- Vider le cache
- Naviguer sur une zone avec ~100 tuiles visibles
- Noter la bande passante consommée (DevTools Network)
- Recharger la page et revenir sur la même zone
- Vérifier que la bande passante est ~0 KB
-
Mesure de vitesse
- Mesurer le temps de chargement initial : ____ ms
- Mesurer le temps de chargement avec cache : ____ ms
- Objectif : >50% de réduction
Tests multi-plateformes
-
Mobile/Desktop non régressé
- Lancer sur Android → Vérifier que HiveCacheStore fonctionne toujours
- Lancer sur iOS → Vérifier que HiveCacheStore fonctionne toujours
- Vérifier les logs "Cache initialized with HiveCacheStore"
-
Web multi-navigateurs
- Tester sur Chrome
- Tester sur Firefox
- Tester sur Safari (si IndexedDB supporté)
- Tester sur Edge
Checklist de validation finale
- Aucune nouvelle dépendance ajoutée au
pubspec.yaml - Code compatible web uniquement (guards
kIsWeb) - Mobile/Desktop non impactés (toujours HiveCacheStore)
- Logs de debug présents et clairs
- Gestion d'erreur complète (try/catch)
- Expiration automatique (30 jours)
- Performance mesurée et améliorée
- Tests manuels passés
- Code documenté (dartdoc)
- Pas de régression sur les autres plateformes
Notes techniques
Pourquoi IndexedDB et pas localStorage ?
- localStorage : Limite de 5-10 MB → Insuffisant pour des tuiles (1 tuile = ~60 KB)
- IndexedDB : Limite de ~50% de l'espace disque disponible → Largement suffisant
Structure de la clé de cache
Format recommandé : {styleId}_{z}_{x}_{y}@{scale}x
Exemple : streets-v11_15_16234_11378@2x
Cela permet :
- Identification unique de chaque tuile
- Support multi-styles (streets, satellite, etc.)
- Support multi-résolutions (@1x, @2x)
Taille estimée du cache
- 1 tuile Mapbox @2x : ~60 KB
- Carte complète niveau zoom 15 : ~500 tuiles
- Total pour une zone : ~30 MB
- IndexedDB peut stocker plusieurs GB → Pas de souci
Maintenance du cache
Le nettoyage automatique (clearExpiredTiles()) s'exécute :
- Au démarrage de l'app
- Évite l'accumulation de vieilles tuiles
- Garde le cache sous contrôle
Dépendances
Packages Flutter (déjà présents)
flutter_map: ^7.0.2→ FournitTileProviderflutter_map_cache: ^1.5.1→ Pour mobile/desktop uniquement
Bibliothèques Dart natives (aucun ajout)
dart:indexed_db→ Pour IndexedDB (web seulement)dart:typed_data→ Pour manipuler les bytespackage:flutter/foundation.dart→ PourkIsWeb
Pas de nouvelle dépendance externe ✅
Estimation
- Temps de développement : 2-3 heures
- Complexité : Moyenne
- Risque : Faible (fallback sur comportement actuel)
- Gain : Important pour les admins PROD
Prochaine étape
Attente de validation utilisateur pour démarrer l'implémentation.