winche_storage 4.0.0 copy "winche_storage: ^4.0.0" to clipboard
winche_storage: ^4.0.0 copied to clipboard

Dart SDK for the WincheStorage backend - a reference-based API with resumable, multipart-aware upload and download tasks for any storage backend.

winche_storage #

Dart SDK for the WincheStorage file management backend. Provides resumable, multipart-aware upload and download tasks behind a reference-based API, with an optional durable offline cache and auto-resume layer.

Features #

  • Reference-based ChildReference API (storage.child('a/b/c.jpg')).
  • Resumable, multipart-aware uploads from a file path or raw bytes.
  • Resumable downloads with HTTP Range support.
  • Pause / resume / cancel on both upload and download tasks, with progress streams.
  • Offline cache: keep files available offline — cache: true on upload (no download roundtrip) or makeAvailableOffline() after. Server reads (getSnapshot/listChildren) and cache reads (offlineSnapshot/ offlineChildren) are separate; content-aware freshness via offlineCopyStatus().
  • Durable transfers: enqueue: true queues an upload/download so it survives an app restart and retries with backoff — start it while offline and just await it. Reattach a progress UI after a restart with uploadFor/downloadFor.
  • Per-operation control: enqueue (durable) and cache (keep-offline) flags; transfers expose a queued state and resolve only on the terminal outcome.
  • Pure Dart — no Flutter dependency. Persistence via sembast (file on native, IndexedDB on web), or fully in-memory.
  • Pluggable backend via the WincheStorageApi interface (WincheStorageHttpApi ships by default).
  • Typed exceptions (WincheStorageException and subclasses).

Installation #

dart pub add winche_storage

Or add it to pubspec.yaml:

dependencies:
  winche_storage: ^4.0.0

Setup #

WincheStorage is ready to use as soon as it's constructed — there is no initialize step. The durable transfer queue and offline cache are enabled simply by configuring a store (see the note under the example).

import 'package:winche_storage/winche_storage.dart';

final storage = WincheStorage(
  WincheStorageConfig(
    uri: Uri.parse('https://your-api.example.com/files'),

    // Optional. Returns the current auth token, re-read on every request, so a
    // rotated token is picked up automatically. Sent as `Authorization: Bearer`.
    tokenProvider: () async => currentToken,

    // Required unless `inMemory: true`. Scopes all local state to one identity
    // — return the signed-in user's id. See "Multi-user" below.
    namespaceResolver: () => currentUserId,

    // Resolves the parent directory the identity-scoped root is created in.
    // Its presence (or `inMemory`, or web) enables the durable transfer queue
    // and offline cache — there are no separate enable flags.
    directoryResolver: () async {
      final dir = await getApplicationDocumentsDirectory(); // from path_provider
      return '${dir.path}/winche_files';
    },

    // Optional. Files larger than this are uploaded in parts. Defaults to 5 MiB.
    multipartThreshold: 5 * 1024 * 1024,

    // Optional. Use an in-memory index (catalog + transfer queue) instead of
    // sembast — files still go to disk. Handy for tests. Defaults to false.
    inMemory: false,

    // Optional. Backoff tuning for the durable transfer queue's retries.
    retryBaseDelay: Duration(seconds: 1),
    retryMaxDelay: Duration(seconds: 30),
    retryMaxAttempts: 5,
    retryPollInterval: Duration(seconds: 30),
  ),
);

Store presence enables the subsystems. The durable transfer queue and offline cache exist whenever there's somewhere to put a store: a directoryResolver (native), inMemory: true, or web (IndexedDB). With none of those configured on native the SDK is fully stateless — basic upload/ download still work (download takes an explicit path), and durable/offline operations throw StateError at call time.

Call await storage.close() when you're done. It aborts in-flight transfers, cancels the retry timers and closes the local store, in that order. It is idempotent and never waits on the network: durable transfers are left resumable, while one-shot transfers fail with StorageCancelledException.

Multi-user #

