Référence de l'API

QoraOptions

Configuration par requête qui remplace les défauts de QoraClientConfig. Passer à fetchQuery ou watchQuery pour personnaliser le cache, les retries, la fraîcheur et le polling.

Vue d'ensemble

QoraOptions configure le comportement d'une requête individuelle. Tout champ laissé à sa valeur par défaut hérite de QoraClientConfig.defaultOptions. Les champs sont fusionnés, les valeurs par requête ayant la priorité.

user_repository.dart
await client.fetchQuery<User>(
  key: ['users', userId],
  fetcher: () => api.getUser(userId),
  options: QoraOptions(
    staleTime: Duration(minutes: 5),
    retryCount: 2,
  ),
);

Constructeur

const QoraOptions({
  Duration staleTime = Duration.zero,
  Duration gcTime = const Duration(minutes: 5),
  bool enabled = true,
  Object? dependsOn,
  int retryCount = 3,
  Duration retryDelay = const Duration(seconds: 1),
  Duration Function(int attemptIndex)? retryDelayCalculator,
  bool Function(Object error, int attemptIndex)? retryCondition,
  bool refetchOnWindowFocus = true,
  bool refetchOnReconnect = true,
  Duration? refetchInterval,
  bool? refetchOnMount,
  NetworkMode networkMode = NetworkMode.online,
  Object? Function(Object? data)? toJson,
})

Champs

staleTime

final Duration staleTime

Défaut : Duration.zero

Durée pendant laquelle les données sont considérées fraîches après un fetch réussi. Pendant la fenêtre de fraîcheur, fetchQuery et watchQuery ignorent complètement le réseau et retournent les données en cache.

Une fois staleTime écoulé, les données sont considérées périmées : Qora les retourne immédiatement mais déclenche aussi une revalidation en arrière-plan.

main.dart
// Toujours périmé (défaut) : servir le cache, toujours revalider
staleTime: Duration.zero,

// Frais pendant 5 minutes : pas de réseau dans la fenêtre
staleTime: Duration(minutes: 5),

// Jamais périmé en pratique
staleTime: Duration(days: 365),
Type de donnéesValeur recommandée
Prix en direct, feedsDuration.zero
Profil utilisateurDuration(minutes: 5)
Contenu article/postDuration(minutes: 10)
Configuration applicativeDuration(hours: 1)
Données de référence statiquesDuration(days: 1)

gcTime

final Duration gcTime

Défaut : Duration(minutes: 5)

Durée pendant laquelle une entrée inactive reste en cache après l'annulation de tous les abonnés watchQuery. Quand le timer GC se déclenche, l'entrée est supprimée ; le prochain accès repart de Initial.

main.dart
// Garder en cache 10 minutes après avoir quitté l'écran
gcTime: Duration(minutes: 10),

// Supprimer immédiatement à la désabonnement (ex. données de session sensibles)
gcTime: Duration.zero,
staleTime et gcTime sont indépendants. Un pattern courant est staleTime < gcTime : les données deviennent périmées (déclenchant une revalidation) mais restent en cache pour que la navigation retour soit instantanée.

enabled

final bool enabled

Défaut : true

Quand false :

  • fetchQuery lève un StateError immédiatement
  • watchQuery émet l'état actuel sans déclencher de fetch

Utile pour les requêtes conditionnelles qui ne doivent s'exécuter que lorsqu'un prérequis est satisfait :

main.dart
// Fetch uniquement quand userId est non-null
options: QoraOptions(enabled: userId != null),

dependsOn

final Object? dependsOn

Défaut : null

La clé d'une autre requête qui doit avoir des données Success avant que cette requête se déclenche. Fournit une dépendance réactive sans coordination manuelle :

APIComportement quand la dépendance n'est pas résolue
watchQueryS'abonne au stream de la dépendance ; se déclenche automatiquement à l'arrivée des données
fetchQueryLève StateError immédiatement
prefetchIgnore silencieusement
main.dart
// Étape 1 : récupérer la session (pas de dépendance)
client.watchQuery<Session>(
  key: ['session'],
  fetcher: () => api.getSession(),
);

