11 KiB
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).textThemeagar 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
EdgeInsetstetap 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.minatauIntrinsicHeightbila 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
ColumntanpaSingleChildScrollViewjika 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).
- Bungkus form dengan
2.6 Tombol & Aksi
- Primary action (Bayar, Tambah, Simpan):
FilledButtonberwarna aksen. - Destructive action (Hapus, Batalkan):
FilledButton/OutlinedButtonberwarna merah (kDanger). - Secondary action (Batal, Tutup):
TextButtonatauOutlinedButton. - 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
SnackBarhijau (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.bottomuntuk padding bawah dinamis padaSingleChildScrollView. - Untuk bottom sheet form yang tinggi dan perlu auto-expand:
- Pakai
DraggableScrollableController. - Buat
FocusNodeuntuk setiapTextField. - Tambah listener ke setiap focus node: jika
hasFocus, animasikan sheet kemaxChildSize(misal0.92), lalu panggilScrollable.ensureVisible()agar field aktif terlihat. - Durasi animasi sheet:
300ms, scroll:250ms, alignment:0.0.
- Pakai
- Jangan andal
Scaffold.resizeToAvoidBottomInsetsaja 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,
ColumndenganMainAxisSize.min. - Konsisten pakai ikon
outlinedvariant 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
Rowuntuk 2 tombol di dialog mobile, kecuali screen width > 600dp. - Tombol konfirmasi destruktif:
FilledButtondenganbackgroundColor: kDanger. - Tombol konfirmasi positif:
FilledButtondenganbackgroundColor: kAccentataukSuccess.
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.
- Spinner:
- Untuk full-screen loading: gunakan
CircularProgressIndicator(color: kAccent)diCenter. - Jangan gunakan
LinearProgressIndicatorkecuali untuk determinate progress yang jelas. - Jika list kosong dan pengguna bisa melakukan aksi (misal tambah produk), tampilkan
FloatingActionButtonatauFilledButtonsebagai 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
SnackBarmerah (kDanger). - Jangan hanya pakai warna tanpa label. Tambahkan ikon/prefix jika perlu.
- Jika field error, pastikan
TextFieldtetap 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
childAspectRatiotetap yang bikin kartu terlalu tinggi/pendek. GunakanAspectRatio(1.1)atau biarkanGridViewyang 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 satuRowdenganExpanded. - Divider tipis antar kelompok.
- Baris bawah: 2 chip (
Bayar Nanti,Split) dalam satuRowdenganExpanded.
- Baris atas: 3 chip (
- Jangan pakai
Wrapuntuk payment chips.Wrapboleh dipakai kembali untuk kasus lain sepertiFilter chipsatauTag. - 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 (
DraggableScrollableSheetataushowModalBottomSheetdenganisScrollControlled: true). - Konten sheet harus selalu scrollable (
SingleChildScrollView), tanpaExpandeddi dalamnya, agar footer tidak pernah terhalangi keyboard. - Untuk menutup sheet dari aksi di dalamnya, gunakan
Navigator.pop(sheetContext)bukanparentContext.
3.4 Dialog
- Semua dialog input yang panjang harus dibungkus
SingleChildScrollViewatauSingleChildScrollViewdi dalamAlertDialogagar bisa di-scroll saat keyboard muncul. - Jangan pakai
Expandeddi dalam dialog yang berpotensi overflow.
3.5 Produk & Form Panjang
- Tab Produk adalah gabungan form + list produk. Gunakan
SingleChildScrollView+ListView.builderdenganshrinkWrap: trueagar form bisa di-scroll tanpa overflow.
3.6 Teks
- Jangan hardcode font size. Gunakan skema
TextStyledariThemeData. - Jika label teks bisa panjang (mis. nama produk), batasi maksimal 2 baris dengan
maxLines: 2+overflow: TextOverflow.ellipsis. - ID transaksi/nama panjang:
Expanded+TextOverflow.ellipsisagar tidak overflow diRow.
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
SnackBarfeedback:- Sukses: hijau (
kSuccess). - Error: merah (
kDanger). - Info: biru/abu.
- Sukses: hijau (
- 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.separateddenganitemCountyang sesuai untuk performa. - Tombol "Muat lebih banyak" harus ada di bawah list jika data bisa banyak.
- Filter chips: gunakan
Wrapagar 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
splitPaymentada, tampilkan per bagian:Split Tunai,Split Bank, atauSplit QRISbeserta 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.