Local state is single-tenant. The offline catalog, the cached bytes and the durable transfer queue carry no identity of their own, so one store shared between two users means the second user reads the first user's pinned files, and the first user's queued uploads replay under the second user's token.

namespaceResolver is therefore required for a persistent store, and gives each identity its own directory:

<directoryResolver()>/winche_storage_<namespace>/index.db   sembast: catalog + queue
<directoryResolver()>/winche_storage_<namespace>/cache/…    cached bytes
<directoryResolver()>/winche_storage_<namespace>/staging/…  pinned uploads in flight

On the web there are no files; the namespace becomes the IndexedDB database name.

It resolves lazily on first store access and is then cached — it pins the identity for the lifetime of that WincheStorage. Returning a different value later does not migrate a live instance. Switching users is:

await storage.close();
storage = WincheStorage(WincheStorageConfig(
  uri: uri,
  namespaceResolver: () => newUserId,
  directoryResolver: directoryResolver,
));

Await the close() before building the replacement — it resolves only once the store is really closed. The previous user's queued transfers stay on disk and resume when they sign back in.

The resolved namespace must match [A-Za-z0-9._-]+ (it becomes a directory name). It is validated, not sanitised: quietly rewriting a user id could collapse two identities onto one store, which is exactly the leak this prevents.

With inMemory: true there is no persistence to scope, so namespaceResolver must be omitted.

How a transfer flows #

The two per-call flags (cache, enqueue) and the durable lifecycle at a glance:

flowchart TD
    Start(["uploadPath / uploadBytes / download"]) --> Cache{"cache: true?<br/>(uploads)"}
    Cache -->|yes| Stage["stage source into the offline cache,<br/>upload from the staged copy"]
    Cache -->|no| Enq{"enqueue: true?<br/>(uploadPath / download)"}
    Stage --> Enq

    Enq -->|no| Direct["one-shot task → running"]
    Direct --> DirOut{"success?"}
    DirOut -->|yes| Done(["complete"])
    DirOut -->|"no (e.g. offline)"| Failed(["failed"])

    Enq -->|yes| Queued["tracked handle: queued"]
    Queued --> Attempt["controller drives one attempt → running"]
    Attempt --> Outcome{"outcome"}
    Outcome -->|success| Done2(["complete · dropped from queue<br/>offline copy finalized if cache:true"])
    Outcome -->|"transient / offline"| Backoff["back to queued · backoff"]
    Backoff --> Attempt
    Outcome -->|"retries exhausted / permanent"| Failed2(["failed"])

A tracked (enqueue: true) handle survives an app restart: on construction the controller rehydrates the queue and re-drives each transfer from queued. Its whenDone resolves only at a terminal node (complete / failed), so you can start a transfer offline and just await it.

Usage #

References #

ChildReference points to a file by its slash-separated path. References compose via .child().

final userRoot = storage.child('userFiles/user-123');
final photoRef = userRoot.child('photo.jpg');
// equivalent to storage.child('userFiles/user-123/photo.jpg')

photoRef.name;     // 'photo.jpg'  — last path segment
photoRef.path;     // 'userFiles/user-123/photo.jpg'
photoRef.parent;   // ChildReference('userFiles/user-123')

Upload #

Upload from a local file path with uploadPath, or from bytes with uploadBytes.

final task = photoRef.uploadPath(
  '/local/path/photo.jpg',
  mimeType: 'image/jpeg',     // optional — inferred from the extension if omitted
  metadata: {'label': 'cover'},
);

// Or from bytes (mimeType is required, as it can't be inferred):
final task = photoRef.uploadBytes(
  bytes,
  'image/jpeg',
  metadata: {'label': 'cover'},
);

// Stream progress
task.stateStream.listen((UploadTaskState state) {
  print('${state.status} — ${(state.progress * 100).toStringAsFixed(1)}%');
});

final FileSnapshot? snapshot = await task.whenDone; // null if cancelled

