Files
app-grosir/.kilo/agent/uiux-pos-grosir.md
2026-07-23 22:12:32 +07:00

11 KiB

UI/UX Design Skill — Dutra Store (POS Grosir Flutter)

Gunakan skill ini sebagai acuan setiap kali mengubah, menambah, atau meninjau tampilan aplikasi POS grosir berbasis Flutter ini. Jika ada pertentangan, prioritaskan yang tercantum di sini.

1. Tujuan Utama

  • Transaksi cepat tanpa hambatan visual.
  • Tidak ada overflow/keluar layar di ukuran HP kecil.
  • Status stok, harga, dan aksi utama terlihat jelas dalam 1 scan.
  • Maintain konsistensi visual antar screen.

2. Design System

2.1 Tema

  • Theme gelap (dark) sebagai basis utama.
  • Warna aksen: amber (Colors.amber).
  • Radius default: 18 untuk kartu, 12 untuk input/dialog, 8 untuk chip/tag.
  • Font: gunakan Theme.of(context).textTheme agar sesuai pengaturan sistem.

2.2 Warna Semantik

  • kAccent — amber untuk aksen dan highlight.
  • kSuccess — hijau untuk status lunas/berhasil.
  • kDanger — merah untuk pembatalan/error/stok habis.
  • kMuted — abu-abu untuk teks sekunder/timestamp.
  • kSurface2 — background chip yang tidak aktif.
  • kBorder — border untuk chip dan outline.

2.3 Spacing & Layout

  • Gunakan EdgeInsets tetap untuk konsistensi:
    • EdgeInsets.all(12) padding screen/list.
    • EdgeInsets.all(16) padding form/dialog.
    • EdgeInsets.symmetric(horizontal: 14, vertical: 10) untuk baris list.
  • Gap antar field/form: SizedBox(height: 10).
  • Gap antar section: SizedBox(height: 14).

2.4 Kartu (Card)

  • Semua item list/kartu harus punya padding minimal EdgeInsets.all(12).
  • Jangan hardcode tinggi kartu; gunakan Column + MainAxisSize.min atau IntrinsicHeight bila perlu.
  • Jika ada 3+ field di dalam kartu, prioritaskan wrap/scroll.
  • Status transaksi harus jelas:
    • Lunas: badge/icon hijau.
    • Belum lunas: badge/icon amber/oranye.
    • Dibatalkan: badge/icon merah + TextDecoration.lineThrough.

2.5 Input & Form

  • Label jelas, helper text opsional untuk field opsional.
  • Jangan taruh form di dalam Column tanpa SingleChildScrollView jika total tinggi form bisa melebihi layar.
  • Saat keyboard muncul, pastikan field terlihat:
    • Bungkus form dengan SingleChildScrollView + padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom).

2.6 Tombol & Aksi

  • Primary action (Bayar, Tambah, Simpan): FilledButton berwarna aksen.
  • Destructive action (Hapus, Batalkan): FilledButton / OutlinedButton berwarna merah (kDanger).
  • Secondary action (Batal, Tutup): TextButton atau OutlinedButton.
  • Setiap dialog harus punya urutan aksi: Konfirmasi di kanan/bawah, Batal di kiri/atas.

2.6a Haptic & Feedback

  • Aksi signifikan yang cepat: tambah item ke keranjang, print struk, scan barcode → HapticFeedback.lightImpact().
  • Aksi destruktif atau butuh konfirmasi: hapus item/batal transaksi → HapticFeedback.mediumImpact().
  • Sukses transaksi/tampilkan feedback →lanjut ke SnackBar hijau (kSuccess), jangan pakai dialog sukses untuk hal yang cepat.
  • Jangan paksa haptic di setiap tap; hanya untuk aksi yang jelas berdampak.

2.7 Auto-Scroll & Keyboard Behavior

  • Setiap form di dalam bottom sheet atau dialog harus meng-handle keyboard muncul/tutup.
  • Gunakan MediaQuery.of(context).viewInsets.bottom untuk padding bawah dinamis pada SingleChildScrollView.
  • Untuk bottom sheet form yang tinggi dan perlu auto-expand:
    1. Pakai DraggableScrollableController.
    2. Buat FocusNode untuk setiap TextField.
    3. Tambah listener ke setiap focus node: jika hasFocus, animasikan sheet ke maxChildSize (misal 0.92), lalu panggil Scrollable.ensureVisible() agar field aktif terlihat.
    4. Durasi animasi sheet: 300ms, scroll: 250ms, alignment: 0.0.
  • Jangan andal Scaffold.resizeToAvoidBottomInset saja untuk bottom sheet form kompleks; manual padding lebih stabil untuk kasus form panjang.

