offline_outbox 1.1.2
offline_outbox: ^1.1.2 copied to clipboard
Resilient offline-first outbox and retry queue for Dart and Flutter with disk persistence, exponential backoff, priority scheduling, and deduplication.
offline_outbox #
β‘ Why offline_outbox? #
In mobile development, network connections flicker constantly. Standard in-memory retry libraries fail when the app process is terminated or the phone restarts.
offline_outbox provides the Transactional Outbox Pattern for Flutter:
- Never Lose a Mutation: When the user places an order, sends a message, or likes a post, the request is written to persistent disk storage before network delivery.
- Exponential Backoff with Random Jitter: Failed network requests automatically back off exponentially with randomized jitter to prevent server thundering-herd spikes on reconnect.
- Idempotency & Deduplication: Prevents duplicate charges or double orders by checking unique
idempotencyKeyvalues before enqueueing. - 4-Tier Priority Scheduling:
OutboxPriority.criticalrequests (payments, bookings) are drained beforeOutboxPriority.lowrequests (analytics, logs). - Storage & HTTP Agnostic: Works seamlessly with Dio,
package:http, Hive, SQLite, or the built-inFileJsonOutboxStorage.
π¦ Installation #
Add offline_outbox to your pubspec.yaml:
dependencies:
offline_outbox: ^1.1.1
Or run:
flutter pub add offline_outbox
π Quick Start #
1. Initialize Engine #
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:offline_outbox/offline_outbox.dart';
// Create storage (File-based, Hive, or InMemory)
final storage = FileJsonOutboxStorage(File('/path/to/outbox.json'));
final outbox = OfflineOutboxEngine(
storage: storage,
retryPolicy: const RetryPolicy(
maxAttempts: 5,
initialDelay: Duration(seconds: 2),
maxDelay: Duration(minutes: 2),
backoffMultiplier: 2.0,
enableJitter: true,
),
executor: (request) async {
// Perform your actual HTTP / Dio request here:
// final response = await dio.request(request.endpoint, data: request.payload, ...);
// return response.statusCode == 200;
return true; // Return true on success, false/throw on retryable error
},
);
await outbox.init();
2. Enqueue an Offline Request #
await outbox.enqueue(
OutboxRequest(
id: 'req_${DateTime.now().millisecondsSinceEpoch}',
endpoint: '/api/v1/orders',
method: 'POST',
payload: {'item_id': 101, 'quantity': 2},
priority: OutboxPriority.critical,
idempotencyKey: 'order_uuid_987654',
),
);
3. Process & Drain Queue #
// Drain the queue manually (e.g. on connectivity reconnect):
final int deliveredCount = await outbox.processQueue();
// Or enable background periodic auto-sync:
outbox.startAutoSync(interval: const Duration(seconds: 15));
4. Real-time Status Stream #
// Listen to live pending items count:
outbox.pendingCountStream.listen((count) {
print('Pending outbox items: $count');
});
// Listen to delivered requests:
outbox.onCompletedStream.listen((req) {
print('Delivered: ${req.endpoint}');
});
π οΈ API Reference #
OutboxRequest Parameters #
| Field | Type | Default | Description |
|---|---|---|---|
id |
String |
Required | Unique item identifier. |
endpoint |
String |
Required | Target API URL or path. |
method |
String |
'POST' |
HTTP method (GET, POST, PUT, DELETE, PATCH). |
payload |
dynamic |
null |
JSON body payload or request data. |
headers |
Map<String, String>? |
null |
HTTP headers. |
idempotencyKey |
String? |
null |
Key to prevent duplicate requests. |
priority |
OutboxPriority |
normal |
critical, high, normal, or low. |
π Support the Project #
If you find this project useful, consider supporting its active maintenance and future development:
π License #
This project is licensed under the Apache License 2.0 - see the LICENSE file for details.
Maintained with β€οΈ by Govind Tank.