Uploading to a path that already has a file:

  • Completed file, identical size + MIME — skipped; the existing record is returned without re-uploading.
  • Completed file, different size or MIME — replaced (the old object is deleted and the new content uploaded).
  • Interrupted upload, identical size + MIME — resumed from the last completed part.
  • Interrupted upload, different size or MIME — discarded and re-uploaded from scratch (so a previously failed attempt never blocks the path).

Files at or below multipartThreshold upload in a single request; larger files are uploaded in parts.

Per-call flags: enqueue and cache

Two optional flags make an upload robust:

  • enqueue: true — durable: the upload joins the transfer queue, is deduped by path, survives an app restart, and retries until it succeeds (so it can be started while offline). File-backed only (uploadPath); requires a configured store. Without it, the upload is a one-shot.
  • cache: true — keep it available offline: the source is staged, uploaded from that staged copy, then moved into the id-keyed offline cache on success — no separate download. Best-effort (a caching failure leaves the upload successful and records a stale pin). Requires a configured offline cache.
// Durable AND offline-kept — start it even while offline; await it; it
// completes once the server is reachable.
final task = photoRef.uploadPath(
  '/local/path/photo.jpg',
  enqueue: true,
  cache: true,
);
await task.whenDone;

A flag whose subsystem isn't configured throws StateError (see Setup). uploadBytes accepts cache (it stages the bytes to disk first) but not enqueue — byte uploads aren't durable; write the bytes to a file and use uploadPath(enqueue: true) for that. The cache upload is the upload-time counterpart to makeAvailableOffline(): same result, reusing the bytes you already have instead of downloading them back.

Download #

download writes the file's bytes to an explicit path. For a managed, offline-cached copy that needs no path, use makeAvailableOffline instead.

final task = photoRef.download('/local/photos/photo.jpg');

task.stateStream.listen((DownloadTaskState state) {
  print('${state.status} — ${(state.progress * 100).toStringAsFixed(1)}%');
});

await task.whenDone;

Pass enqueue: true to make the download durable — it joins the transfer queue and resumes after an app restart, retrying until it succeeds (requires a configured store; throws StateError otherwise). Without it the download is a one-shot.

Pause / resume / cancel #

Both UploadTask and DownloadTask support mid-flight control:

task.pause();
task.resume(); // resumes from the last completed part / byte offset

// Upload cancel — also deletes the remote file record
await task.cancel();

// Download cancel — deletes any partially written local file
task.cancel();

Offline cache #

Requires a configured store (see Setup). Pin a file to download it into a managed, id-keyed cache directory and track it so it stays available offline.

If you're the one uploading the file, prefer uploadPath(..., cache: true) (see Upload) — it populates the cache from the bytes you already have, skipping the download this method would otherwise perform.

// Download + pin for offline use. Completes when the file is on disk.
await photoRef.makeAvailableOffline();

// On a directory path it pins every file directly under it (one level — the
// server lists a single level, so nested sub-directories aren't included):
await storage.child('userFiles/user-123').makeAvailableOffline();

// What changed about the pinned copy? (content overwrite vs deleted vs current)
switch (await photoRef.offlineCopyStatus()) {
  case OfflineCopyStatus.contentChanged:
    await photoRef.refreshOfflineCopy(); // bytes changed — re-download
  case OfflineCopyStatus.remoteDeleted:
    await photoRef.removeOfflineCopy();  // gone — drop the local copy
  case OfflineCopyStatus.upToDate:
  case OfflineCopyStatus.notPinned:
  case OfflineCopyStatus.unknown:        // offline / no fingerprint — leave as-is
    break;
}

// Drop the local copy and catalog entry.
await photoRef.removeOfflineCopy();

// Remove every pinned file.
await storage.clearOfflineCache();

