๐ฆ Offline Outbox Sync for Flutter (ObjectBox + Isolate + Retry + Multipart Support)
A powerful and lightweight offline-first outbox queue system for Flutter apps. Designed for real-world enterprise use cases where network instability, large payloads, and token expiry must be handled automatically.
Supports:
โ
JSON API requests
โ
Multipart file uploads
โ
Multiple files per request
โ
Empty multipart field names
โ
Automatic retry with priority
โ
Automatic token refresh (401 handling)
โ
Runs in Isolate for non-blocking UI
โ
ObjectBox storage for fast persistence
โ
Fully background-safe
| Feature | Status |
|---|---|
| JSON request outbox | โ |
| Multipart upload (files/images/docs) | โ |
| Multiple files with dynamic field names | โ |
| Empty file field name ("") support | โ |
| Priority-based queue (1 โ 4) | โ |
| Automatic retry on failures | โ |
| Retry with new token on 401 | โ |
| Save status code + response | โ |
| RootIsolateToken support | โ |
| Zero UI freeze | Isolate-based |
| Production-ready | ๐ฅ |
๐ฆ Installation
Add to pubspec.yaml:
dependencies:
ma_ng_outbox: latest
Make sure ObjectBox is installed:
dependencies:
objectbox: any
objectbox_flutter_libs: any
๐ Basic Setup 1๏ธโฃ Initialize Outbox in main.dart
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await OutboxBootstrap.setup();
runApp(MyApp());
}
๐งฑ How Outbox Works
Every API call is stored as an OutboxItem:
await objectBox.addToOutbox(
operation: "POST",
url: "https://api.server.com/save",
payload: {"title": "Offline Save"},
priority: Priority.high,
primaryKey: "local-123",
filePathsJson: [''],
fileFieldsJson: ['']
);
Then Outbox will:
โ Automatically sync when online
โ Retry failed requests
โ Retry 401 using refresh token
โ Save response & status code
โ Process highest priority first
๐งต Architecture Overview
UI Layer โโโบ addToOutbox() โโโบ ObjectBox storage
โ
โผ
Background Sync Trigger
โ
โผ
Isolate Spawned
โ
โผ
outboxIsolateEntry() processing
โ
โโโโบ JSON Request
โโโโบ Multipart Request
โโโโบ Multi-file Upload
โโโโบ Retry on 401 (Refresh Token)
โ
โผ
Main Isolate receives result
โ
โผ
Updates ObjectBox (status + response)
๐ Adding File Upload Requests โญ One File
await objectBox.addToOutbox(
operation: "POST",
url: Api.uploadImage,
payload: {
"description": "Offline photo",
"userId": 12,
},
filePathsJson: jsonEncode([imageFile.path]),
fileFieldsJson: jsonEncode(["file"]), // or ""
);
โญ Multiple Files
await objectBox.addToOutbox(
operation: "POST",
url: Api.uploadDocuments,
payload: {"caseId": 55},
filePathsJson: jsonEncode([frontPath, backPath]),
fileFieldsJson: jsonEncode(["front", "back"]),
);
๐ Token Refresh Flow (401 Handling)
1.If API returns 401 Unauthorized:
2.Outbox calls refresh token API
3.Saves new access token to OutboxItem
4.Retries the original request automatically
5.Continues syncing
This is built-in.
๐ Manual Triggering of Sync
SyncController.runIsolateSync(
objectBox: objectBox,
token: "<accessToken>",
rootToken: rootToken,
);
๐ Performance & Scaling
* 25,000+ outbox items stress-tested
* Large file uploads supported
* Zero UI freeze due to isolate-based architecture
* Perfect for enterprise offline-first apps