Hors-ligne et réseau
La plupart des bibliothèques traitent « hors-ligne » comme une erreur de plus. Qora le traite comme un état : les lectures se mettent en pause au lieu d'échouer, les écritures peuvent être mises en file, et les deux reprennent seules au retour du réseau. Cette page décrit le modèle ; la recette Network-Aware détaille la configuration.
Comment Qora sait que l'appareil est hors-ligne
Le client écoute un ConnectivityManager. QoraScope.auto attache FlutterConnectivityManager, basé sur connectivity_plus : une configuration Flutter standard n'a besoin de rien d'autre.
Deux conséquences à connaître :
- Sans connectivity manager, le client se considère en ligne. Rien ne se met en pause, et rien n'est rejoué à la reconnexion. Une application Dart pure peut attacher le sien avec
client.attachConnectivityManager(...). - La connectivité désigne une interface réseau, pas un serveur joignable. Derrière un portail captif ou avec l'API en panne, l'appareil compte comme en ligne : les requêtes partent, sont retentées, et finissent en
Failure.
Deux axes : ce que vous avez, et ce qui se passe
Une requête a un état et un statut de fetch, et la gestion du hors-ligne vit dans le second :
QoraStateest ce que vous avez :Initial,Loading,Success,Failure, chacun portant la donnée quand il y en a une.FetchStatusest ce que fait le moteur :fetching,paused(en attente du réseau) ouidle.
QoraBuilder passe les deux à votre builder. Les combinaisons hors-ligne courantes :
| État | fetchStatus | Signification |
|---|---|---|
Loading, previousData renseigné | paused | Hors-ligne, donnée en cache disponible, refetch en attente |
Loading, sans previousData | paused | Hors-ligne et rien en cache : afficher un écran hors-ligne, pas un spinner |
Success | paused | Hors-ligne en mode offlineFirst : donnée en cache servie, refetch en attente |
Success | idle | Donnée fraîche, rien à faire |
Un fetch lancé hors-ligne ne lève pas d'exception dans les widgets. Appelé de façon impérative, fetchQuery renvoie la donnée en cache s'il y en a une, et lève sinon QoraOfflineException.
Lectures : NetworkMode
networkMode décide de ce que fait une requête quand elle doit charger alors que l'appareil est hors-ligne. Réglez-le par requête dans QoraOptions, ou pour toutes les requêtes dans defaultOptions.
| Mode | Hors-ligne | À la reconnexion |
|---|---|---|
online (défaut) | Le fetch n'est pas envoyé. L'état devient Loading(previousData: cache), le statut paused | Le fetch s'exécute |
offlineFirst | Avec une donnée en cache : l'état reste Success, statut paused. Sans : comme online | Le fetch s'exécute |
always | Le fetch est envoyé quand même, puis retenté et en échec comme toute erreur | Rien de particulier |
Utilisez always pour les fetchers qui n'ont pas besoin du réseau, comme une base locale. Utilisez offlineFirst quand un écran ne doit jamais repasser en chargement simplement parce que l'appareil a perdu le réseau.
Écritures : la file de mutations hors-ligne
Une mutation ne se met pas en pause. Sans activation explicite, une mutation appelée hors-ligne envoie sa requête et échoue. Avec offlineQueue: true dans ses MutationOptions, elle est mise en file à la place :
mutate(variables) hors-ligne
1. onMutate s'exécute tout de suite → les mises à jour optimistes marchent hors-ligne
2. l'appel est ajouté à la file
3. état : MutationSuccess(isOptimistic: true) si optimisticResponse est défini,
MutationPending sinon
reconnexion
4. l'appel est envoyé, une fois, dans l'ordre de mise en file
succès → onSuccess, invalidates, onSettled
échec → onError(error, variables, contexte de l'étape 1), onSettled
Trois propriétés comptent en pratique :
- La file appartient au client, pas au widget. Une écriture en file est envoyée même si l'écran qui l'a créée a été fermé.
- Un rejeu en échec n'est pas retenté.
onErrors'exécute avec le snapshot deonMutate: le rollback écrit pour le cas en ligne couvre donc aussi celui-ci. Prévenez l'utilisateur à cet endroit. - L'ordre est conservé, les dépendances ne sont pas vérifiées. Si une création en file est rejetée, une mise à jour en file du même élément est quand même envoyée, et échoue à son tour.
optimisticResponse ne définit que l'état propre de la mutation, par exemple pour afficher un badge « en file ». Les modifications des données en cache se font dans onMutate.
QoraMutationBuilder et useMutation utilisent tous deux la file (useMutation depuis qora_hooks 1.3.1). Un MutationController utilisé seul doit recevoir isOnline et offlineQueue explicitement.Reconnexion
Quand le connectivity manager signale le retour du réseau, le client :
- Rejoue les requêtes en pause par lots.
ReconnectStrategyfixe la taille des lots et un délai aléatoire entre eux (par défaut : 5 à la fois, jusqu'à 100 ms), pour qu'une centaine de requêtes en pause n'atteignent pas le serveur en même temps. - Rejoue ensuite la file de mutations, après les lectures, une mutation à la fois.
Indépendamment, QoraScope.auto relance aussi les requêtes périmées affichées à l'écran quand l'application revient au premier plan.
Entre deux redémarrages
| Quoi | Survit à un redémarrage ? |
|---|---|
| Données de requêtes en cache | Oui, avec PersistQoraClient et un StorageAdapter. Voir Persistance |
| Mutations en file | Non. La file vit en mémoire ; sa persistance est prévue pour la 1.4 |
Une application qui ne doit pas perdre les écritures faites hors-ligne avant d'être tuée doit, pour l'instant, aussi les enregistrer elle-même (par exemple dans une table outbox locale) et les renvoyer au démarrage.
Étapes suivantes
- Recette Network-Aware : configuration,
isOptimistic,ReconnectStrategy - Widgets de statut réseau : bannières hors-ligne
- Étude de cas : Offline-First : une application complète avec persistance et file
- Mutations et invalidation : le cycle de vie des écritures sur lequel s'appuie cette page