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)

PropertyTypeDescription
hasDatabooltrue for Success, or Loading/Failure with prior data
dataOrNullT?Best available data across all states, or null
isInitialbooltrue if Initial
isLoadingbooltrue if Loading
isSuccessbooltrue if Success
isErrorbooltrue if Failure
errorOrNullObject?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

FieldTypeDescription
previousDataT?Last successful data (null on first fetch)
main.dart
Loading<User>()                          // first fetch
Loading<User>(previousData: cachedUser)  // background reload

Properties (extensions)

PropertyTypeDescription
isFirstLoadbooltrue when previousData == null
isRefreshingbooltrue when previousData != null
hasDatabooltrue 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

FieldTypeDescription
dataTThe fetched data
updatedAtDateTimeTimestamp of the last successful fetch

Properties and methods

MemberTypeDescription
ageDurationTime elapsed since updatedAt
isStale(Duration threshold)booltrue 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

FieldTypeDescription
errorObjectThe thrown error
stackTraceStackTrace?Optional stack trace
previousDataT?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
Copyright © 2026