Repository navigation
Offline Write Queue
OfflineWriteQueue is a middleware that keeps writes that failed because the device is offline and replays them later, in order. Reads are never queued.
final queue = OfflineWriteQueue(store: MemoryWriteQueueStore());
final client = WordpressClient.forSite(
siteUrl: siteUrl,
bootstrapper: (b) => b.withMiddleware(queue).withDefaultAuthorization(auth).build(),
);
final r = await client.posts.create(CreatePostRequest(title: 'Hi'));
if (r.wasQueued) {
showSnack('Saved, will sync when online'); // r is still the failure response
}
// when connectivity returns, for example from a connectivity_plus listener
final result = await queue.flush(client);
print('${result.sent.length} sent, ${result.rejected.length} refused, ${result.remaining} left');| Behaviour | |
|---|---|
| Queued | POST, PUT, PATCH, DELETE (methods) that fail with connectionFailed. Timeouts only with queueOnTimeout: true, because the server may already have processed the write and a replay could duplicate it |
| Not queued | Reads, server errors (4xx/5xx), cancellations, uploads and other non JSON bodies |
flush |
Oldest first. Accepted writes are removed. A 4xx refusal (rest_forbidden, ...) is removed and listed in rejected. Stops at the first write that still cannot be delivered (offline, 5xx, 408, 429) and keeps it and the rest |
maxLength |
Queue size cap (default 100); writes beyond it are not queued |
| Stored | Method, URL, query, body and the requireAuth flag. Headers and credentials are not stored; the replay is authorized by the client you flush with |
Stores:
| Store | Keeps the queue |
|---|---|
MemoryWriteQueueStore() |
Until the app stops |
JsonWriteQueueStore(read: ..., write: ...) |
In any text storage (shared_preferences, localStorage), on every platform |
FileWriteQueueStore(path) from package:wordpress_client/io.dart
|
In a JSON file, written atomically. Not on the web |
your own WriteQueueStore
|
Wherever you like (all, add, remove); QueuedWrite has toJson / fromJson
|
final queue = OfflineWriteQueue(
store: JsonWriteQueueStore(
read: () async => prefs.getString('wp_queue'),
write: (json) => prefs.setString('wp_queue', json),
),
);Queued bodies (the post you were saving, for example) are stored as plain text. Headers and credentials are not.
The original call still returns its failure (connectionFailed), because nothing reached the server. response.wasQueued tells you it was kept, and response.extra[OfflineWriteQueue.queuedKey] holds the QueuedWrite.id.
final waiting = await queue.pending; // List<QueuedWrite>, oldest first
final count = await queue.length;A QueuedWrite has method, url, query, body, queuedAt and id.
JsonWriteQueueStore covers most cases. To keep the queue in a database, implement WriteQueueStore:
class PrefsStore implements WriteQueueStore {
PrefsStore(this.prefs);
final SharedPreferences prefs;
static const _key = 'wp_queue';
List<QueuedWrite> _read() => [
for (final s in prefs.getStringList(_key) ?? const <String>[])
QueuedWrite.fromJson(jsonDecode(s) as Map<String, dynamic>),
];
@override
Future<List<QueuedWrite>> all() async => _read();
@override
Future<void> add(QueuedWrite w) =>
prefs.setStringList(_key, [..._read().map((e) => jsonEncode(e.toJson())), jsonEncode(w.toJson())]);
@override
Future<void> remove(String id) => prefs.setStringList(
_key, [for (final w in _read()) if (w.id != id) jsonEncode(w.toJson())]);
}More ideas for storage: Integration with Storage Packages.
Connectivity().onConnectivityChanged.listen((results) {
if (!results.contains(ConnectivityResult.none)) queue.flush(client);
});Calling flush while another is running returns the running one, so it is safe to trigger from several places.
- Authentication is not stored. Flush with a client that has valid credentials; a write refused with 401/403 is reported in
rejectedand dropped. - The server may have changed since the write was made. Conflicts surface as 4xx answers in
rejected. - Queued writes do not touch the cache until they are sent.
Getting started
- Usage
- Bootstrap and Configuration
- Authorization
- Browser Sign-In
- WordPress.com and Jetpack
- Multisite
- Supported REST Methods
Requests and data
- Sending Requests
- Error Handling and Responses
- Fluent Queries
- Interfaces and Extensions
- Custom Post Types
- Model Generator
- Meta and ACF
- Concurrent Edits
- Batch Requests
- Embed and Fields
- Pagination and Finders
- Parallel Requests
- Streaming and Polling
- Pattern Directory Items
Behaviour
- Retries
- Caching
- Request De-duplication and Concurrency
- Middlewares
- Logging
- Offline Write Queue
- Events and Statistics
Other APIs
Extend and test
Reference