Reads split cleanly into server and cache — each call has one source:

  • getSnapshot() / listChildren() are server-only: they return the authoritative server record(s) and throw StorageUnavailableException when the server is unreachable. They never consult the cache, so their fromCache is always false and they don't carry isCached / localPath.
  • offlineSnapshot() / offlineChildren() are cache-only: they read the local catalog without contacting the server (fromCache == true), so they keep working offline. Both require a configured store. offlineSnapshot() returns a missing snapshot when the file isn't pinned; offlineChildren() returns the pinned files directly under the path (a partial view, possibly empty).
// Server-only: authoritative, throws when offline.
final FileSnapshot snap = await photoRef.getSnapshot();
if (snap.exists) print('server size: ${snap.data!.sizeBytes}');

// Cache-only: the local copy, no network.
final FileSnapshot offline = await photoRef.offlineSnapshot();
if (offline.exists) {
  print('available offline at: ${offline.data!.localPath}'); // <cacheDir>/<id>.jpg
}

Compose them yourself for a remote-first-with-fallback read:

Future<FileSnapshot> read(ChildReference ref) async {
  try {
    return await ref.getSnapshot();      // server first
  } on StorageUnavailableException {
    return await ref.offlineSnapshot();  // fall back to the cached copy
  }
}

The directory counterpart — annotate a live listing with offline state by cross-referencing the cache-only read:

final live = await userRoot.listChildren();     // full server listing (throws offline)
final cached = await userRoot.offlineChildren(); // pinned-only, works offline
final cachedPaths = {for (final f in cached.files) f.reference.path};
for (final snap in live.files) {
  final badge = cachedPaths.contains(snap.reference.path) ? '✓ offline' : '';
  print('${snap.reference.path} $badge');
}

On a cache-only snapshot, data.localPath is the absolute path to the local copy and data.isCached is true when its content is downloaded. Server-only reads don't carry these — use the offline* reads or offlineCopyStatus().

Cached files are stored at <directoryResolver()>/winche_storage_<namespace>/cache/<fileId><.ext> — keyed by the immutable file id (so they survive path/metadata changes), with an extension derived from the name or MIME type. Pins are explicit and never auto-evicted.

Auto-resume #

Available when a store is configured (see Setup). Uploads and downloads started with enqueue: true are persisted to a durable queue, resumed when the SDK is constructed, and retried on failure with exponential backoff (configurable via the retry* fields on WincheStorageConfig).

// `enqueue: true` makes it durable — queued, resumed after a restart, retried.
final task = photoRef.download('/local/photos/photo.jpg', enqueue: true);

// On app start, the SDK auto-resumes pending transfers. You can also trigger
// drains explicitly (e.g. when connectivity returns):
await storage.resumeDownloads();
await storage.resumeUploads();

// Resume a single path's transfer.
await photoRef.resumeTransfer();

// Observe lifecycle events as the queue drains.
storage.transferEvents.listen((TransferEvent e) {
  // started | completed | failed | retrying | paused
  print('${e.type} ${e.kind} ${e.path}');
});

How a failed attempt is handled

A durable transfer's response to failure depends on what failed. One uniform "count an attempt, then give up" policy would let an expired token or a flat network destroy queued work in a couple of minutes.

Failure Behaviour
unauthenticated, unavailable Paused. The record and the handle both survive, and no attempt is counted — a paused transfer can wait indefinitely without exhausting its budget. It keeps probing on the usual backoff, so a brief blip recovers in about a second. Emits TransferEventType.paused; the record's status becomes TransferStatus.paused with the reason in lastError.
internal, deadlineExceeded, unknown, and any non-WincheStorageException Retried with exponential backoff, up to retryMaxAttempts. Past the cap the handle fails; the record stays in the queue as failed so pendingTransfers() still surfaces it.
permissionDenied, notFound, invalidArgument, failedPrecondition Terminal. The record is dropped and the handle fails. The failed event carries the full TransferRecord in record, so the work is recoverable — a path and an error alone would lose the source localPath, mimeType and metadata.

After refreshing an auth token, call storage.resumeTransfers() to re-drive everything that paused, rather than waiting out the retryPollInterval backstop:

