printStruckOrder berhenti di kegagalan pertama, jadi satu station gagal membuat semua station sesudahnya tidak pernah dicoba. Karena checker (Bluetooth) berada sebelum kitchen dan bar (Network) dalam rantai, satu kegagalan koneksi Bluetooth menghilangkan tiga tiket sekaligus, dua di antaranya di jaringan yang sehat. Jalur printer Network juga mengembalikan right(false) saat gagal, yang lolos sebagai sukses ke pemanggil, sehingga kasir melihat 'berhasil dicetak' padahal tidak ada kertas keluar. Perubahan: repository kini mengembalikan Stream<List<PrintStationResult>> dan tetap mencoba semua station; kegagalan Network jadi left; station Network berjalan paralel dengan rantai Bluetooth; station Bluetooth dikelompokkan per alamat agar perpindahan koneksi seminimal mungkin; CapabilityProfile di-cache. Bluetooth: retry connect dengan backoff dan disconnect di tiap percobaan ulang (socket RFCOMM setengah terbuka membuat connect berulang gagal identik), jeda settle dikembalikan setelah dipangkas ke 300ms, satu lock untuk seluruh radio (bukan per MAC), izin runtime dipastikan di jalur cetak, dan repository jadi LazySingleton agar state koneksi benar-benar dibagi. UI: dialog progres per-station menggantikan toast tunggal, lengkap dengan durasi tiap station dan tombol cetak ulang khusus yang gagal. Enam listener duplikat di halaman diganti satu listener terpusat di AppWidget. Menu Print di detail transaksi dapat item Print All. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Apskel POS Flutter
Aplikasi Point of Sale (POS) berbasis Flutter untuk manajemen kasir restoran. Mendukung manajemen order, produk, meja, pelanggan, pembayaran, printer bluetooth/network, analitik, dan push notification via FCM.
Fitur Utama
- Autentikasi — Login dengan email & password, logout, session management
- Order Management — Buat, kelola, dan proses order
- Produk & Kategori — Manajemen menu dan kategori produk
- Meja — Manajemen meja restoran
- Pelanggan — Data pelanggan dan riwayat transaksi
- Checkout & Pembayaran — Proses checkout dengan berbagai metode pembayaran
- Split Bill — Pembagian tagihan
- Void & Refund — Pembatalan dan pengembalian transaksi
- Printer — Cetak struk via Bluetooth dan Network printer
- Analitik — Dashboard, laporan penjualan, produk, kategori, payment method, profit/loss, inventory
- Sinkronisasi — Sinkronisasi data offline/online
- Push Notification (FCM) — Notifikasi real-time via Firebase Cloud Messaging
Tech Stack
| Kategori | Library |
|---|---|
| State Management | flutter_bloc + bloc |
| Dependency Injection | get_it + injectable |
| Navigation | auto_route |
| HTTP Client | dio |
| Local Database | sqflite |
| Local Storage | shared_preferences |
| Firebase | firebase_core, firebase_crashlytics, firebase_messaging |
| Push Notification | flutter_local_notifications |
| Device Info | device_info_plus, package_info_plus |
| Code Generation | freezed, json_serializable |
| Printer | print_bluetooth_thermal, flutter_esc_pos_network |
| Chart | fl_chart |
| Connectivity | connectivity_plus |
Arsitektur
Project menggunakan Clean Architecture dengan 4 layer:
lib/
├── application/ # BLoC — state management per fitur
├── domain/ # Entity, repository interface, failure
├── infrastructure/ # Implementasi repository, DTO, datasource
├── presentation/ # UI — pages, components, router
└── common/ # Shared utilities, DI modules, service, theme
Struktur per Fitur
feature/
├── application/
│ └── feature_bloc.dart # BLoC
├── domain/
│ ├── entities/ # Domain model (freezed)
│ ├── repositories/ # Interface repository
│ └── failures/ # Failure types
└── infrastructure/
├── datasources/
│ ├── remote_data_provider.dart
│ └── local_data_provider.dart
├── dtos/ # Data Transfer Object (json_serializable)
└── repositories/ # Implementasi repository
Setup & Menjalankan
Prasyarat
- Flutter SDK
^3.8.1 - Dart SDK
^3.8.1 - Android SDK / Xcode (untuk iOS)
- Firebase project yang sudah dikonfigurasi
Instalasi
# Clone repository
git clone <repo-url>
cd apskel-pos-flutter-v2
# Install dependencies
flutter pub get
# Generate kode (freezed, injectable, auto_route, json_serializable)
dart run build_runner build --delete-conflicting-outputs
Menjalankan App
# Development
flutter run
# Release
flutter run --release
App otomatis menggunakan environment dev saat debug dan prod saat release.
Environment
Konfigurasi environment ada di lib/env.dart:
| Environment | Base URL | Database |
|---|---|---|
dev |
https://api-pos.apskel.id |
apskel_pos_dev.db |
prod |
https://api-pos.apskel.id |
apskel_pos_dev.db |
Environment dipilih otomatis di main.dart:
await configureDependencies(
kReleaseMode ? Environment.prod : Environment.dev,
);
Dependency Injection
DI menggunakan get_it + injectable. Semua service, repository, dan BLoC didaftarkan via annotation.
Modul DI
| File | Isi |
|---|---|
di_firebase.dart |
FirebaseMessaging, FlutterLocalNotificationsPlugin, DeviceInfoPlugin, PackageInfo |
di_dio.dart |
Dio HTTP client |
di_shared_preferences.dart |
SharedPreferences |
di_database.dart |
DatabaseHelper (SQLite) |
di_auto_route.dart |
AppRouter |
di_connectivity.dart |
Connectivity |
Setelah menambah atau mengubah class yang menggunakan annotation injectable, jalankan:
dart run build_runner build --delete-conflicting-outputs
Firebase & FCM
Setup
Firebase sudah dikonfigurasi di android/app/google-services.json. Inisialisasi dilakukan di main.dart sebelum app berjalan.
FCM Service
FcmService (lib/common/service/fcm_service.dart) menangani:
- Request permission notifikasi (Android 13+ / iOS)
- Ambil dan log FCM token
- Foreground notification via
flutter_local_notifications - Background & terminated message handler
- Subscribe/unsubscribe topic
FCM token dikirim ke server saat login bersama device info.
Login Payload
Saat login, app mengirim data berikut ke API:
{
"email": "user@example.com",
"password": "secret",
"fcm_token": "dXj3k9...",
"device_id": "abc123",
"device_name": "Samsung Galaxy Tab S8",
"device_type": "tablet",
"platform": "android",
"app_version": "1.0.4+9",
"os_version": "Android 13 (SDK 33)"
}
Nilai valid: device_type → mobile | tablet | desktop, platform → android | ios | web
Printer
Mendukung dua jenis printer:
- Bluetooth — via
print_bluetooth_thermal - Network (LAN) — via
flutter_esc_pos_network
Orientasi Layar
App dikunci ke mode landscape (kiri & kanan) karena didesain untuk tablet POS.
Catatan Development
- Jangan edit
injection.config.dartdan file*.freezed.dart/*.g.dartsecara manual — file tersebut di-generate otomatis - Gunakan
log()daridart:developeruntuk logging, bukanprint() print()dinonaktifkan di release mode