// Étape 2 : récupérer l'utilisateur une fois que la session a des données
client.watchQuery<User>(
  key: ['user'],
  fetcher: () => api.getUser(session!.userId),
  options: QoraOptions(dependsOn: ['session']),
);

Voir le guide des requêtes dépendantes pour des exemples complets.


retryCount

final int retryCount

Défaut : 3

Nombre de tentatives de retry automatique après un échec de fetch. Mettre à 0 pour désactiver les retries.

main.dart
retryCount: 0,  // échec immédiat à la première erreur
retryCount: 5,  // retry jusqu'à 5 fois

retryDelay

final Duration retryDelay

Défaut : Duration(seconds: 1)

Délai de base entre les tentatives de retry. Par défaut Qora utilise un backoff exponentiel :

  • Tentative 0 : 1 s
  • Tentative 1 : 2 s
  • Tentative 2 : 4 s
  • Tentative 3 : 8 s

retryDelayCalculator

final Duration Function(int attemptIndex)? retryDelayCalculator

Défaut : null (utilise le backoff exponentiel)

Calculateur de délai personnalisé. Reçoit l'index de tentative base-zéro et retourne le délai avant cette tentative.

main.dart
// Délai fixe de 500 ms
retryDelayCalculator: (_) => const Duration(milliseconds: 500),

// Backoff exponentiel avec jitter
retryDelayCalculator: (i) => Duration(
  milliseconds: (500 * pow(2, i) + Random().nextInt(200)).toInt(),
),

retryCondition

final bool Function(Object error, int attemptIndex)? retryCondition

