betuko_offline_sync 3.0.0
betuko_offline_sync: ^3.0.0 copied to clipboard
Ultra-simple offline-first Flutter package. Just get(), save(), and syncAll(). Automatic local storage with Hive, manual sync when you want.
Betuko Offline Sync #
Ultra-simple offline-first package for Flutter. Your app always works, online or offline.
✨ Features #
- 🚀 Super Simple API - Just
get(),save(),syncAll() - 📱 Always Fast -
get()always returns local data instantly - 🔄 Manual Sync - User decides when to sync with
syncAll() - 💾 Auto Storage - Uses Hive for persistent local storage
- 📊 Sync Status - Know exactly what's synced and what's pending
- 🔧 Debug Tools - Built-in debugging and reset utilities
📦 Installation #
dependencies:
betuko_offline_sync: ^3.0.0
flutter pub get
🚀 Quick Start #
1. Configure (once at app start) #
import 'package:betuko_offline_sync/betuko_offline_sync.dart';
void main() {
GlobalConfig.init(
baseUrl: 'https://your-api.com',
token: 'your-auth-token',
);
runApp(MyApp());
}
2. Create a Manager #
final reports = OnlineOfflineManager(
boxName: 'reports',
endpoint: '/api/reports',
);
3. Use It! #
// Get data (ALWAYS returns local data - instant!)
final data = await reports.get();
// Save data (stored locally, synced later)
await reports.save({
'title': 'My Report',
'date': DateTime.now().toIso8601String(),
});
// Sync with server (when user wants fresh data)
await OnlineOfflineManager.syncAll();
📖 API Reference #
Instance Methods #
| Method | Returns | Description |
|---|---|---|
get() |
List<Map> |
All local data |
getSynced() |
List<Map> |
Only synced data |
getPending() |
List<Map> |
Only pending data |
getFullData() |
FullSyncData |
All data + counts |
getSyncInfo() |
SyncInfo |
Just counts |
save(Map data) |
void |
Save locally |
delete(String id) |
void |
Delete by ID |
clear() |
void |
Clear all data |
reset() |
void |
Clear data + cache |
dispose() |
void |
Release resources |
Static Methods #
| Method | Returns | Description |
|---|---|---|
syncAll() |
Map<String, SyncResult> |
Sync all managers |
getAllSyncInfo() |
Map<String, SyncInfo> |
Status of all managers |
resetAll() |
void |
Reset everything |
debugInfo() |
void |
Print debug info |
getAllBoxesInfo() |
List<HiveBoxInfo> |
Hive boxes info |
getTotalRecordCount() |
int |
Total records |
getTotalPendingCount() |
int |
Total pending |
deleteAllBoxes() |
void |
Delete from disk |
📊 Check Sync Status #
Per Manager #
// Get full data with status
final data = await reports.getFullData();
print('Total: ${data.total}');
print('Synced: ${data.syncedCount}');
print('Pending: ${data.pendingCount}');
print('Percentage: ${data.syncPercentage}%');
// Access the actual data
for (final item in data.synced) {
print('Synced: ${item['title']}');
}
for (final item in data.pending) {
print('Pending: ${item['title']}');
}
All Managers #
final allStatus = await OnlineOfflineManager.getAllSyncInfo();
for (final entry in allStatus.entries) {
print('${entry.key}: ${entry.value.synced}/${entry.value.total}');
}
🔧 Debug Tools #
// Print complete debug info
await OnlineOfflineManager.debugInfo();
// Output:
// ═══════════════════════════════════════════════════════════
// 📊 DEBUG INFO - OnlineOfflineManager
// ═══════════════════════════════════════════════════════════
// 📦 Managers activos: 2
// • reports: 150 registros (3 pendientes)
// • users: 50 registros (0 pendientes)
// 💾 Boxes Hive:
// • reports: 150 registros (abierta)
// • users: 50 registros (abierta)
// ⚙️ GlobalConfig:
// • Inicializado: true
// • BaseURL: https://api.com
// ═══════════════════════════════════════════════════════════
🔄 Multiple Managers #
// Create multiple managers
final reports = OnlineOfflineManager(
boxName: 'reports',
endpoint: '/api/reports',
);
final users = OnlineOfflineManager(
boxName: 'users',
endpoint: '/api/users',
);
final products = OnlineOfflineManager(
boxName: 'products',
endpoint: '/api/products',
);
// Sync ALL with one call
final results = await OnlineOfflineManager.syncAll();
for (final entry in results.entries) {
if (entry.value.success) {
print('✅ ${entry.key}: synced');
} else {
print('❌ ${entry.key}: ${entry.value.error}');
}
}
🎯 Complete Example #
import 'package:flutter/material.dart';
import 'package:betuko_offline_sync/betuko_offline_sync.dart';
void main() {
GlobalConfig.init(
baseUrl: 'https://api.example.com',
token: 'your-token',
);
runApp(MyApp());
}
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
final reports = OnlineOfflineManager(
boxName: 'reports',
endpoint: '/api/reports',
);
List<Map<String, dynamic>> data = [];
bool isSyncing = false;
@override
void initState() {
super.initState();
_loadData();
}
Future<void> _loadData() async {
final result = await reports.get();
setState(() => data = result);
}
Future<void> _sync() async {
setState(() => isSyncing = true);
await OnlineOfflineManager.syncAll();
await _loadData();
setState(() => isSyncing = false);
}
Future<void> _addReport() async {
await reports.save({
'title': 'Report ${DateTime.now().millisecondsSinceEpoch}',
'date': DateTime.now().toIso8601String(),
});
await _loadData();
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Offline-First App'),
actions: [
IconButton(
icon: isSyncing
? SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(color: Colors.white))
: Icon(Icons.sync),
onPressed: isSyncing ? null : _sync,
),
],
),
body: ListView.builder(
itemCount: data.length,
itemBuilder: (context, index) {
final item = data[index];
final isSynced = item['sync'] == 'true';
return ListTile(
title: Text(item['title'] ?? 'No title'),
trailing: Icon(
isSynced ? Icons.cloud_done : Icons.cloud_off,
color: isSynced ? Colors.green : Colors.orange,
),
);
},
),
floatingActionButton: FloatingActionButton(
onPressed: _addReport,
child: Icon(Icons.add),
),
),
);
}
@override
void dispose() {
reports.dispose();
super.dispose();
}
}
🔐 Update Token #
// After login or token refresh
GlobalConfig.updateToken('new-token');
🗑️ Reset Everything #
// Reset all data (useful for logout)
await OnlineOfflineManager.resetAll();
// Or delete all boxes from disk
await OnlineOfflineManager.deleteAllBoxes();
📋 Data Classes #
SyncInfo #
class SyncInfo {
int total; // Total records
int synced; // Synced records
int pending; // Pending records
double syncPercentage; // 0-100
bool isFullySynced; // true if pending == 0
}
FullSyncData #
class FullSyncData {
List<Map> all; // All data
List<Map> synced; // Synced data
List<Map> pending; // Pending data
int total;
int syncedCount;
int pendingCount;
double syncPercentage;
bool isFullySynced;
}
SyncResult #
class SyncResult {
bool success;
String? error;
}
🤝 Contributing #
Contributions are welcome! Please feel free to submit a Pull Request.
📄 License #
MIT License - see LICENSE for details.
👨💻 Author #
Betuko - GitHub