Documentation Index Fetch the complete documentation index at: https://docs-mstore.faisalaffan.com/llms.txt
Use this file to discover all available pages before exploring further.
Offline-First Quick Start
Get started dengan offline-first implementation dalam 5 menit!
🎯 What You’ll Build
Setelah mengikuti guide ini, Anda akan memiliki:
✅ Flutter app yang bisa create transactions offline
✅ Auto-sync ke backend saat online
✅ Conflict detection & resolution
✅ Real-time sync status UI
📋 Prerequisites
Backend : MStore Backend v1.0+ sudah running
Flutter : Flutter 3.16+ installed
Database : MySQL 8.0+ dengan schema terbaru
🚀 5-Minute Setup
Step 1: Add Dependencies (1 min)
# pubspec.yaml
dependencies :
isar : ^3.1.0+1
isar_flutter_libs : ^3.1.0+1
uuid : ^4.2.1
connectivity_plus : ^5.0.2
dev_dependencies :
isar_generator : ^3.1.0+1
build_runner : ^2.4.7
Step 2: Define Isar Model (1 min)
// lib/models/transaction_local.dart
import 'package:isar/isar.dart' ;
part 'transaction_local.g.dart' ;
@collection
class TransactionLocal {
Id id = Isar .autoIncrement;
@Index (unique : true )
late String offlineId;
@Index (unique : true )
late String offlineReference;
late String deviceId;
late double grandTotal;
late String status;
@enumerated
late SyncStatus syncStatus;
int ? serverId;
String ? transactionCode;
}
enum SyncStatus { pending, syncing, synced, failed }
flutter pub run build_runner build
Step 3: Initialize Isar (1 min)
// lib/main.dart
import 'package:isar/isar.dart' ;
import 'package:path_provider/path_provider.dart' ;
Future < void > main () async {
WidgetsFlutterBinding . ensureInitialized ();
final dir = await getApplicationDocumentsDirectory ();
final isar = await Isar . open (
[ TransactionLocalSchema ],
directory : dir.path,
);
runApp ( MyApp (isar : isar));
}
Step 4: Create Transaction Offline (1 min)
// lib/services/transaction_service.dart
import 'package:uuid/uuid.dart' ;
Future < TransactionLocal > createTransaction ( Isar isar) async {
final tx = TransactionLocal ()
..offlineId = const Uuid (). v4 ()
..offlineReference = 'DEVICE001- ${ DateTime . now (). millisecondsSinceEpoch } '
..deviceId = 'DEVICE001'
..grandTotal = 33000
..status = 'paid'
..syncStatus = SyncStatus .pending;
await isar. writeTxn (() async {
await isar.transactionLocals. put (tx);
});
return tx;
}
Step 5: Sync to Backend (1 min)
// lib/services/sync_service.dart
import 'package:dio/dio.dart' ;
Future < void > syncToBackend ( Isar isar) async {
final pending = await isar.transactionLocals
. filter ()
. syncStatusEqualTo ( SyncStatus .pending)
. findAll ();
if (pending.isEmpty) return ;
final dio = Dio ( BaseOptions (baseUrl : 'http://localhost:3002/api/v1' ));
final response = await dio. post ( '/pos/offline/batch-sync' , data : {
'transactions' : pending. map ((tx) => {
'offline_id' : tx.offlineId,
'offline_reference' : tx.offlineReference,
'device_id' : tx.deviceId,
'grand_total' : tx.grandTotal,
'status' : tx.status,
'branch_code' : 'BRN-MST-OSK00001' ,
'payment_method' : 'CASH' ,
'id_user_apps' : 1 ,
'created_at_device' : DateTime . now (). toIso8601String (),
'subtotal' : tx.grandTotal,
'discount_total' : 0 ,
'tax_total' : 0 ,
'payment_status' : 'paid' ,
}). toList (),
'payments' : [],
'voids' : [],
'device_id' : 'DEVICE001' ,
'synced_at' : DateTime . now (). toIso8601String (),
});
// Update sync status
final results = response.data[ 'data' ][ 'results' ] as List ;
await isar. writeTxn (() async {
for ( final result in results) {
final tx = pending. firstWhere ((t) => t.offlineId == result[ 'offline_id' ]);
if (result[ 'success' ]) {
tx.syncStatus = SyncStatus .synced;
tx.serverId = result[ 'server_id' ];
tx.transactionCode = result[ 'transaction_code' ];
} else {
tx.syncStatus = SyncStatus .failed;
}
await isar.transactionLocals. put (tx);
}
});
}
🎉 Done!
Anda sekarang sudah memiliki offline-first app yang working!
Test It
Create transaction offline :
final tx = await createTransaction (isar);
print ( 'Created: ${ tx . offlineReference } ' );
Sync to backend :
await syncToBackend (isar);
print ( 'Synced!' );
Check status :
final synced = await isar.transactionLocals
. filter ()
. syncStatusEqualTo ( SyncStatus .synced)
. findAll ();
print ( 'Synced: ${ synced . length } transactions' );
📖 Next Steps
Complete Flutter Guide Full implementation dengan UI, error handling, dan testing
Backend Implementation Understand backend architecture
API Reference Complete API documentation
Best Practices Production-ready tips
🆘 Troubleshooting
Transaction Not Syncing?
Check Internet Connection
final connectivity = await Connectivity (). checkConnectivity ();
print ( 'Connected: ${ connectivity != ConnectivityResult . none } ' );
Check Pending Transactions
final pending = await isar.transactionLocals
. filter ()
. syncStatusEqualTo ( SyncStatus .pending)
. findAll ();
print ( 'Pending: ${ pending . length } ' );
# Check backend logs
tail -f logs/app.log | grep "offline"
-- Check transactions table
SELECT * FROM transactions
WHERE is_offline = 1
ORDER BY created_at DESC
LIMIT 10 ;
-- Check conflicts
SELECT * FROM offline_conflicts
WHERE status = 'pending' ;
💡 Pro Tips
Batch Size : Sync max 50 transactions per request untuk optimal performance
Auto Sync : Setup Timer untuk auto-sync setiap 30 detikTimer . periodic ( Duration (seconds : 30 ), (_) => syncToBackend (isar));
Conflict Handling : Always show conflicts to user untuk manual resolution
Don’t Block UI : Sync harus non-blocking, jangan tunggu response
📊 Architecture Overview
┌─────────────────────────────────────┐
│ Flutter App │
│ ┌───────────────────────────────┐ │
│ │ Create Transaction │ │
│ │ (Offline-First) │ │
│ └───────────┬───────────────────┘ │
│ │ │
│ ┌───────────▼───────────────────┐ │
│ │ Save to Isar DB │ │
│ │ (Local Storage) │ │
│ └───────────┬───────────────────┘ │
│ │ │
│ ┌───────────▼───────────────────┐ │
│ │ Add to Sync Queue │ │
│ └───────────┬───────────────────┘ │
└──────────────┼───────────────────────┘
│
│ Background Sync
│
┌──────────────▼───────────────────────┐
│ Backend API │
│ ┌───────────────────────────────┐ │
│ │ POST /batch-sync │ │
│ └───────────┬───────────────────┘ │
│ │ │
│ ┌───────────▼───────────────────┐ │
│ │ Check Duplicate │ │
│ │ (offline_reference) │ │
│ └───────────┬───────────────────┘ │
│ │ │
│ ┌───────────▼───────────────────┐ │
│ │ Save to MySQL │ │
│ └───────────────────────────────┘ │
└──────────────────────────────────────┘
Congratulations! 🎉 Anda sudah berhasil implement offline-first architecture. Check complete documentation untuk production-ready implementation.