Avec Provider
Provider et ChangeNotifier sont efficaces pour l'état UI et l'injection de dépendances. La gestion des données serveur introduit un boilerplate répétitif dans chaque notifier : isLoading, error, gardes de cache manuelles et gestion des retries incohérente.
Qora prend en charge cette responsabilité spécifique. Provider conserve tout le reste.
Le problème avec ChangeNotifier pour les données serveur
Un notifier de récupération de données typique :
class UserNotifier extends ChangeNotifier {
User? data;
bool isLoading = false;
Object? error;
int? _loadedId;
Future<void> fetchUser(int id) async {
if (_loadedId == id && data != null) return; // cache manuel, pas de TTL, pas de SWR
isLoading = true;
error = null;
notifyListeners();
try {
data = await UserApi.getUser(id);
_loadedId = id;
} catch (e) {
error = e; // pas de retry, pas de backoff
} finally {
isLoading = false;
notifyListeners();
}
}
}
Consumer<UserNotifier>(
builder: (context, notifier, _) {
if (notifier.isLoading) return const CircularProgressIndicator();
if (notifier.error != null) return ErrorWidget('${notifier.error}');
if (notifier.data == null) {
notifier.fetchUser(userId); // effet de bord dans build, cycle de vie fragile
return const SizedBox.shrink();
}
return UserCard(notifier.data!);
},
)
Ce pattern est écrit identiquement pour chaque appel API. Il manque aussi : stale-while-revalidate, refetch en arrière-plan au focus/reconnexion, déduplication des requêtes et support hors ligne.
Après : laisser Qora gérer les données serveur
final qoraClient = QoraClient(
config: const QoraClientConfig(
defaultOptions: QoraOptions(staleTime: Duration(minutes: 5)),
),
);
runApp(
MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => ThemeNotifier()),
ChangeNotifierProvider(create: (_) => AuthNotifier()),
],
child: QoraScope(
client: qoraClient,
lifecycleManager: FlutterLifecycleManager(qoraClient: qoraClient),
connectivityManager: FlutterConnectivityManager(),
child: const MyApp(),
),
),
);
QoraBuilder<User>(
queryKey: ['users', userId],
fetcher: () => UserApi.getUser(userId),
builder: (context, state, fetchStatus) => switch (state) {
Success(:final data) => UserCard(data),
Loading() => const CircularProgressIndicator(),
Failure(:final error) => ErrorWidget('$error'),
Initial() => const SizedBox.shrink(),
},
)
La version Qora supprime les flags de chargement manuels, les notifications de notifier et les gardes de cache personnalisées. Retry, déduplication des requêtes, revalidation en arrière-plan et comportement SWR sont intégrés.
Le troisième argument du builder fetchStatus distingue FetchStatus.fetching de FetchStatus.paused en conditions hors ligne. Utiliser cet état pour afficher un indicateur d'attente de connexion plutôt qu'un spinner de chargement.
Division des responsabilités
| Responsabilité | Garder dans Provider | Déplacer vers Qora |
|---|---|---|
| Données récupérées depuis une API (users, products, feeds) | QoraBuilder + fetcher: | |
| Mutations (create, update, delete) | QoraMutationBuilder | |
| État UI (onglet sélectionné, modal ouvert, valeurs de formulaire) | ChangeNotifier | |
| Session d'authentification (token en mémoire) | ChangeNotifier | |
| Injection de dépendances (services, repositories) | Provider / ChangeNotifierProvider |
Correspondance des APIs
| Pattern Provider | Équivalent Qora |
|---|---|
ChangeNotifier + méthode fetch() | QoraBuilder avec fetcher: |
Consumer + ternaire sur isLoading / error / data | switch (state) sur QoraState |
if (data != null) return manuel | staleTime dans QoraOptions |
ChangeNotifierProvider (couche données) | QoraScope |
notifyListeners() après écriture | client.invalidate(key) |
Checklist
- Ajouter
qora_flutteràpubspec.yamlet exécuterflutter pub get - Imbriquer
QoraScopeà l'intérieur de la racineMultiProviderexistante (pas un remplacement) - Identifier les classes
ChangeNotifierqui ne font que fetcher et émettre loading/error/data et les remplacer parQoraBuilder(fetcher:, builder à 3 arguments) - Remplacer les ternaires loading/error/data de
Consumerparswitch (state) - Remplacer les gardes de cache manuelles par
staleTime - Remplacer
notifyListeners()post-écriture parclient.invalidate(key) - Conserver les entrées
ChangeNotifierProviderpour l'état UI, l'auth et l'injection de dépendances
Voir aussi
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.
Avec BLoC
Utiliser Qora pour l'état serveur et BLoC pour les machines à états UI, avec une séparation explicite des préoccupations.