Syncache<T> class

An offline-first cache and sync engine for Dart applications.

Syncache provides a unified API for caching data with multiple strategies, reactive updates via streams, and optimistic mutations with automatic background sync.

Basic Usage

// Create a cache instance
final cache = Syncache<User>(
  store: MemoryStore<User>(),
);

// Fetch data with offline-first policy
final user = await cache.get(
  key: 'user:123',
  fetch: (request) => api.getUser(123),
);

// Watch for reactive updates
cache.watch(
  key: 'user:123',
  fetch: (request) => api.getUser(123),
).listen((user) {
  print('User updated: ${user.name}');
});

Caching Policies

Control caching behavior with Policy:

Optimistic Mutations

await cache.mutate(
  key: 'user:123',
  mutation: Mutation<User>(
    apply: (user) => user.copyWith(name: 'New Name'),
    send: (user) => api.updateUser(user),
  ),
);

Network Awareness

Provide a custom Network implementation for offline detection:

final cache = Syncache<User>(
  store: MemoryStore<User>(),
  network: MyConnectivityNetwork(),
);

See also:

Available extensions

Constructors

Syncache({required Store<T> store, Network? network, List<SyncacheObserver>? observers, RetryConfig? defaultRetry, MutationRetryConfig? mutationRetry, MutationOutbox? outbox, T decodeOptimisticSnapshot(Object? snapshot)?, Duration? cleanupInterval, int? opportunisticCleanupEvery, ConflictResolver<T>? conflictResolver})
Creates a Syncache instance with the given store and optional network.

Properties

cleanupInterval → Duration?
How often a periodic expired-entry sweep runs, or null (the default) for manual-only cleanup via cleanup.
final
conflictResolver → ConflictResolver<T>?
Cache-wide default ConflictResolver, consulted when a mutation's send throws a SyncacheConflictException (typically HTTP 409) and the call did not pass its own resolver via mutate(conflictResolver:).
final
decodeOptimisticSnapshot → T Function(Object? snapshot)?
Decodes a persisted optimistic snapshot (see mutate) back into a T during recoverPendingMutations.
final
defaultRetry → RetryConfig
final
hashCode → int
The hash code for this object.
no setterinherited
hasPendingMutations → bool
Returns whether there are any pending mutations.
no setter
isSyncedStream → Stream<bool>
Stream that emits true when all mutations are synced.
no setter
mutationRetry → MutationRetryConfig
final
network → Network
final
observers → List<SyncacheObserver>
final
opportunisticCleanupEvery → int?
Opportunistic cleanup frequency: run a sweep at most once every N writes, or null (the default) to disable opportunistic sweeping.
final
outbox → MutationOutbox?
Optional persistence backend for pending mutations.
final
pendingMutationCount → int
Returns the number of pending mutations.
no setter
pendingMutationsSnapshot → List<PendingMutationInfo>
Returns a snapshot of current pending mutations.
no setter
pendingMutationsStream → Stream<List<PendingMutationInfo>>
Stream of pending mutation info for UI sync indicators.
no setter
runtimeType → Type
A representation of the runtime type of the object.
no setterinherited
stats → Future<SyncacheStats>
Returns an immutable snapshot of the cache's health counters.
no setter
store → Store<T>
final

Methods