await refreshMyToken();
await storage.resumeTransfers();

Note that a paused transfer's whenDone does not settle while it is paused — that is the point of the stable handle: you can start an upload while offline and simply await it.

When a transfer's whenDone resolves, it has fully landed: the durable record is already gone from pendingTransfers(), the completed event has already been emitted, and a cache: true upload's offline copy is already committed. You can read any of them on the next line without waiting.

A durable (enqueue: true) transfer is tracked and deduped by path: calling uploadPath/download again for the same path returns the existing handle rather than starting a duplicate. After a restart the original handle object is gone, so reattach a progress UI with storage.uploadFor(path) / downloadFor(path). Per-byte progress stays on the handle's stateStream, which also reports the queued state while it waits to (re)start.

List a directory #

final DirectorySnapshot dir = await storage.child('userFiles/user-123').listChildren(
  mimeType: 'image/jpeg', // optional filter
);

for (final snapshot in dir.files) {
  print('${snapshot.reference.path} — ${snapshot.data?.sizeBytes} bytes');
}

listChildren() is server-only — it throws StorageUnavailableException when offline. For the locally pinned files, use offlineChildren() (see Offline cache).

Get file metadata #

getSnapshot() always returns a FileSnapshot. Check exists to know whether the file is present; data is null when it isn't. It's server-only (throws when offline); for the cached copy use offlineSnapshot() — see the Offline cache section.

final FileSnapshot snapshot = await photoRef.getSnapshot();

if (snapshot.exists) {
  final data = snapshot.data!;
  print(data.id);
  print(data.path);
  print(data.directory);
  print(data.mimeType);
  print(data.sizeBytes);
  print(data.uploadStatus); // UploadStatus.pending | .complete | .failed
  print(data.metadata);
  print(data.version);
  print(data.createdAt);
  print(data.updatedAt);
}

Update metadata #

final FileSnapshot updated = await photoRef.updateMetadata({'label': 'hero'});

Delete #

final bool deleted = await photoRef.delete(); // false if the file didn't exist

API reference #

WincheStorageConfig #

Field Type Default Description
uri Uri required Base URI of the WincheStorage REST backend
tokenProvider FutureOr<String> Function()? null Returns the auth token, re-read per request and sent as Authorization: Bearer
multipartThreshold int 5 * 1024 * 1024 File size (bytes) above which multipart upload is used
inMemory bool false Use an in-memory index (catalog + queue) instead of sembast; files still go to disk. Also enables the subsystems
namespaceResolver FutureOr<String> Function()? null Required unless inMemory (and must be omitted when it is). Scopes all local state to one identity (lazy, cached). Must match [A-Za-z0-9._-]+. See Multi-user
directoryResolver Future<String> Function()? null Resolves the parent directory the identity-scoped root is created in (lazy, cached). Its presence (or inMemory/web) enables the durable queue + offline cache
retryBaseDelay Duration 1s Initial backoff before the first durable-transfer retry
retryMaxDelay Duration 30s Cap on the exponential backoff between retries
retryMaxAttempts int 5 Retries before a transfer fails permanently
retryPollInterval Duration 30s Backstop poll interval that re-drives still-retryable failed transfers, and every paused one

WincheStorage #

