Outils de debug

Sérialisation des données

Comment les données de requête en cache deviennent l'arbre JSON affiché dans l'overlay, et comment rendre lisibles les types de modèles personnalisés.

L'overlay affiche les données de requête en cache sous forme d'arbre JSON extensible. La lisibilité de cet arbre dépend du type T renvoyé par le fetcher : la plupart des modèles ne demandent aucune configuration, et les autres une seule option.


Ordre de résolution de la sérialisation

QoraClient n'a aucune connaissance des types de domaine. Quand un fetch se règle, le résultat est passé au tracker actif selon l'ordre de résolution suivant :

PrioritéConditionRésultat
1QoraOptions.toJson est définiAppelle la fonction fournie et utilise sa valeur de retour
2String, num, bool ou nullPassé tel quel
3ListChaque élément est sérialisé récursivement
4MapChaque valeur est sérialisée récursivement
5Objet avec une méthode toJson()Appelée via dispatch dynamique, résultat utilisé
6Tout le reste.toString() appelé, affiché comme chaîne simple

Le parcours est récursif. Un List<Map<String, User>> est géré correctement sans configuration, à condition que User implémente toJson().


Scénarios courants

json_serializable et freezed

Les deux générateurs de code produisent une méthode toJson(). Le sérialiseur la détecte et l'appelle automatiquement. Aucune configuration supplémentaire n'est requise.

user_repository.dart
// @JsonSerializable() ou @freezed : pas de config supplémentaire nécessaire
fetchQuery<User>(
  key: ['users', userId],
  fetcher: () => api.getUser(userId),
);

L'overlay rend le graphe d'objets complet sous forme d'arbre extensible.


Réponses Map brutes

Quand le fetcher retourne Map<String, dynamic> directement (par exemple, après décodage manuel du corps de la réponse HTTP), aucune configuration n'est requise.

user_repository.dart
fetchQuery<Map<String, dynamic>>(
  key: ['users', userId],
  fetcher: () async {
    final response = await http.get(Uri.parse('/users/$userId'));
    return jsonDecode(response.body) as Map<String, dynamic>;
  },
);

Cette approche ne nécessite aucune configuration de sérialisation. L'étape de désérialisation doit être gérée dans le fetcher ou la couche repository plutôt que dans un widget consommateur.


Modèles manuels sans toJson

Quand un modèle n'a pas de méthode toJson(), le sérialiseur revient à .toString() et affiche un nœud de chaîne unique. Pour produire une sortie structurée, fournir un sérialiseur explicite via QoraOptions.toJson.

user_repository.dart
fetchQuery<User>(
  key: ['users', userId],
  fetcher: () => api.getUser(userId),
  options: QoraOptions(
    toJson: (data) => (data as User).toJson(),
  ),
);

Listes

Les listes d'objets typés suivent les mêmes règles de résolution. Quand le type d'élément implémente toJson(), chaque élément est sérialisé automatiquement.

posts_repository.dart
fetchQuery<List<Post>>(
  key: ['posts'],
  fetcher: () => api.getPosts(),
  // Post implémente toJson() : détecté automatiquement, pas de config supplémentaire.
);

Quand les éléments individuels n'ont pas de toJson(), fournir un sérialiseur :

posts_repository.dart
fetchQuery<List<Post>>(
  key: ['posts'],
  fetcher: () => api.getPosts(),
  options: QoraOptions(
    toJson: (data) => (data as List<Post>).map((p) => p.toJson()).toList(),
  ),
);

L'option toJson

QoraOptions.toJson accepte toute fonction avec la signature :

Object? Function(Object? data)

La valeur de retour doit être JSON-safe : Map<String, dynamic>, List, String, num, bool ou null. Retourner tout autre type causera une erreur d'exécution quand le rendu DevTools encode la valeur.

user_repository.dart
// Le cast est sûr : T est User et data est le résultat du fetcher.
options: QoraOptions(
  toJson: (data) => (data as User).toJson(),
),

toJson suit les mêmes sémantiques de fusion que tous les autres champs de QoraOptions. Une valeur sur QoraClientConfig.defaultOptions s'applique globalement et peut être remplacée par requête.


Quand utiliser chaque approche

SituationRecommandation
Modèles json_serializable ou freezedPas de configuration requise. Détecté auto.
Map ou List bruts depuis jsonDecodePas de configuration requise. Passé tel quel.
Modèle manuel avec une méthode toJson()Pas de configuration requise. Détecté auto.
Modèle sans toJson()Fournir QoraOptions(toJson: ...).
Sérialisation personnalisée avec une signature toJson non standardFournir QoraOptions(toJson: ...).

Impact en production

Le chemin de sérialisation (_serializeForTracker) n'est exécuté que quand QoraTracker.needsSerialization retourne true. En production, QoraClient utilise par défaut NoOpTracker, qui retourne false. Aucune sérialisation ne s'exécute et aucun overhead n'est introduit.

main.dart
final client = QoraClient(
  tracker: kDebugMode ? tracker : null, // null active NoOpTracker
);
Copyright © 2026