API Reference
QoraState
Sealed class hierarchy representing the four possible states of a query. Exhaustive pattern matching is enforced at compile time.
Class Hierarchy
lib/src/state/qora_state.dart
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
Properties (available on all states)
| Property | Type | Description |
|---|---|---|
hasData | bool | true for Success, or Loading/Failure with prior data |
dataOrNull | T? | Best available data across all states, or null |
isInitial | bool | true if Initial |
isLoading | bool | true if Loading |
isSuccess | bool | true if Success |
isError | bool | true if Failure |
errorOrNull | Object? | Error from Failure, or null |
map<R>(R Function(T) transform)
Transform the data type while preserving state structure. previousData in Loading/Failure is also transformed.
main.dart
final nameState = userState.map((user) => user.name);
// Success<User> → Success<String>
when({onInitial, onLoading, onSuccess, onError})
Execute callbacks for the current state. All four callbacks are optional.
main.dart
state.when(
onSuccess: (data, updatedAt) => print('Loaded: ${data.name}'),
onError: (error, stackTrace, previousData) => showSnackBar('$error'),
);
maybeWhen<R>({required orElse, onInitial, onLoading, onSuccess, onError})
Map the state to a value of type R. Unhandled states fall back to orElse.
main.dart
final widget = state.maybeWhen(
onSuccess: (data, _) => UserCard(data),
orElse: () => const CircularProgressIndicator(),
);
Initial<T>
The query has not been fetched yet. Default state for any key that has never been loaded or has been removed from cache.
main.dart
const Initial<User>()
state.isInitial // true
state.hasData // false
state.dataOrNull // null
Loading<T>
A fetch is actively in progress.
Fields
| Field | Type | Description |
|---|---|---|
previousData | T? | Last successful data (null on first fetch) |
main.dart
Loading<User>() // first fetch
Loading<User>(previousData: cachedUser) // background reload
Properties (extensions)
| Property | Type | Description |
|---|---|---|
isFirstLoad | bool | true when previousData == null |
isRefreshing | bool | true when previousData != null |
hasData | bool | true when previousData != null |
user_page.dart
case Loading(:final previousData):
return previousData != null
? Stack(children: [UserCard(previousData), const LinearProgressIndicator()])
: const CircularProgressIndicator();
Success<T>
Data was fetched successfully.
Success: Fields
| Field | Type | Description |
|---|---|---|
data | T | The fetched data |
updatedAt | DateTime | Timestamp of the last successful fetch |
Properties and methods
| Member | Type | Description |
|---|---|---|
age | Duration | Time elapsed since updatedAt |
isStale(Duration threshold) | bool | true if age > threshold |
successDataOrNull (ext.) | T? | Returns data (null on other states) |
user_page.dart
case Success(:final data, :final updatedAt):
return Column(children: [
UserCard(data),
Text('Updated ${_timeAgo(updatedAt)}'),
]);
Factory
main.dart
Success.now(data) // equivalent to Success(data: data, updatedAt: DateTime.now())
Failure<T>
The fetch failed after all retry attempts.
Failure: Fields
| Field | Type | Description |
|---|---|---|
error | Object | The thrown error |
stackTrace | StackTrace? | Optional stack trace |
previousData | T? | Last successful data (enables graceful degradation) |
user_page.dart
case Failure(:final error, :final previousData):
return Column(children: [
if (previousData != null) ...[
UserCard(previousData), // show stale data
const SizedBox(height: 8),
],
ErrorBanner('$error'),
]);
Extensions on QoraState<T>
Exported from package:qora/qora.dart via state_extensions.dart.
Data access
main.dart
state.requireData() // T: throws StateError if no data
state.successDataOrNull // T?: data only from Success
state.updatedAt // DateTime?: updatedAt from Success, null otherwise
state.status // QoraStatus enum (initial/loading/success/error)
Mapping
main.dart
state.mapData((data) => data.name) // same as map: Success data and previousData
state.fold(
onInitial: () => 'not loaded',
onLoading: (prev) => 'loading...',
onSuccess: (data, _) => data.name,
onError: (error, _, __) => 'error',
)
Combining
main.dart
// Two states → one combined state (Success only when both are Success)
userState.combine(postsState, (user, posts) => UserWithPosts(user, posts))
// Lists and tuples
QoraStateUtils.combineList([s1, s2, s3])
QoraStateUtils.combine2(userState, settingsState)
QoraStateUtils.combine3(userState, postsState, tagsState)
Extensions on Stream<QoraState<T>>
main.dart
stream.whereHasData() // only states with data
stream.whereSuccess() // only Success states
stream.whereError() // only Failure states
stream.dataOrNull() // Stream<T?>: null for no-data states
stream.data() // Stream<T>: skips no-data states
stream.mapData((data) => transform(data)) // Stream<QoraState<R>>
stream.delayLoading(Duration(milliseconds: 300))
Serialization: QoraStateCodec<T>
cache_storage.dart
final codec = QoraStateCodec<User>(
encode: (user) => user.toJson(),
decode: (json) => User.fromJson(json as Map<String, dynamic>),
);
// Encode
final json = codec.encodeState(state); // Map<String, dynamic>
// Decode
final restored = codec.decodeState(json); // QoraState<User>
QoraStatus Enum
A simplified status enum for cases where the full sealed class is not needed:
main.dart
state.status // QoraStatus.initial | .loading | .success | .error
state.status.isLoading // bool
state.status.isSuccess // bool
state.status.isError // bool