listviewseparatordividerPemula3 mnt baca

ListView.separated

Daftar dengan pemisah antar item: Divider atau widget custom.

Analogi: Bata dan Semen

Bayangkan kamu menyusun bata menjadi pagar. Batanya adalah item daftarmu, dan semen di antara bata adalah pemisahnya. ListView.builder biasa hanya memberimu batanya: kalau mau semen, kamu harus mengoleskannya sendiri di setiap sela, dan gampang kebablasan mengoles semen juga di ujung pagar (jelek dilihat).

ListView.separated adalah tukang yang lebih pintar: kamu cukup bilang "ini batanya, ini semennya", dan dia otomatis menaruh semen tepat di antara bata, tidak kurang, tidak lebih, tidak di ujung.

Kenapa Butuh .separated?

Daftar tanpa pemisah terlihat menyatu dan melelahkan dibaca, apalagi daftar panjang berisi teks mirip seperti kontak, chat, atau riwayat transaksi. Masalahnya, cara manual itu berantakan:

  • Menaruh Divider() di dalam itemBuilder membuat garis muncul juga setelah item terakhir, yang terlihat seperti kesalahan desain.
  • Mengakali dengan logika ganjil/genap (index % 2) membuat kode sulit dibaca dan rawan bug saat data berubah.

ListView.separated memisahkan dua tanggung jawab secara bersih: itemBuilder hanya membangun item, separatorBuilder hanya membangun pemisah. Flutter menjamin pemisah hanya muncul di antara item, tidak di awal dan tidak di akhir.

Contoh 1: Daftar Kontak Sederhana

Mulai dari yang paling umum: garis tipis di antara baris kontak.

dart
final kontak = ['Andi', 'Budi', 'Citra', 'Dewi'];

ListView.separated(
  itemCount: kontak.length,
  itemBuilder: (context, index) => ListTile(
    leading: CircleAvatar(child: Text(kontak[index][0])),
    title: Text(kontak[index]),
    subtitle: const Text('Terakhir online kemarin'),
  ),
  separatorBuilder: (context, index) => const Divider(
    height: 1,
    thickness: 1,
    indent: 72, // sejajar dengan teks, bukan dengan avatar
    endIndent: 16,
  ),
)

Perhatikan indent: 72: garis tidak dimulai dari tepi kiri layar, melainkan sejajar dengan teks di samping avatar. Detail kecil seperti ini yang membuat daftar terlihat rapi seperti aplikasi profesional.

Contoh 2: Daftar Chat dengan Header Tanggal Selingan

Separator tidak harus garis. Di aplikasi chat, pemisah bisa berupa label tanggal yang muncul setiap kali hari berganti:

dart
ListView.separated(
  itemCount: pesan.length,
  itemBuilder: (context, index) => BubbleChat(pesan: pesan[index]),
  separatorBuilder: (context, index) {
    final hariIni = pesan[index].tanggal;
    final hariBesok = pesan[index + 1].tanggal;
    if (!_hariYangSama(hariIni, hariBesok)) {
      return Padding(
        padding: const EdgeInsets.symmetric(vertical: 12),
        child: Center(
          child: Chip(label: Text(formatTanggal(hariBesok))),
        ),
      );
    }
    return const SizedBox(height: 6);
  },
)

Mengakses pesan[index + 1] di sini aman, karena separatorBuilder hanya dipanggil untuk indeks 0 sampai itemCount - 2 (pemisah terakhir ada di antara item terakhir dan item sebelumnya). Jadi index + 1 tidak akan pernah melebihi batas daftar.

Kesalahan Umum Pemula

1. Menaruh Divider di dalam itemBuilder

Salah:

dart
// JANGAN: garis muncul juga setelah item terakhir
itemBuilder: (context, index) => Column(
  children: [
    ListTile(title: Text(menu[index])),
    const Divider(),
  ],
),

Benar: pindahkan garis ke separatorBuilder dan pakai ListView.separated.

2. Salah hitung posisi iklan selingan

Banyak yang ingin menampilkan banner promo setiap 5 item lalu menulis index % 5 == 0. Karena indeks separator dimulai dari 0, banner justru muncul setelah item ke-1, ke-6, ke-11, bukan ke-5, ke-10, ke-15.

Salah:

dart
separatorBuilder: (context, index) {
  if (index % 5 == 0) return const BannerPromo(); // muncul di posisi yang salah
  return const Divider();
},

Benar:

dart
separatorBuilder: (context, index) {
  if ((index + 1) % 5 == 0) return const BannerPromo();
  return const Divider();
},

3. Divider terlalu mencolok

Divider() bawaan tanpa parameter memang berfungsi, tapi garis default yang gelap dan full-bleed membuat daftar terlihat berat. Atur height, thickness, color, dan indent agar pemisah menjadi elemen pendukung, bukan bintang utama.

Catatan teknis: separatorBuilder dipanggil tepat itemCount - 1 kali. Kalau itemCount bernilai 0 atau 1, ia tidak pernah dipanggil, jadi kamu tidak perlu menangani kasus daftar kosong secara khusus di sana.

Kapan Separator Custom Bersinar

Selain garis dan iklan selingan, separator custom cocok untuk mengelompokkan daftar panjang menjadi bagian-bagian yang mudah dipindai: header abjad di daftar kontak ("A", "B", "C"), subtotal per tanggal di riwayat transaksi, atau badge "Baru" di antara item lama dan item baru. Aturannya sederhana: kalau pemisahnya butuh logika yang bergantung pada posisi atau data di sekitarnya, separatorBuilder adalah tempat yang tepat. Kalau pemisahnya selalu sama untuk semua sela, cukup kembalikan satu widget const agar Flutter bisa me-cache-nya dan scroll tetap mulus.

Tantangan

Divider tiap item

Ubah ListView.builder menumu menjadi ListView.separated dengan Divider abu-abu di antara item.