9.2 KiB
9.2 KiB
Coding Standard — Dutra Store (POS Grosir Flutter)
Gunakan skill ini sebagai acuan setiap kali menulis, mengubah, atau meninjau kode aplikasi. Jika ada pertentangan, prioritaskan yang tercantum di sini.
1. Prinsip Umum
1.1 Clean Code
- Satu function/method lakukan satu hal saja (Single Responsibility).
- Nama variable/function harus deskriptif:
isPayingbukanflag,recordPaymentbukansave. - Jangan duplikasi logic — extract ke function/helper yang reuseable.
- Komentar hanya untuk "mengapa", bukan "apa yang dilakukan". Kode harus self-explanatory untuk "apa".
1.2 File Organization
- Satu file satu class utama. Exception: helper kecil yang sangat spesifik untuk screen itu.
- Nama file:
snake_case.dart. Nama class:PascalCase. - Urutan import:
dart:corepackage:third-partypackage:<project>/internal- Relative import
./atau../
- Jangan gunakan relative import untuk file di folder berbeda; gunakan package import.
1.3 Naming Convention
- Class/Widget:
PascalCase→KasirScreen,_CartBarState - Private class/widget: underscore prefix →
_CartBar,_ReceiptRow - Function/method:
camelCase→addToCart,_showReceipt - Variable:
camelCase→isPayingRef,payingStart - Constant:
lowercase with underscoresatauPascalCasesesuai konvensi project. Untuk warna dicolors.dartpakailowercase_with_underscores:kAccent,kSuccess. - Boolean: pakai prefix
is,has,should,can→isLoading,hasError,shouldShow
1.4 Null Safety
- Hindari
!(force unwrap) kecuali benar-benar yakin tidak null. - Gunakan
latehanya untuk field yang di-init diinitStateatau setelah async yang dijamin selesai. - Gunakan
?(nullable) untuk parameter yang opsional. - Gunakan
??(default value) daripada ternary cek null. - Jangan biarkan
dynamickeculatau untuk JSON parsing yang benar-benar dinamis.
2. State Management (Provider)
2.1 AppState Structure
AppStateextendsChangeNotifier.- Semua mutable state disimpan di
AppState, bukan di widget state kecuali state lokal UI (misal_searchTextEditingController). - Gunakan
context.watch<AppState>()untuk rebuild saat state berubah. - Gunakan
context.read<AppState>()untuk aksi tanpa rebuild (misal dionPressed). - Jangan panggil
notifyListeners()di dalam getter — getter harus pure.
2.2 Async Operations
- Semua async yang modifikasi state harus wrapped dengan try-catch.
- Gunakan
_withTimeout()untuk semua operasi Supabase agar tidak hang selamanya. - Set
notifyListeners()di akhir operasi sukses maupun gagal agar UI tetap konsisten. - Jangan lupa handle
mountedcheck sebelumsetStateataunotifyListenersdi dalam async callback.
2.3 Offline Queue
- Semua operasi tulis (insert/update/delete) ke Supabase harus menyimpan ke
_offlineQueuejika gagal. - Jangan duplicate queue logic — panggil
_offlineQueue.addToQueue()di catch block. - Saat online,
OfflineQueueServiceakan otomatis retry.
3. Supabase & Database
3.1 Query Pattern
- Pakai
_withTimeout()untuk semua query Supabase. - Gunakan
.maybeSingle()untuk query yang return 0 atau 1 row. - Gunakan
.select()dengan kolom spesifik kecuali butuh semua kolom. - Jangan hardcode ID di query — gunakan parameter.
3.2 Error Handling
- Tangkap
PostgrestExceptionuntuk error spesifik Supabase. - Tangkap
TimeoutExceptionuntuk timeout. - Tangkap
Exceptionumum sebagai fallback. - Jangan expose stack trace ke user — tampilkan pesan yang ramah.
3.3 Transaction Safety
- Untuk operasi yang butuh atomic (misal checkout + update stock), lakukan di urutan yang benar:
- Insert sale
- Update stock untuk setiap item
- Reload products + sales
- Jika salah satu gagal, rollback manual atau tambahkan ke offline queue.
4. Widget & UI Pattern
4.1 Widget Composition
- Prefer composition over inheritance.
- Ekstrak widget kecil yang berulang jadi
constwidget terpisah. - Gunakan
constconstructor untuk semua widget yang immutable. - Jangan buat
StatefulWidgetkecuali butuh state lokal.
4.2 StatefulWidget Best Practices
- State harus minimal — simpan hanya yang benar-benar perlu berubah.
- Jangan simpan objek kompleks di state tanpa dispose (misal controller, timer, animation).
- Semua
FocusNode,AnimationController,Timer,StreamSubscriptionharus di-dispose. - Gunakan
SingleTickerProviderStateMixinuntukAnimationController,TickerProviderStateMixinuntuk multiple.
4.3 Dialog & Bottom Sheet
- Gunakan
showDialog+AlertDialoguntuk dialog konfirmasi/form pendek. - Gunakan
showModalBottomSheetdenganisScrollControlled: trueuntuk form panjang atau list yang perlu scroll. - Bungkus form dengan
SingleChildScrollView+padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom). - Gunakan
StatefulBuilderjika dialog butuh update UI internal (misal loading, toggle). - Jangan pass
contextdari dialog ke parent untuk navigasi — pass context yang benar saat showDialog.
4.4 List & Grid
- Gunakan
ListView.builderuntuk list panjang,ListView.separateduntuk list dengan separator. - Gunakan
GridView.builderuntuk grid panjang. - Tambahkan
shrinkWrap: truedanphysics: NeverScrollableScrollPhysics()jika list/grid di dalamSingleChildScrollView. - Jangan lupa
itemExtentatauprototypeItemjika list item heightnya fixed untuk performa.
4.5 Text & Format
- Semua angka mata uang wajib menggunakan
formatRupiah(). - Jangan hardcode string angka/uang di UI.
- Gunakan
TextOverflow.ellipsisuntuk teks yang bisa panjang. - Gunakan
maxLinesuntuk membatasi teks diRow/Columnyang bisa overflow.
5. Error & Edge Case Handling
5.1 Mounted Check
- Setiap async callback yang akses
contextatausetStateharus checkif (!mounted) returnsebelum akses. - Jangan gunakan
mounteduntuk logic bisnis — hanya untuk guard UI update.
5.2 Empty/Null State
- Selalu handle kasus data kosong:
if (products.isEmpty),if (cart.isEmpty). - Tampilkan empty state yang informatif, bukan cuma
Container()atauSizedBox.shrink().
5.3 Boundary Value
- Qty minimum: 1 (jika <= 0, remove dari cart).
- Harga minimum: 0 (jika <= 0, tampilkan error atau disable button).
- Stok: clamp ke
max(0, stok - qty)saat checkout. - Diskon: clamp ke
min(diskon, subtotal).
6. Testing & Verification
6.1 Static Analysis
- Jalankan
flutter analyzesebelum commit. - Target: No issues found (tanpa error, warning yang bisa diperbaiki).
- Dead code harus dihapus, tidak di-
//comment.
6.2 Manual Testing Checklist
- Test flow lengkap: login → kasir → tambah produk → bayar → struk → riwayat → detail.
- Test role kasir: akses yang disembunyikan benar-benar hilang.
- Test role admin: semua aksi admin muncul dan berfungsi.
- Test offline: matikan wifi, buat transaksi, hidupkan wifi, cek sync.
- Test keyboard: input uang/diskon dengan keyboard muncul.
- Test small screen: HP 5" atau emulator 360x640.
- Test landscape (jika applicable).
- Test rotation (jika applicable).
6.3 Performance
- Jangan rebuild seluruh list saat satu item berubah — gunakan
ListView.builder+key. - Jangan simpan controller di
build()— simpan diStatedan dispose. - Jangan lakukan network call di
build()— pindah keinitStateataudidChangeDependencies. - Gunakan
constconstructor untuk widget yang immutable.
7. Git & Commit
7.1 Commit Message
- Format:
<type>(<scope>): <description> - Type:
feat,fix,refactor,style,docs,test,chore - Contoh:
feat(kasir): tambah tombol Tambah Manual untuk pesanan offline - Jangan commit dengan message "update" atau "fix" tanpa deskripsi.
7.2 Branching
- Feature branch dari
main:feature/nama-fitur - Bug fix dari
main:fix/nama-bug - Jangan commit langsung ke
mainkecuali hotfix yang sudah di-review.
8. Anti-Patterns (Jangan Lakukan)
- ❌ Jangan hardcode warna, spacing, atau font size — gunakan design tokens.
- ❌ Jangan pakai
GlobalKeykecuali benar-benar perlu. - ❌ Jangan simpan state di
StatelessWidget— pindah keStatefulWidgetatauAppState. - ❌ Jangan lupa dispose controller/timer/stream subscription.
- ❌ Jangan paksa
setStatedi dalambuild()— akan infinite loop. - ❌ Jangan pakai
awaitdi dalambuild()tanpa FutureBuilder/future handling. - ❌ Jangan lupa handle error untuk semua async — jangan biarkan app crash.
- ❌ Jangan gabungkan logic bisnis dengan UI — pisahkan di
AppStateatau service layer. - ❌ Jangan duplicate widget — ekstrak jadi widget terpisah atau helper function.
- ❌ Jangan lupa check
mountedsebelum akses context setelah async gap.
9. Referensi Codebase
lib/state/app_state.dart— pattern state management, async handling, offline queue.lib/screens/kasir_screen.dart— pattern dialog, bottom sheet, form handling.lib/screens/riwayat_screen.dart— pattern list dengan role-based visibility, detail sheet.lib/utils/colors.dart— design tokens (warna, spacing, radius).lib/utils/format.dart— formatRupiah, formatTanggal, formatWaktu.lib/models/— pattern model dengan factory constructor + toJson/fromJson.