2.8 Empty State

  • Jangan tampilkan teks kosong tanpa visual indicator.
  • Minimal: icon + teks. Contoh: Icon(Icons.shopping_cart_outlined, size: 48, color: kMuted) + teks "Keranjang kosong".
  • Center alignment, Column dengan MainAxisSize.min.
  • Konsisten pakai ikon outlined variant untuk empty state.

2.9 Dialog Button Layout

  • Mobile: tombol dialog harus full-width stacked vertikal dengan SizedBox(height: 8) antar tombol.
  • Urutan: Batal di atas, Konfirmasi di bawah.
  • Jangan pakai Row untuk 2 tombol di dialog mobile, kecuali screen width > 600dp.
  • Tombol konfirmasi destruktif: FilledButton dengan backgroundColor: kDanger.
  • Tombol konfirmasi positif: FilledButton dengan backgroundColor: kAccent atau kSuccess.

2.10 Loading State

  • Untuk operasi < 500ms: tidak perlu loading indicator.
  • Untuk operasi > 500ms: tampilkan loading di button yang dipencet.
    • Spinner: CircularProgressIndicator(strokeWidth: 2, color: Color(0xFF06210F)) untuk light button text.
    • Spinner: CircularProgressIndicator(strokeWidth: 2, color: kAccent) untuk dark button text.
  • Untuk full-screen loading: gunakan CircularProgressIndicator(color: kAccent) di Center.
  • Jangan gunakan LinearProgressIndicator kecuali untuk determinate progress yang jelas.
  • Jika list kosong dan pengguna bisa melakukan aksi (misal tambah produk), tampilkan FloatingActionButton atau FilledButton sebagai helper agar tidak dead-end.

2.11 Error State

  • Error dari form validation: tampilkan inline di bawah field, warna kDanger, font 13.
  • Error dari operasi database/API: tampilkan SnackBar merah (kDanger).
  • Jangan hanya pakai warna tanpa label. Tambahkan ikon/prefix jika perlu.
  • Jika field error, pastikan TextField tetap bisa di-focus dan di-scroll.

3. Responsif & Overflow

3.1 Grid Produk (Kasir)

  • Default: 2 kolom di layar HP (crossAxisCount: 2).
  • Tablet (lebar > 600dp): naik jadi 3 kolom.
  • Jangan pakai childAspectRatio tetap yang bikin kartu terlalu tinggi/pendek. Gunakan AspectRatio(1.1) atau biarkan GridView yang menentukan tinggi.

3.2 Payment Method Chips

  • Urutan chip di UI: Tunai, Bank, QRIS, Bayar Nanti, Split.
  • Untuk mobile, kelompokkan menjadi:
    • Baris atas: 3 chip (Tunai, Bank, QRIS) dalam satu Row dengan Expanded.
    • Divider tipis antar kelompok.
    • Baris bawah: 2 chip (Bayar Nanti, Split) dalam satu Row dengan Expanded.
  • Jangan pakai Wrap untuk payment chips. Wrap boleh dipakai kembali untuk kasus lain seperti Filter chips atau Tag.
  • Chip harus compact, aman di layar kecil, dan tidak memaksa scroll horizontal.

3.3 Bottom Sheet

  • Keranjang harus berupa bottom sheet, bukan panel tetap.
  • Sheet harus bisa di-drag (DraggableScrollableSheet atau showModalBottomSheet dengan isScrollControlled: true).
  • Konten sheet harus selalu scrollable (SingleChildScrollView), tanpa Expanded di dalamnya, agar footer tidak pernah terhalangi keyboard.
  • Untuk menutup sheet dari aksi di dalamnya, gunakan Navigator.pop(sheetContext) bukan parentContext.

3.4 Dialog

  • Semua dialog input yang panjang harus dibungkus SingleChildScrollView atau SingleChildScrollView di dalam AlertDialog agar bisa di-scroll saat keyboard muncul.
  • Jangan pakai Expanded di dalam dialog yang berpotensi overflow.

3.5 Produk & Form Panjang

  • Tab Produk adalah gabungan form + list produk. Gunakan SingleChildScrollView + ListView.builder dengan shrinkWrap: true agar form bisa di-scroll tanpa overflow.

3.6 Teks

  • Jangan hardcode font size. Gunakan skema TextStyle dari ThemeData.
  • Jika label teks bisa panjang (mis. nama produk), batasi maksimal 2 baris dengan maxLines: 2 + overflow: TextOverflow.ellipsis.
  • ID transaksi/nama panjang: Expanded + TextOverflow.ellipsis agar tidak overflow di Row.