Member Description
WincheStorage(config) Creates the SDK. Ready to use immediately; auto-resumes pending transfers when a store is configured.
WincheStorage.withStore(api, store, {...}) Advanced/testing: build over an explicit WincheStorageApi and StorageLocalStore (subsystems always available).
child(path) Returns a ChildReference for the given path.
resumeTransfers() Re-drives every transfer halted by a pause (expired token, unreachable server). Call after refreshing an auth token. Throws StateError when no store is configured.
resumeDownloads() Drains all queued downloads. Throws StateError when no store is configured.
resumeUploads() Drains all queued uploads. Throws StateError when no store is configured.
pendingTransfers({kind}) Snapshot of the durable queue (pending/running/paused/failed TransferRecords), optionally filtered by TransferKind. Throws StateError when no store is configured.
transferEvents Stream<TransferEvent> of queue lifecycle events. Throws StateError when no store is configured.
uploadFor(path) / downloadFor(path) The live tracked transfer handle for path (or null), to reattach a progress UI after a restart. Throws StateError when no store is configured.
clearOfflineCache() Evicts every pinned file. Throws StateError when no store is configured.
close() Aborts in-flight transfers, cancels the retry timers and closes the local store, in that order. Idempotent, never waits on the network. Durable transfers stay resumable; one-shot ones fail with StorageCancelledException. Every member above throws StateError afterwards.
isClosed Whether close() has been called.

ChildReference #

Member Description
path The file's slash-separated path string.
name The last path segment (e.g. photo.jpg).
parent The parent reference, or null at a single-segment path.
child(path) Returns a new ChildReference at this.path/path.
getSnapshot() Fetches metadata from the server only; throws StorageUnavailableException when offline.
listChildren({mimeType}) Lists files under this path from the server only, returning a DirectorySnapshot (its .files holds a FileSnapshot each); throws when offline.
offlineSnapshot() The cached copy's metadata, read from the local catalog only (fromCache: true); missing snapshot when not pinned. Requires a store.
offlineChildren({mimeType}) The locally pinned files directly under this path, read from the local catalog only (fromCache: true, partial, may be empty). Requires a store.
uploadPath(localPath, {mimeType, metadata, multipartThreshold, enqueue, cache}) Starts an UploadTask from a local file. enqueue: true → durable/queued; cache: true → also kept offline (stage-first). Each requires its subsystem, else StateError.
uploadBytes(bytes, mimeType, {metadata, multipartThreshold, cache}) Starts an UploadTask from raw bytes. cache: true keeps it offline. Not durable — use uploadPath(enqueue: true) for that.
download(saveTo, {enqueue}) Starts a DownloadTask to the explicit path saveTo. enqueue: true → durable/queued (else one-shot).
makeAvailableOffline() Pins + downloads the file for offline use; on a directory path, pins every file directly under it (one level). Requires a configured store.
refreshOfflineCopy() Re-downloads the current remote version into the cache. Requires a configured store.
offlineCopyStatus() Future<OfflineCopyStatus> — upToDate / contentChanged / remoteDeleted / notPinned / unknown (offline or no fingerprint). Requires a configured store.
removeOfflineCopy() Removes the local copy + catalog entry. Requires a configured store.
resumeTransfer() Resumes this path's queued/paused durable transfer. Requires a configured store.
updateMetadata(metadata) Updates server-side metadata. Returns a FileSnapshot. If the file is pinned offline, its cached metadata is updated too (content fingerprint preserved).
delete() Deletes the file. Returns bool.

Offline / durable-transfer methods throw StateError when no store is configured (no directoryResolver, no inMemory, on native).

UploadTask #

Member Type Description
state UploadTaskState Current synchronous snapshot of status + progress.
stateStream Stream<UploadTaskState> Broadcast stream of state changes.
whenDone Future<FileSnapshot?> Completes with the confirmed FileSnapshot, or null if cancelled.
pause() — Cancels the in-flight request; preserves uploaded parts.
resume() — Restarts from the last completed part.
cancel() Future<void> Cancels the upload and deletes the remote file record.

UploadTaskStatus: queued, running, paused, complete, failed, cancelled. A durable (enqueue: true) task starts queued; its whenDone resolves only on the terminal outcome (success or permanent failure), surviving offline retries.

DownloadTask #

Member Type Description
state DownloadTaskState Current synchronous snapshot of status + progress.
stateStream Stream<DownloadTaskState> Broadcast stream of state changes.
whenDone Future<void> Completes when the download finishes, or throws on failure.
saveTo String The absolute destination path the file is written to.
pause() — Cancels the in-flight request; the partial file is kept for resume.
resume() — Resumes from the byte offset already written (HTTP Range request).
cancel() — Cancels and deletes any partial local file.

