Files
apskel-pos-flutter-v2/README.md
T
efrilmandClaude Opus 5 ebcddca4bb docs: perbarui README untuk FVM, build_runner, dan cara run/build
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>
2026-09-02 20:44:24 +07:00

311 lines
10 KiB
Markdown

# 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 (`freezed` 2.x, `build_runner` 2.5, `analyzer` 7.7.1) hanya paham bahasa Dart sampai 3.9. Flutter 3.44 ke atas membawa framework yang ditulis di bahasa 3.10+, dan `analyzer` 7.7.1 crash saat membacanya. Selama stack codegen belum dinaikkan, **Flutter 3.41.9 adalah versi terbaru yang masih bisa menjalankan `build_runner`.** Lihat [Troubleshooting](#troubleshooting).
### Instalasi
```bash
# 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 run` tanpa `fvm` memakai Flutter global dan hasilnya bisa berbeda.
### Menjalankan App
Environment **tidak** ditentukan otomatis dari debug/release — harus dioper lewat `--dart-define`:
```bash
# 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:
```bash
# 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`
```bash
# 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 bisa `git 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`:
```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:
```bash
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:
```json
{
"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`, bukan `flutter` / `dart` global
- Jangan edit `injection.config.dart` dan file `*.freezed.dart` / `*.g.dart` secara manual — file tersebut di-generate otomatis
- Setelah mengubah kelas `@freezed` atau anotasi DI, jalankan build_runner sebelum menjalankan app
- Gunakan `log()` dari `dart:developer` untuk logging, bukan `print()`
- `print()` dinonaktifkan di release mode
---
## Troubleshooting
### `build_runner` gagal: `Missing implementation of visitDotShorthandPropertyAccess`
Kamu sedang memakai Flutter yang terlalu baru. Cek:
```bash
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:
```bash
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.