addObserver(SyncacheObserver observer) → void
Registers observer for all cache events, in addition to any observers passed to the constructor. Unlike the constructor list, observers can be attached and detached at any time — used by helpers that observe a cache they were given (e.g. PaginatedCache).
cleanup() → Future<int>
Removes every expired entry from the store and returns the count.
clear({bool refetchWatchers = true}) → Future<void>
Clears all cached entries.
clearPendingMutations() → void
Removes all pending mutations from the sync queue.
clearScope(String scope, {bool refetchWatchers = true}) → Future<void>
Clears all cached entries within a scope.
debugSnapshot({int sampleKeyLimit = 20}) → Future<Map<String, Object?>>
Builds a JSON-safe snapshot of the cache's live internal state for debugging and diagnostics.
dispose() → void
Disposes of this Syncache instance and releases all resources.
entry(String key, {required Fetcher<T> fetch, Policy policy = Policy.offlineFirst, Duration? ttl, List<String>? tags}) → SyncacheEntry<T>
Creates a typed SyncacheEntry bound to key, fetch and the given default cache parameters.
get({required String key, required Fetcher<T> fetch, Policy policy = Policy.offlineFirst, Duration? ttl, RetryConfig? retry, CancellationToken? cancel, List<String>? tags}) → Future<T>
Retrieves a value from the cache or network based on the policy.
getConditional({required String key, required ConditionalFetcher<T> fetch, Policy policy = Policy.offlineFirst, Duration? ttl, RetryConfig? retry, CancellationToken? cancel}) → Future<T>
Retrieves a value using a ConditionalFetcher that supports HTTP 304.
getWithMeta({required String key, required Fetcher<T> fetch, Policy policy = Policy.offlineFirst, Duration? ttl, RetryConfig? retry, CancellationToken? cancel}) → Future<CacheResult<T>>
Retrieves a value with cache metadata.
invalidate(String key) → Future<void>
Invalidates (deletes) the cached entry for the given key.
invalidateInScope(String scope, String pattern) → Future<void>
Invalidates entries within a scope matching a pattern.
invalidatePattern(String pattern) → Future<void>
Invalidates all cache entries whose keys match the given glob pattern.
invalidateTag(String tag) → Future<void>
Invalidates all cache entries with the specified tag.
invalidateTags(List<String> tags, {bool matchAll = false}) → Future<void>
Invalidates all cache entries that have any of the specified tags.
mutate({required String key, required Mutation<T> mutation, List<String>? invalidates, List<String>? invalidateTags, Map<String, Object?>? payload, Object? optimisticSnapshot, ConflictResolver<T>? conflictResolver}) → Future<MutationHandle<T>>
Applies an optimistic mutation to a cached value.
mutateList({required String key, required ListOperation<T> operation, required Future<void> send(), List<String>? invalidates, List<String>? invalidateTags}) → Future<void>

Available on Syncache<List<T>>, provided by the SyncacheListExtension extension

Mutates a cached list with a specific operation.
mutateListItem<Id>({required String key, required ListOperation<T> operation, required Future<T> send(T item), required Id idSelector(T item), List<String>? invalidates, List<String>? invalidateTags}) → Future<void>

Available on Syncache<List<T>>, provided by the SyncacheListExtension extension

Mutates a cached list with server response updating the item.
noSuchMethod(Invocation invocation) → dynamic
Invoked when a nonexistent method or property is accessed.
inherited
prefetch(List<PrefetchRequest<T>> requests) → Future<List<PrefetchResult>>
Prefetches multiple cache entries in parallel.
prefetchGraph(List<PrefetchNode<T>> nodes, {PrefetchGraphOptions options = PrefetchGraphOptions.defaults}) → Future<PrefetchGraphResult>
Prefetches multiple cache entries with dependency ordering.
prefetchOne({required String key, required Fetcher<T> fetch, Duration? ttl, Policy policy = Policy.refresh, RetryConfig? retry}) → Future<bool>
Prefetches a single cache entry. Returns true on success.
processPendingMutations() → Future<void>
Processes pending mutations now, without waiting for the next mutate() call or a reconnect event.
recoverPendingMutations(Future<Mutation<T>> resolve(PendingMutationRecord record)) → Future<int>
Replays persisted outbox records as live pending mutations.
removeFirstPendingMutation() → bool
Removes the first pending mutation from the sync queue.
removeObserver(SyncacheObserver observer) → void
Unregisters an observer previously attached via addObserver. Silently does nothing if it is not registered.
removePendingMutation(String id) → bool
Removes the pending mutation with the given id from the sync queue.
resetStats([Set<SyncacheCounter>? counters]) → void
Resets stats counters.
scoped(String scope) → ScopedSyncache<T>
Creates a scoped view of this cache.
set({required String key, required T value, Duration? ttl}) → Future<void>
Sets a value directly in the cache without fetching.
toString() → String
A string representation of this object.
inherited
watch({required String key, required Fetcher<T> fetch, Policy policy = Policy.offlineFirst, Duration? ttl}) → Stream<T>
Returns a reactive stream of values for the given key.
watchWithDependencies({required String key, required Fetcher<T> fetch, required List<String> refetchWhen, Policy policy = Policy.offlineFirst, Duration? ttl}) → Stream<T>
Returns a reactive stream that automatically refetches when dependencies change.
watchWithMeta({required String key, required Fetcher<T> fetch, Policy policy = Policy.offlineFirst, Duration? ttl}) → Stream<CacheResult<T>>
Returns a reactive stream of values with metadata for the given key.

Operators

operator ==(Object other) → bool
The equality operator.
inherited