DownloadTaskStatus: queued, running, paused, complete, failed, cancelled (same semantics as UploadTaskStatus).

FileSnapshot #

An immutable snapshot of a file's metadata at a point in time.

Member Type Description
exists bool Whether the file is present.
data FileData? The file record, or null when exists is false.
fromCache bool True when the snapshot came from a cache-only read (offlineSnapshot()); always false for the server-only getSnapshot().
reference ChildReference The reference this snapshot belongs to (use reference.path for the full path).
name String The last path segment.
timestamp DateTime When the snapshot was taken.

FileData fields: id, directory, path, mimeType, sizeBytes, uploadStatus, metadata, version, createdAt, updatedAt, contentHash, plus two client-side offline fields:

contentHash — the server's content fingerprint (object ETag), used by offlineCopyStatus(); null when the backend hasn't recorded one.

Field Type Description
localPath String? Absolute path to the local copy, when pinned/registered.
isCached bool True when the content is fully downloaded locally and ready for offline use.

DirectorySnapshot #

An immutable snapshot of a directory listing, returned by listChildren() (server) or offlineChildren() (cache).

Member Type Description
files List<FileSnapshot> One snapshot per child file (unmodifiable).
fromCache bool True when the listing came from offlineChildren() (a cache-only, partial, pinned-only view); false for the server-only listChildren().
reference ChildReference The directory this snapshot lists.
name String The directory's last path segment.
length / isEmpty / isNotEmpty — Convenience over files.
timestamp DateTime When the snapshot was taken.

Offline / auto-resume types #

Type Description
TransferEvent {type, kind, path, error, record} emitted on transferEvents. record is the TransferRecord on a failed event, so dropped work can be re-enqueued.
TransferEventType started, completed, failed, retrying, paused.
TransferKind upload, download.
TransferRecord / TransferStatus A persisted queue entry returned by pendingTransfers — {seq, kind, path, status, attempt, lastError, …}; status is pending, running, paused, or failed. A paused transfer is waiting on a token or the network and has spent no attempts.
CatalogEntry / CatalogStatus A pinned file record (downloading, ready, stale).
OfflineCopyStatus notPinned, upToDate, contentChanged, remoteDeleted, unknown — result of offlineCopyStatus().
StorageLocalStore Persistence interface; MemoryStorageLocalStore and SembastStorageLocalStore ship by default.

WincheStorageException #

A sealed exception hierarchy thrown on API errors. Each carries a semantic status (StorageErrorStatus), a message, optional details, and the originating statusCode.

try {
  await photoRef.updateMetadata({'label': 'hero'});
} on StorageNotFoundException catch (e) {
  print('not found: ${e.message}');
} on WincheStorageException catch (e) {
  print('${e.statusCode}: ${e.message}');
}

Subclasses: StorageNotFoundException, StoragePermissionDeniedException, StorageUnauthenticatedException, StorageInvalidArgumentException, StorageFailedPreconditionException, StorageDeadlineExceededException, StorageUnavailableException, StorageCancelledException, StorageInternalException, StorageUnknownException.

Dependencies #

  • dio — HTTP client used by WincheStorageHttpApi, UploadTask, and DownloadTask
  • mime — MIME type inference from file extension in ChildReference.uploadPath
  • path — platform-correct path joining for the offline cache
  • sembast / sembast_web — pure-Dart durable store for the offline catalog and transfer queue (native file / web IndexedDB)

License #

MIT

0
likes
0
points
16
downloads

Publisher

verified publisherwinchetechnologies.co.uk

Weekly Downloads

Dart SDK for the WincheStorage backend - a reference-based API with resumable, multipart-aware upload and download tasks for any storage backend.

Repository (GitHub)
View/report issues

License

unknown (license)

Dependencies

dio, mime, path, sembast, sembast_web

More

Packages that depend on winche_storage