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 |
|---|---|
| Version Manager | fvm — Flutter dipin di .fvmrc |
| 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
- FVM — project ini mengunci versi Flutter, jangan pakai Flutter global
- Flutter 3.41.9 (Dart 3.11.5) — dipin di
.fvmrc - Android SDK / Xcode (untuk iOS)
- Firebase project yang sudah dikonfigurasi
Kenapa versinya dikunci? Stack code generation project ini (
freezed2.x,build_runner2.5,analyzer7.7.1) hanya paham bahasa Dart sampai 3.9. Flutter 3.44 ke atas membawa framework yang ditulis di bahasa 3.10+, dananalyzer7.7.1 crash saat membacanya. Selama stack codegen belum dinaikkan, Flutter 3.41.9 adalah versi terbaru yang masih bisa menjalankanbuild_runner. Lihat Troubleshooting.
Instalasi
# Clone repository
git clone <repo-url>
cd apskel-pos-flutter-v2
# Pasang FVM (sekali per mesin)
dart pub global activate fvm
# Ambil & pakai versi Flutter yang dipin di .fvmrc
fvm install
fvm use
dart pub global activate memasang fvm ke folder bin milik pub yang belum tentu ada di PATH. Kalau muncul fvm: command not found / The term 'fvm' is not recognized, tambahkan foldernya sekali saja:
# Windows PowerShell — permanen, buka terminal baru setelahnya
[Environment]::SetEnvironmentVariable('Path', [Environment]::GetEnvironmentVariable('Path','User') + ";$env:LOCALAPPDATA\Pub\Cache\bin", 'User')
# Untuk terminal yang sedang terbuka
$env:Path += ";$env:LOCALAPPDATA\Pub\Cache\bin"
# macOS / Linux — tambahkan ke ~/.zshrc atau ~/.bashrc
export PATH="$PATH:$HOME/.pub-cache/bin"
Setelah fvm bisa dipanggil, lanjutkan:
# Install dependencies
fvm flutter pub get
# Generate kode
fvm dart run build_runner build
Semua perintah Flutter/Dart di project ini harus diawali
fvm.flutter runtanpafvmmemakai Flutter global dan hasilnya bisa berbeda.
Menjalankan App
Environment tidak ditentukan otomatis dari debug/release — harus dioper lewat --dart-define:
# Development
fvm flutter run --dart-define=ENV=dev
# Production
fvm flutter run --dart-define=ENV=prod
# Production, release mode
fvm flutter run --release --dart-define=ENV=prod
Kalau ENV tidak dioper, nilainya jatuh ke dev (lihat defaultValue di lib/main.dart).
Di VSCode sudah tersedia konfigurasi Dev dan Prod di .vscode/launch.json, tinggal pilih di panel Run and Debug.
Build
--dart-define=ENV=prod wajib ikut, kalau tidak APK-nya akan memakai database dan base URL milik dev:
# APK
fvm flutter build apk --release --dart-define=ENV=prod
fvm flutter build apk --release --split-per-abi --dart-define=ENV=prod
# App Bundle (Play Store)
fvm flutter build appbundle --release --dart-define=ENV=prod
# iOS
fvm flutter build ipa --release --dart-define=ENV=prod
Code Generation
Project ini banyak memakai kode generated (freezed, injectable, auto_route, json_serializable, flutter_gen). Kode itu wajib di-generate ulang setiap kali kamu:
- menambah/mengubah kelas
@freezed(entity, event, state, DTO) - menambah/mengubah anotasi DI (
@injectable,@LazySingleton,@Injectable(as: ...)) - menambah halaman baru dengan
@RoutePage() - menambah aset di
pubspec.yaml
# Sekali jalan
fvm dart run build_runner build
# Mode watch — regenerate otomatis saat file berubah, enak dipakai saat ngoding
fvm dart run build_runner watch
Hindari
--delete-conflicting-outputs. Flag itu menghapus seluruh file generated sebelum mulai build. Kalau build-nya gagal di tengah jalan, kamu berakhir tanpa kode generated sama sekali dan project tidak bisa di-compile. Pakai hanya kalau build_runner benar-benar mengeluh soal konflik output, dan pastikan working tree-mu bersih dulu supaya bisagit restore.
Environment
Konfigurasi environment ada di lib/env.dart — itu sumber kebenarannya, jangan disalin ke sini karena gampang basi.
| Environment | Database |
|---|---|
dev |
apskel_pos_staging.db |
prod |
apskel_pos_prod.db |
Environment dipilih dari --dart-define=ENV=... di lib/main.dart:
const String env = String.fromEnvironment('ENV', defaultValue: 'dev');
await configureDependencies(env);
Nilai env diteruskan ke injectable, yang lalu memilih antara DevEnv (@dev) dan ProdEnv (@prod). Menambah environment baru berarti menambah kelas ber-anotasi di lib/env.dart, lalu generate ulang.
Karena dev dan prod memakai nama database berbeda, berpindah environment di perangkat yang sama tidak akan mencampur datanya.
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
- Selalu pakai
fvm flutter/fvm dart, bukanflutter/dartglobal - Jangan edit
injection.config.dartdan file*.freezed.dart/*.g.dartsecara manual — file tersebut di-generate otomatis - Setelah mengubah kelas
@freezedatau anotasi DI, jalankan build_runner sebelum menjalankan app - Gunakan
log()daridart:developeruntuk logging, bukanprint() print()dinonaktifkan di release mode
Troubleshooting
build_runner gagal: Missing implementation of visitDotShorthandPropertyAccess
Kamu sedang memakai Flutter yang terlalu baru. Cek:
fvm flutter --version # harus 3.41.9
flutter --version # ini Flutter global, boleh versi apa saja
Kalau versinya bukan 3.41.9, artinya perintahnya jalan tanpa fvm. Log build juga memberi petunjuk yang sama:
SDK language version 3.13.0 is newer than `analyzer` language version 3.9.0
Perbaikannya: fvm use, lalu ulangi dengan awalan fvm.
Menaikkan Flutter melewati 3.41.x mengharuskan seluruh stack codegen ikut naik — freezed 2 → 3/4, build_runner → 2.16, injectable_generator 2 → 3, auto_route 9 → 10. freezed 3 mewajibkan tiap kelas dideklarasikan abstract/sealed dan menghapus when/map (diganti pattern matching), jadi ini pekerjaan tersendiri, bukan sambilan.
Project tidak bisa di-compile, banyak error _$Something tidak ditemukan
Kode generated-nya hilang atau belum dibuat. Jalankan:
fvm dart run build_runner build
fvm: command not found
FVM terpasang di ~/AppData/Local/Pub/Cache/bin (Windows) atau ~/.pub-cache/bin (macOS/Linux) yang mungkin belum ada di PATH. Tambahkan direktori itu ke PATH, atau panggil dengan path lengkap.