Migrer depuis d'Autres Librairies
Ce guide fait correspondre les concepts de flutter_query et cached_query à ceux de Qora. Les deux librairies sont solides ; le but n'est pas de les critiquer mais de rendre la migration prévisible.
flutter_query 0.11 et cached_query 3.7. Sur une autre version, vérifiez les noms dans leur propre documentation.Migrer progressivement
Pas besoin de tout réécrire d'un coup. Un QoraScope peut cohabiter avec un autre provider : vous pouvez migrer un écran ou une fonctionnalité à la fois.
void main() {
runApp(
QoraScope.auto(
client: QoraClient(),
child: const MyApp(), // les providers existants restent à l'intérieur
),
);
}
Depuis flutter_query
flutter_query repose sur des hooks (flutter_hooks) et suit TanStack Query de près. Le chemin le plus rapide est qora_hooks, qui garde le même style.
Correspondance des concepts
| Concept | flutter_query | Qora |
|---|---|---|
| Provider | QueryClientProvider(client: QueryClient()) | QoraScope(client: QoraClient()) |
| Requête (hooks) | useQuery(key, (context) => fn()) | useQuery(key: key, fetcher: fn) |
| Requête (widget) | non fourni | QoraBuilder<T> |
| Mutation | useMutation((vars, context) => fn(vars)) | useMutation(mutator: fn) ou QoraMutationBuilder |
| Fenêtre de fraîcheur | staleDuration | QoraOptions.staleTime |
| Rétention du cache inactif | gcDuration | QoraOptions.gcTime |
| Placeholder / seed | placeholder, seed | placeholderData, initialData |
| Invalider (préfixe, défaut) | client.invalidateQueries(queryKey: ['todos']) | client.invalidatePrefix(['todos']) |
| Invalider (exact) | invalidateQueries(queryKey: k, exact: true) | client.invalidate(k) |
| Lire / écrire le cache | getQueryData, setQueryData | getQueryData, setQueryData |
invalidateQueries de flutter_query matche par préfixe par défaut. invalidate(key) de Qora est exact. Utilisez invalidatePrefix là où vous comptiez sur le comportement par défaut.Lire l'état
flutter_query expose des booléens sur un objet résultat. Qora retourne un QoraState<T> scellé : le compilateur vérifie que chaque cas est traité, et les données périmées restent disponibles pendant un refetch ou une erreur.
// flutter_query
final result = useQuery(const ['user'], (context) => api.getUser());
if (result.isLoading) return const CircularProgressIndicator();
if (result.isError) return Text('Error: ${result.error}');
return UserCard(result.data!);
// Qora (qora_hooks)
final state = useQuery<User>(key: const ['user'], fetcher: api.getUser);
return switch (state) {
Success(:final data) => UserCard(data),
Failure(:final error, :final previousData) =>
previousData != null ? UserCard(previousData) : Text('Error: $error'),
Loading(:final previousData) =>
previousData != null ? UserCard(previousData) : const CircularProgressIndicator(),
Initial() => const SizedBox.shrink(),
};
Mutations et invalidation
// flutter_query
final mutation = useMutation<Todo, Exception, String, void>(
(title, context) => api.createTodo(title),
onSuccess: (data, variables, onMutateResult, context) {
// invalider ['todos'] via le QueryClient
},
);
// Qora : on déclare ce qu'il faut rafraîchir, sans callback manuel
final mutation = useMutation<Todo, String>(
mutator: api.createTodo,
options: const MutationOptions(invalidates: [['todos']]),
);
mutation.mutate('Buy milk');
Depuis cached_query
cached_query est orienté objet : on crée des instances Query / Mutation (souvent dans un repository) et on écoute leurs streams, avec les widgets Flutter dans cached_query_flutter.
Correspondance des concepts
| Concept | cached_query | Qora |
|---|---|---|
| Config globale | CachedQuery.instance.config(...) | QoraClient(config: QoraClientConfig(...)) |
| Requête | Query(key: 'user', queryFn: fn) | client.watchQuery(key: ['user'], fetcher: fn) |
| Stream | query.stream (QueryStatus) | watchQuery / watchState (QoraState) |
| Lecture ponctuelle | await query.result | await client.fetchQuery(key: ..., fetcher: ...) |
| Widget | QueryBuilder | QoraBuilder<T> |
| Forcer un refetch | query.refetch() | client.invalidate(key) (les watchers actifs refetch) |
| Invalider en masse | CachedQuery.instance.invalidateCache(...) | invalidatePrefix, invalidateWhere, invalidateTags |
| Fraîcheur / rétention | staleDuration, cacheDuration | staleTime, gcTime |
| Liste infinie | InfiniteQuery<T, Arg> | InfiniteQueryBuilder<TData, TPageParam> |
| Mutation | Mutation(mutationFn: fn, invalidateQueries: [...]) | QoraMutationBuilder / useMutation avec MutationOptions.invalidates |
| Persistance | cached_storage (sqflite) | PersistQoraClient + votre StorageAdapter |
| Refetch au retour / à la reconnexion | refetchOnResume, refetchOnConnection | QoraScope.auto() |
Clés
Les clés cached_query sont en général des chaînes. Les clés Qora sont des listes, ce qui rend l'invalidation par préfixe naturelle :
// cached_query
Query(key: 'user-$id', queryFn: () => api.getUser(id));
// Qora
client.watchQuery(key: ['users', id], fetcher: () => api.getUser(id));
client.invalidatePrefix(['users']); // toutes les requêtes utilisateur
Ce que vous gagnez
- File de mutations hors ligne : les écritures faites hors ligne sont mises en file et rejouées dans l'ordre à la reconnexion, avec une UI optimiste entre-temps.
- État à deux axes :
QoraState(données) etFetchStatus(fetching / paused / idle) sont indépendants, donc « hors ligne avec données périmées » est un état de première classe. - Invalidation par tags : les mutations invalident des tags logiques plutôt que des clés codées en dur.
- Overlay DevTools intégré à l'app pour inspecter le cache, les timelines et les mutations.
Gestion des erreurs
Les deux librairies exposent les erreurs brutes. Avec Qora, on les mappe une seule fois via QoraClientConfig.errorMapper, qui reçoit l'erreur et sa stack trace et retourne une QoraException :
import 'package:qora_flutter/qora_flutter.dart';
final client = QoraClient(
config: QoraClientConfig(
errorMapper: (Object error, StackTrace? stackTrace) => error is QoraException
? error
: QoraException(error.toString(), originalError: error, stackTrace: stackTrace),
),
);
Voir l'intégration Dio pour un mapper complet.
Venant de TanStack Query
Correspondance précise des concepts, hooks et options de configuration de TanStack Query vers leurs équivalents Qora. Couvre ce qui est identique, ce qui est renommé et ce qui fonctionne différemment.
Assistants IA
Donner à Claude, Cursor, Copilot ou tout assistant de code le contexte nécessaire pour écrire du code Qora correct.