OfflineOutboxService class

Persisted, priority-drained outbox for syncing local changes to a remote authority once back online — built on RetryExecutor (FEAT-35) for transient-failure backoff and, when connectivity is supplied, driven automatically by ConnectivityCoordinator (FEAT-62)'s online transitions the same way that service drains its own internal task queue.

Lifecycle: an item is enqueued → sits pending → drain calls uploader for it → either SyncAck (item removed) or SyncConflict (handled per conflictPolicy, see there) → a ConflictPolicy.manual or unresolved ConflictPolicy.merge conflict parks it in manualReviewItems until resolveManual.

Crash safety: an item stays in the persisted outbox from enqueue until the exact call that removes it also persists that removal — so a crash at any point before that (including between the remote authority accepting it and this service recording that) leaves the item still queued for the next drain, which retries it with the same OutboxItem.idempotencyKey. Nothing is ever lost; avoiding a duplicate effect on the remote side is the injected uploader's responsibility (a real backend keys on OutboxItem.idempotencyKey to dedupe a retried request it already applied) — this service only guarantees it always retries safely, never that it never retries.

Error handling: every public method returns SdkResult — this service never throws for a caller-facing error.

Compatibility: storageKey namespaces the persisted JSON blob the same way every other kit service's storageKey does — pass a distinct one per independent outbox instance (e.g. one per save slot).

Inheritance
  • Object
  • GetLifeCycle
  • DisposableInterface
  • GetxService
  • OfflineOutboxService

Constructors

OfflineOutboxService({required StorageService storage, required OutboxUploader uploader, ConnectivityCoordinator? connectivity, ConflictPolicy conflictPolicy = ConflictPolicy.manual, ConflictMerger? merger, int capacity = 200, RetryPolicy retryPolicy = const RetryPolicy(maxAttempts: 3), RetryExecutor? retryExecutor, String? storageKey})

Properties

capacity → int
final
conflictPolicy → ConflictPolicy
final
connectivity → ConnectivityCoordinator?
final
hashCode → int
The hash code for this object.
no setterinherited
initialized → bool
Checks whether the controller has already been initialized.
no setterinherited
isClosed → bool
Checks whether the controller has already been closed.
no setterinherited
items → RxList<OutboxItem>
final
manualReviewItems → List<OutboxItem>
no setter
merger → ConflictMerger?
final
onDelete → InternalFinalCallback<void>
Internal callback that starts the cycle of this controller.
finalinherited
onStart → InternalFinalCallback<void>
Called at the exact moment the widget is allocated in memory. It uses an internal "callable" type, to avoid any @overrides in subclases. This method should be internal and is required to define the lifetime cycle of the subclass.
finalinherited
retryPolicy → RetryPolicy
final
runtimeType → Type
A representation of the runtime type of the object.
no setterinherited
storage → StorageService
final
uploader → OutboxUploader
final

Methods

$configureLifeCycle() → void
inherited
drain() → Future<void>
Attempts to sync every non-OutboxItem.manualReview item, highest-OutboxItem.priority first. Safe to call concurrently with itself (a no-op re-entry) or with enqueue/resolveManual.
enqueue({required String idempotencyKey, required Map<String, Object?> payload, int priority = 0, int? expiresAtMs}) → SdkResult<void>
Queues payload under idempotencyKey — replaces any existing item with the same key rather than duplicating it. Bounded at capacity: once full, the lowest-priority item is evicted to make room.
noSuchMethod(Invocation invocation) → dynamic
Invoked when a nonexistent method or property is accessed.
inherited
onClose() → void
Called before onDelete method. onClose might be used to dispose resources used by the controller. Like closing events, or streams before the controller is destroyed. Or dispose objects that can potentially create some memory leaks, like TextEditingControllers, AnimationControllers. Might be useful as well to persist some data on disk.
override
onInit() → void
Called immediately after the widget is allocated in memory. You might use this to initialize something for the controller.
override
onReady() → void
Called 1 frame after onInit(). It is the perfect place to enter navigation events, like snackbar, dialogs, or a new route, or async request.
inherited
resolveManual({required String idempotencyKey, required ManualResolution resolution}) → SdkResult<void>
Resolves an item sitting in manualReviewItems. ManualResolution.keepLocal re-queues its original payload for another drain attempt; ManualResolution.acceptRemote removes it — the local change is abandoned, nothing further is attempted.
toString() → String
A string representation of this object.
inherited

Operators

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

Static Properties

maybe → OfflineOutboxService?
no setter