4. UX untuk Kasir (Critical Path)

  • Target: transaksi selesai dalam 3 tap (pilih produk → bayar → selesai).
  • Jangan sembunyikan aksi utama (Bayar) di bawah banyak opsi.
  • Tampilkan total, diskon, dan kembalian secara real-time.
  • Gunakan chip/segmented button untuk tipe pembeli (Umum/Reseller) — jangan dropdown.
  • Setiap item di keranjang harus bisa diubah qty atau tipe harga dalam 1 tap.
  • Jika stok habis, matikan visual (opacity turun, disable tap) dan tampilkan badge "Habis".
  • Jika stok menipis, tampilkan badge peringatan oranye sebelum habis.

5. Payment Flow: Bayar Nanti

  • Saat checkout pilih Bayar Nanti, tombol harus jelas: "Pesanan Sekarang".
  • Field "Uang Dibayar" menjadi disabled dengan label "Belum Bayar".
  • Tampilkan status "Belum Bayar" di receipt/summary dengan warna kMuted.
  • Di Riwayat, transaksi unpaid harus punya badge/status yang menonjol.
  • Saat customer datang bayar: tombol "Terima Pembayaran" atau "Bayar Sisa" di detail transaksi.

6. Aksesibilitas & Feedback

  • Minimal tap target: 44x44dp untuk semua tombol/icon.
  • Warna teks harus kontras dengan background (minimal 4.5:1 untuk teks normal).
  • Jangan hanya andal warna untuk status (tambah ikon/label). Contoh: stok habis = badge merah + teks "Habis".
  • Setiap aksi berhasil/gagal harus ada SnackBar feedback:
    • Sukses: hijau (kSuccess).
    • Error: merah (kDanger).
    • Info: biru/abu.
  • Loading state harus muncul saat operasi > 500ms. Gunakan CircularProgressIndicator(color: kAccent).

7. Role-Based Visibility

  • Fitur yang tidak diizinkan untuk role tertentu harus disembunyikan sepenuhnya, bukan cuma di-disable.
  • Admin: lihat semua transaksi + filter tanggal + aksi batalkan transaksi.
  • Kasir: lihat transaksi hari ini, tidak ada aksi batalkan, tidak ada filter tanggal.

8. List & Pagination

  • Gunakan ListView.separated dengan itemCount yang sesuai untuk performa.
  • Tombol "Muat lebih banyak" harus ada di bawah list jika data bisa banyak.
  • Filter chips: gunakan Wrap agar responsif di layar kecil.

8a. Printer & Thermal Receipt

  • Semua teks struk harus Bahasa Indonesia dan ringkas.
  • Struk wajib menampilkan: nama toko, tanggal, jam, nomor transaksi, daftar item, diskon (jika ada), total, metode pembayaran, detail pembayaran tunai/split (jika ada), dan nama kasir.
  • Jangan tampilkan label per-item Umum/Reseller di struk thermal, agar lebih rapi.
  • Jika splitPayment ada, tampilkan per bagian: Split Tunai, Split Bank, atau Split QRIS beserta nominalnya.
  • Batasi lebar konten sesuai PaperSize.mm58 agar tidak terpotong.
  • Setelah cetak, arahkan feedback ke user melalui SnackBar, bukan hanya log.
  • Flow pairing printer harus bisa diakses oleh role kasir, bukan hanya admin.

9. Reusability

  • Buat widget kecil reuseable untuk komponen yang berulang: _Tag, _ReportCard, _Avatar, _Stepper, _PaymentMethodChip.
  • Jangan duplikasi warna/spacing. Taruh konstanta di lib/utils/colors.dart.

10. Checklist Sebelum Submit Perubahan UI/UX

  • flutter analyze → No issues found (tanpa error).
  • Coba di layar kecil (HP 5") dan besar (tablet).
  • Coba dengan keyboard muncul (input uang/diskon).
  • Coba mode gelap dan terang (jika supported).
  • Pastikan tidak ada hardcoded height yang bikin overflow.
  • Semua teks dalam Bahasa Indonesia.
  • Konsisten pakai formatRupiah() untuk angka mata uang.

11. Referensi Project

  • lib/utils/colors.dart — design tokens.
  • lib/screens/kasir_screen.dart — contoh bottom sheet cart, grid produk, dialog pembayaran.
  • lib/screens/riwayat_screen.dart — contoh kartu laporan, filter role-based.