Défaut : null (retenter toutes les erreurs jusqu'à retryCount)

Prédicat qui détermine si un fetch échoué doit être réessayé. Reçoit l'erreur et l'index de tentative base-zéro qui vient d'échouer. Retourner false pour ignorer les tentatives suivantes.

Utile pour éviter de réessayer des erreurs non récupérables comme les réponses HTTP 4xx :

main.dart
// Réessayer uniquement les erreurs réseau, pas les erreurs applicatives
retryCondition: (error, attempt) => error is SocketException,

// Ne jamais réessayer les réponses 4xx (si mappées vers une exception typée)
retryCondition: (error, attempt) => error is! HttpException,

// Réessayer uniquement la première tentative, échouer rapidement ensuite
retryCondition: (error, attempt) => attempt == 0,

Quand retryCondition retourne false, toutes les tentatives restantes pour cette erreur sont ignorées et l'erreur est levée immédiatement.


refetchOnWindowFocus

final bool refetchOnWindowFocus

Défaut : true

Indique si cette requête doit être rechargée quand l'application regagne le focus de fenêtre. Nécessite qu'un LifecycleManager soit enregistré sur le client.


refetchOnReconnect

final bool refetchOnReconnect

Défaut : true

Indique si cette requête doit être rechargée quand la connexion réseau est rétablie. Nécessite qu'un ConnectivityManager soit enregistré sur le client.


refetchInterval

final Duration? refetchInterval

Défaut : null (désactivé)

Quand défini, watchQuery recharge automatiquement à cette cadence tant qu'au moins un abonné est actif. Le timer s'arrête quand le dernier abonné annule.

dashboard_page.dart
// Polling toutes les 30 secondes pendant que le widget est monté
options: QoraOptions(refetchInterval: Duration(seconds: 30)),

refetchOnMount

final bool? refetchOnMount

Défaut : null (revient à QoraClientConfig.refetchOnMount)

Contrôle si watchQuery déclenche un fetch au premier abonnement :

ValeurComportement
trueToujours recharger au montage, même si les données sont fraîches
falseFetch uniquement si les données sont absentes ; ignorer si en cache
nullUtiliser QoraClientConfig.refetchOnMount (défaut : true)

networkMode

final NetworkMode networkMode

Défaut : NetworkMode.online

Contrôle le comportement de cette requête quand l'appareil n'a pas de connectivité :

ValeurComportement
NetworkMode.onlineLe fetch est mis en pause hors ligne (FetchStatus.paused). Rejoué automatiquement à la reconnexion.
NetworkMode.alwaysLe fetch s'exécute toujours quelle que soit la connectivité. Utiliser pour les fetchers locaux (base de données, mémoire).
NetworkMode.offlineFirstLes données en cache sont retournées immédiatement. Une revalidation en arrière-plan est mise en file pour quand la connexion est rétablie.
lib/data/user_repository.dart
// Défaut : mise en pause hors ligne, rejeu à la reconnexion
options: const QoraOptions(networkMode: NetworkMode.online),

// Requête base de données locale : toujours exécuter, jamais mettre en pause
options: const QoraOptions(networkMode: NetworkMode.always),

// Servir le cache immédiatement, rafraîchir en arrière-plan quand en ligne
options: const QoraOptions(networkMode: NetworkMode.offlineFirst),
Définir NetworkMode.always pour les requêtes sur SQLite, Hive ou tout store local : elles n'ont pas besoin de connectivité et ne doivent jamais être mises en pause.

structuralSharing

final bool structuralSharing

Défaut : true

Préserve l'égalité référentielle pour les données imbriquées inchangées entre les fetches. Quand activé, chaque fetch réussi compare les nouvelles données avec le cache existant via une égalité profonde. Si les données sont structurellement inchangées, la référence précédente est conservée.

Cela évite les reconstructions inutiles de widgets : les widgets qui comparent avec identical() ou == peuvent ignorer le rendu quand les données n'ont pas réellement changé.

La comparaison est récursive pour Map, List et Set. Les classes de modèle personnalisées doivent surcharger == et hashCode pour une détection fiable.

Désactiver uniquement si la vérification d'égalité profonde devient un goulot d'étranglement mesuré avec de très grandes structures de données :

main.dart
// Activé (défaut) : préserver les références quand les données n'ont pas changé
QoraOptions(structuralSharing: true),

// Désactivé : toujours créer de nouvelles références à chaque fetch
QoraOptions(structuralSharing: false),

keepPreviousData

final bool keepPreviousData

Défaut : false

Quand true, les données précédentes restent visibles pendant que le prochain fetch est en cours, supprimant la transition Success → Loading → Success. L'entrée reste dans l'état Success pendant le fetch et en cas d'échec, donc l'UI n'affiche jamais de spinner de chargement.

Utile pour la pagination basée sur un offset : garder la page actuelle visible pendant le chargement de la suivante.

post_list.dart
// Changement de page sans flash de chargement
fetchQuery<List<Post>>(
  key: ['posts', 'page', page],
  fetcher: () => api.getPosts(page: page, limit: 20),
  options: QoraOptions(
    keepPreviousData: true,
    staleTime: Duration.zero,
  ),
);

Sans effet sur le fetch initial (pas de données précédentes à conserver).


transform

final Object Function(Object? data)? transform

Défaut : null

Fonction de transformation appliquée aux données de fetch réussies avant leur entrée dans le cache. Utilisez-la pour découpler la transformation des données du fetcher : le fetcher retourne les données brutes (ex. maps JSON décodées) et transform les convertit en modèle final.

La transformation s'exécute avant le partage structurel, donc les modifications des données transformées sont correctement détectées par rapport à l'entrée en cache.

user_repository.dart
// Le fetcher retourne du JSON brut ; transform le convertit en classe de modèle.
fetchQuery<User>(
  key: ['users', userId],
  fetcher: () => api.getUser(userId),             // retourne Map<String, dynamic>
  options: QoraOptions(
    transform: (raw) => User.fromJson(raw as Map<String, dynamic>),
  ),
);

Le fetcher et la transform sont tous deux testables indépendamment : le fetcher retourne des données brutes, la transform est une fonction pure.


transformError

final Object Function(Object error)? transformError

Défaut : null

Fonction de transformation appliquée aux erreurs de fetch avant leur entrée dans le cache. Reçoit l'erreur brute levée par le fetcher (après toutes les tentatives). Utilisez-la pour normaliser les erreurs de votre couche API ou pour les enrichir avec du contexte métier.

S'exécute avant le QoraClientConfig.errorMapper, donc la transformation par requête a priorité sur le mappeur global.

post_repository.dart
fetchQuery<Post>(
  key: ['post', postId],
  fetcher: () => api.getPost(postId),
  options: QoraOptions(
    transformError: (error) => switch (error) {
      DioException(e: final err) => AppError.fromDio(err),
      _ => AppError.unknown(error.toString()),
    },
  ),
);

providesTags

final List<QueryTag>? providesTags

Défaut : null

Étiquettes que cette requête fournit pour l'invalidation de cache basée sur les tags. Lorsqu'une mutation invalide un tag correspondant (via MutationOptions.invalidatesTags), toutes les requêtes qui fournissent ce tag sont automatiquement re-récupérées, indépendamment de leur clé de requête.

Cela découple la logique d'invalidation de la structure des clés de requête, facilitant l'invalidation de requêtes apparentées à travers différents motifs de clés :

post_repository.dart
// La requête fournit des tags
watchQuery<Post>(
  key: ['post', postId],
  fetcher: () => api.getPost(postId),
  options: QoraOptions(
    providesTags: [
      QueryTag('post', postId.toString()),
      QueryTag('author', authorId.toString()),
    ],
  ),
);

Correspondance générique

Invalider QueryTag('post') (sans id) re-récupère TOUTES les requêtes qui fournissent un tag de type 'post', indépendamment de leurs ids individuels. Invalider QueryTag('post', '123') ne re-récupère que les requêtes qui fournissent le tag spécifique 'post:123'.

// Re-récupère TOUTES les requêtes taguées avec 'post' (n'importe quel id).
client.invalidateTags([QueryTag('post')]);

// Re-récupère seulement les requêtes taguées avec 'post:123'.
client.invalidateTags([QueryTag('post', '123')]);

toJson

final Object? Function(Object? data)? toJson

Défaut : null

Sérialiseur optionnel qui contrôle comment les données de la requête sont affichées dans l'overlay DevTools.

Quand omis, Qora applique l'ordre de résolution suivant automatiquement :

  1. Les données qui sont déjà Map, List, String, num, bool ou null: passées telles quelles.
  2. Tout objet avec une méthode toJson() (json_serializable, freezed, manuel): appelée dynamiquement.
  3. Tout le reste: repli sur .toString().

Fournir toJson explicitement uniquement quand la détection automatique est insuffisante, par exemple quand la méthode toJson() a une signature non standard ou retourne un type non encodable.

user_repository.dart
// Sérialiseur explicite (remplace la détection automatique)
fetchQuery<User>(
  key: ['users', userId],
  fetcher: () => api.getUser(userId),
  options: QoraOptions(
    toJson: (data) => (data as User).toJson(),
  ),
);

getRetryDelay(int attemptIndex)

Duration getRetryDelay(int attemptIndex)

Calcule le délai de retry pour l'index de tentative base-zéro donné. Délègue à retryDelayCalculator si défini, sinon applique le backoff exponentiel : retryDelay × 2^attemptIndex.


merge(QoraOptions? other)

QoraOptions merge(QoraOptions? other)

Retourne un nouveau QoraOptions fusionnant this avec other. Les champs de other ont la priorité. Les champs nullable (retryDelayCalculator, refetchInterval, refetchOnMount) reviennent à this quand non définis dans other.

Utilisé en interne par QoraClient pour combiner les défauts globaux avec les options par requête.


Recettes courantes

main.dart
// Données en direct : toujours frais, polling toutes les 10 s
QoraOptions(
  staleTime: Duration.zero,
  refetchInterval: Duration(seconds: 10),
)

// Données de config : cache agressif
QoraOptions(
  staleTime: Duration(hours: 1),
  gcTime: Duration(hours: 2),
  retryCount: 5,
)

// Requête conditionnelle
QoraOptions(enabled: currentUser != null)

// Désactiver les retries pour les mutations
QoraOptions(retryCount: 0)
Copyright © 2026