QoraState
Hiérarchie de classes
sealed class QoraState<T> { ... }
final class Initial<T> extends QoraState<T> { ... }
final class Loading<T> extends QoraState<T> { ... }
final class Success<T> extends QoraState<T> { ... }
final class Failure<T> extends QoraState<T> { ... }
QoraState<T> : Base
Propriétés (disponibles sur tous les états)
| Propriété | Type | Description |
|---|---|---|
hasData | bool | true pour Success, ou Loading/Failure avec des données précédentes |
dataOrNull | T? | Meilleures données disponibles dans tous les états, ou null |
isInitial | bool | true si Initial |
isLoading | bool | true si Loading |
isSuccess | bool | true si Success |
isError | bool | true si Failure |
errorOrNull | Object? | Erreur de Failure, ou null |
map<R>(R Function(T) transform)
Transforme le type de données tout en préservant la structure d'état. previousData dans Loading/Failure est également transformé.
final nameState = userState.map((user) => user.name);
// Success<User> → Success<String>
when({onInitial, onLoading, onSuccess, onError})
Exécute des callbacks pour l'état actuel. Les quatre callbacks sont optionnels.
state.when(
onSuccess: (data, updatedAt) => print('Chargé : ${data.name}'),
onError: (error, stackTrace, previousData) => showSnackBar('$error'),
);
maybeWhen<R>({required orElse, onInitial, onLoading, onSuccess, onError})
Mappe l'état vers une valeur de type R. Les états non gérés tombent dans orElse.
final widget = state.maybeWhen(
onSuccess: (data, _) => UserCard(data),
orElse: () => const CircularProgressIndicator(),
);
Initial<T>
La requête n'a pas encore été exécutée. État par défaut pour toute clé qui n'a jamais été chargée ou a été supprimée du cache.
const Initial<User>()
state.isInitial // true
state.hasData // false
state.dataOrNull // null
Loading<T>
Un fetch est activement en cours.
Champs
| Champ | Type | Description |
|---|---|---|
previousData | T? | Dernières données réussies (null lors du premier fetch) |
Loading<User>() // premier fetch
Loading<User>(previousData: cachedUser) // rechargement en arrière-plan
Propriétés (extensions)
| Propriété | Type | Description |
|---|---|---|
isFirstLoad | bool | true quand previousData == null |
isRefreshing | bool | true quand previousData != null |
hasData | bool | true quand previousData != null |
case Loading(:final previousData):
return previousData != null
? Stack(children: [UserCard(previousData), const LinearProgressIndicator()])
: const CircularProgressIndicator();
Success<T>
Les données ont été récupérées avec succès.
Champs
| Champ | Type | Description |
|---|---|---|
data | T | Les données récupérées |
updatedAt | DateTime | Horodatage du dernier fetch réussi |
Propriétés et méthodes
| Membre | Type | Description |
|---|---|---|
age | Duration | Temps écoulé depuis updatedAt |
isStale(Duration threshold) | bool | true si age > threshold |
successDataOrNull (ext.) | T? | Retourne data (null pour les autres états) |
case Success(:final data, :final updatedAt):
return Column(children: [
UserCard(data),
Text('Mis à jour ${_timeAgo(updatedAt)}'),
]);
Factory
Success.now(data) // équivalent à Success(data: data, updatedAt: DateTime.now())
Failure<T>
Le fetch a échoué après toutes les tentatives de retry.
Champs
| Champ | Type | Description |
|---|---|---|
error | Object | L'erreur levée |
stackTrace | StackTrace? | Stack trace optionnelle |
previousData | T? | Dernières données réussies (permet la dégradation gracieuse) |
case Failure(:final error, :final previousData):
return Column(children: [
if (previousData != null) ...[
UserCard(previousData), // afficher les données périmées
const SizedBox(height: 8),
],
ErrorBanner('$error'),
]);
Extensions sur QoraState<T>
Exportées depuis package:qora/qora.dart via state_extensions.dart.
Accès aux données
state.requireData() // T : lève StateError si pas de données
state.successDataOrNull // T? : données uniquement depuis Success
state.updatedAt // DateTime? : updatedAt depuis Success, null sinon
state.status // enum QoraStatus (initial/loading/success/error)
Mapping
state.mapData((data) => data.name) // comme map : données Success et previousData
state.fold(
onInitial: () => 'non chargé',
onLoading: (prev) => 'chargement...',
onSuccess: (data, _) => data.name,
onError: (error, _, __) => 'erreur',
)
Combinaison
// Deux états → un état combiné (Success uniquement quand les deux sont Success)
userState.combine(postsState, (user, posts) => UserWithPosts(user, posts))
// Listes et tuples
QoraStateUtils.combineList([s1, s2, s3])
QoraStateUtils.combine2(userState, settingsState)
QoraStateUtils.combine3(userState, postsState, tagsState)
Extensions sur Stream<QoraState<T>>
stream.whereHasData() // uniquement les états avec des données
stream.whereSuccess() // uniquement les états Success
stream.whereError() // uniquement les états Failure
stream.dataOrNull() // Stream<T?> : null pour les états sans données
stream.data() // Stream<T> : ignore les états sans données
stream.mapData((data) => transform(data)) // Stream<QoraState<R>>
stream.delayLoading(Duration(milliseconds: 300))
Sérialisation : QoraStateCodec<T>
final codec = QoraStateCodec<User>(
encode: (user) => user.toJson(),
decode: (json) => User.fromJson(json as Map<String, dynamic>),
);
// Encoder
final json = codec.encodeState(state); // Map<String, dynamic>
// Décoder
final restored = codec.decodeState(json); // QoraState<User>
Enum QoraStatus
Un enum de statut simplifié pour les cas où la classe sealed complète n'est pas nécessaire :
state.status // QoraStatus.initial | .loading | .success | .error
state.status.isLoading // bool
state.status.isSuccess // bool
state.status.isError // bool
QoraKey
Clé de requête typée et immuable avec égalité structurelle. Supporte à la fois la syntaxe List brute et QoraKey.
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.