🚀 Guide: Système de Force Update
📋 Vue d'ensemble
Ce système permet de forcer les utilisateurs à mettre à jour l'application en cas de bug critique ou breaking change. L'utilisateur sera bloqué jusqu'à ce qu'il installe la nouvelle version.
🏗️ Architecture
main.dart
└── VersionCheckWrapper (vérifie au démarrage)
├── Si update requis → ForceUpdatePage (écran de blocage)
└── Si OK → MyApp (application normale)
Composants créés:
core/models/app_version.dart- Modèle de donnéescore/services/version_service.dart- Service de vérificationcore/presentation/pages/force_update_page.dart- Écran de blocagecore/presentation/pages/version_check_wrapper.dart- Wrapper de checksupabase/migrations/20250112_create_app_version_config.sql- Table BDD
🎯 Comment utiliser
1. Exécuter la migration Supabase
# Via Supabase CLI
supabase migration up
# Ou copier/coller le SQL dans le dashboard Supabase
2. Configurer les URLs des stores
Dans la table app_version_config, mettez à jour les URLs:
UPDATE app_version_config
SET android_url = 'https://play.google.com/store/apps/details?id=VOTRE_PACKAGE_ID',
ios_url = 'https://apps.apple.com/app/idVOTRE_APP_ID'
WHERE platform = 'android' OR platform = 'ios';
3. Forcer une mise à jour (exemple)
Scénario: Vous avez un bug critique en v1.0.0, vous sortez v1.0.1 en urgence.
-- 1. Désactiver l'ancienne config
UPDATE app_version_config
SET is_active = false
WHERE platform = 'android' AND version = '1.0.0';
-- 2. Créer nouvelle config qui force la mise à jour
INSERT INTO app_version_config (
platform,
version,
build_number,
min_required_version,
min_required_build_number,
force_update,
update_message,
android_url,
is_active
) VALUES (
'android',
'1.0.1', -- Version actuelle disponible
2, -- Build number actuel
'1.0.1', -- Version minimale requise
2, -- Build number minimal
true, -- ⚠️ FORCE UPDATE ACTIVÉ
'⚠️ Mise à jour critique : Un bug majeur a été corrigé. Veuillez mettre à jour immédiatement.',
'https://play.google.com/store/apps/details?id=VOTRE_PACKAGE_ID',
true
);
Résultat: Tous les utilisateurs en v1.0.0 seront bloqués et devront installer v1.0.1.
📊 Cas d'usage
✅ Cas 1: Mise à jour recommandée (non forcée)
UPDATE app_version_config
SET force_update = false,
update_message = 'Une nouvelle version est disponible avec de nouvelles fonctionnalités!'
WHERE platform = 'android';
Comportement: L'utilisateur peut continuer à utiliser l'app normalement.
✅ Cas 2: Mise à jour forcée (bug critique)
UPDATE app_version_config
SET force_update = true,
min_required_version = '1.0.2',
min_required_build_number = 3,
update_message = '🚨 Mise à jour de sécurité obligatoire'
WHERE platform = 'android';
Comportement: Utilisateurs en v < 1.0.2 sont bloqués jusqu'à mise à jour.
✅ Cas 3: Breaking change API
-- Exemple: Vous changez l'API backend et les anciennes versions ne sont plus compatibles
UPDATE app_version_config
SET force_update = true,
min_required_version = '2.0.0',
min_required_build_number = 10,
update_message = 'Mise à jour requise pour compatibilité avec nos nouveaux serveurs'
WHERE platform = 'android';
🔍 Comment ça marche
Flow technique:
-
Au démarrage de l'app:
VersionCheckWrapper
→ VersionService.checkForUpdate()
→ Récupère version locale (package_info_plus)
→ Récupère config depuis Supabase (app_version_config)
→ Compare les versions -
Si version obsolète ET force_update = true:
Affiche ForceUpdatePage (écran bloquant)
→ Utilisateur clique "Mettre à jour"
→ Ouvre Play Store / App Store
→ Installation nouvelle version
→ Redémarrage app
→ Check version OK ✅ -
Si version OK:
Continue normalement → MyApp()
🛠️ Administration
Consulter les versions actives:
SELECT
platform,
version,
build_number,
min_required_version,
min_required_build_number,
force_update,
is_active
FROM app_version_config
WHERE is_active = true;
Désactiver le force update:
UPDATE app_version_config
SET force_update = false
WHERE platform = 'android';
Changer le message:
UPDATE app_version_config
SET update_message = 'Nouveau message personnalisé'
WHERE platform = 'android';
📱 Tests
Tester en local:
-
Modifier la version dans pubspec.yaml:
version: 0.9.0+1 # Version inférieure à la minimale requise -
Dans Supabase, forcer la mise à jour:
UPDATE app_version_config
SET force_update = true,
min_required_version = '1.0.0',
min_required_build_number = 1
WHERE platform = 'android'; -
Lancer l'app:
flutter run -
Résultat attendu: Écran de force update s'affiche 🚫
⚠️ Points d'attention
1. Gestion des erreurs
Si Supabase est down, le check version laisse passer pour ne pas bloquer l'app.
// Dans VersionService.checkForUpdate()
catch (e) {
debugPrint('❌ Erreur: $e');
return null; // Laisser passer
}
2. RLS Policies
La table app_version_config est publique en lecture pour que l'app puisse checker sans auth.
-- Lecture publique
CREATE POLICY "Lecture publique de la config version active"
ON app_version_config FOR SELECT
USING (is_active = true);
3. Une seule config active par plateforme
La contrainte UNIQUE(platform, is_active) empêche d'avoir plusieurs configs actives.
-- ❌ ERREUR si on essaie d'insérer une 2ème config active
INSERT INTO app_version_config (platform, is_active, ...)
VALUES ('android', true, ...); -- Violation de contrainte
🚦 Workflow recommandé
Lors d'une release normale:
- Publier nouvelle version sur stores
- NE PAS activer
force_update - Monitorer l'adoption naturelle
Lors d'un hotfix critique:
- Publier version fixée sur stores
- Activer
force_update = true - Mettre à jour
min_required_version - Personnaliser
update_message - Monitorer les mises à jour
- Une fois adoption > 95%, désactiver
force_update
📈 Monitoring
Requête utile pour voir la répartition des versions:
-- À implémenter: Table de tracking des versions utilisateurs
CREATE TABLE user_app_versions (
user_id UUID REFERENCES auth.users(id),
platform TEXT,
version TEXT,
build_number INT,
last_seen TIMESTAMPTZ
);
-- Puis query:
SELECT
version,
COUNT(*) as nb_users
FROM user_app_versions
WHERE platform = 'android'
GROUP BY version
ORDER BY nb_users DESC;
🎨 Personnalisation
Changer le message de l'écran:
Éditez ForceUpdatePage:
// lib/core/presentation/pages/force_update_page.dart
AppText.h1(
'Votre titre personnalisé', // Modifiez ici
...
)
Changer l'icône:
Icon(
Icons.update, // Changez l'icône ici
size: 100,
...
)
🔒 Sécurité
- ✅ Lecture publique de la config (pas de données sensibles)
- ✅ Écriture admin seulement
- ✅ Fallback gracieux en cas d'erreur
- ✅ Pas de bypass possible (WillPopScope)
📞 Support
En cas de problème:
- Vérifier les logs:
flutter logs - Vérifier Supabase:
supabase logs - Désactiver temporairement:
force_update = false
Auteur: Claude Code Date: 2025-01-12 Version: 1.0