Bagian setup lama tidak cuma kurang, tapi salah: diklaim environment dipilih otomatis dari debug/release, padahal datang dari --dart-define=ENV. Siapa pun yang mengikutinya akan menghasilkan APK 'production' yang menunjuk database dev. Tabel environment dan contoh kodenya juga sudah tidak sesuai isi lib/env.dart. Ditambah: prasyarat FVM, langkah instalasi ber-fvm, perintah build per environment, bagian Code Generation (termasuk peringatan --delete-conflicting-outputs yang menghapus seluruh file generated sebelum build), dan Troubleshooting. URL base sengaja tidak disalin ke README; cukup menunjuk lib/env.dart, karena menyalin nilai itulah yang membuat dokumen ini basi. 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
# 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
# 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.