efrilmandClaude Opus 5 32f64a197a fix: pulihkan koneksi Bluetooth dan deteksi kegagalan printer jaringan
Bluetooth: percobaan ulang connect sebelumnya tidak menutup socket lebih dulu, padahal 'read failed, socket might closed or timeout' justru berarti socket RFCOMM masih setengah terbuka — ketiga percobaan gagal identik dan retry-nya sia-sia. Tiap percobaan ulang kini didahului disconnect paksa.

connectBluetooth mengembalikan sukses begitu ADA koneksi tanpa memeriksa apakah itu printer yang diminta, dan tidak pernah mencatat MAC yang aktif. Kini memakai jalur yang sama dengan mencetak.

Izin runtime Bluetooth sebelumnya hanya diminta dari dialog pilih perangkat. Karena reinstall mencabut izin, kasir yang tidak pernah membuka dialog itu gagal cetak tanpa penjelasan. Status adapter, izin, dan pairing kini ikut di log, dan printer yang tidak ter-pairing digagalkan langsung dengan pesan yang menyebut tindakannya.

Jaringan: printTicket hanya menaruh byte di socket lalu menutup koneksi, tidak pernah membaca balik, sehingga kertas habis atau cover terbuka terlihat sama dengan sukses — itu sebabnya kegagalan kitchen tidak pernah muncul di Crashlytics. NetworkPrinterClient baru menanyakan kondisi printer lewat DLE EOT sebelum dan sesudah mencetak, dengan degradasi anggun untuk printer yang tidak mendukungnya.

Panggilan disconnect() ganda dihapus; printTicket sudah flush dan close sendiri, dan panggilan kedua tanpa await membuat error-nya tercatat sebagai crash fatal palsu. Ditambah jejak per station ke Crashlytics.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-05 14:06:22 +07:00
2025-10-25 02:09:47 +07:00
2025-10-23 23:06:02 +07:00
2025-10-23 23:06:02 +07:00
2026-05-10 22:51:04 +07:00
2025-10-23 22:16:53 +07:00
2026-05-07 17:44:03 +07:00
2025-10-23 23:06:02 +07:00
2025-11-04 14:58:51 +07:00
2026-09-02 20:43:49 +07:00
2026-09-02 20:43:49 +07:00
2025-10-23 22:00:33 +07:00
2025-11-10 16:56:12 +07:00

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.

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 run tanpa fvm memakai 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 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:

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, 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:

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.

S
Description
No description provided
Readme
2.5 MiB
Languages
C++ 44.2%
CMake 36.3%
Dart 11%
Swift 3.3%
C 2.6%
Other 2.5%