GridView.builder
Tata letak grid efisien untuk galeri dan katalog produk.
Kapan Memakai Grid
Grid adalah jawaban ketika item-itemmu setara dan paling enak dilihat berdampingan: galeri foto, katalog produk, menu dashboard, atau pilihan avatar. Seperti ListView.builder, GridView.builder juga lazy: ia hanya membangun sel yang sedang terlihat di layar, jadi ribuan item pun tetap ringan.
Satu-satunya hal baru yang perlu kamu pahami adalah gridDelegate, si pengatur tata letak yang menentukan berapa kolom dan seberapa besar tiap sel.
Contoh 1: Katalog Produk 2 Kolom
GridView.builder(
padding: const EdgeInsets.all(12),
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
crossAxisSpacing: 12,
mainAxisSpacing: 12,
childAspectRatio: 3 / 4,
),
itemCount: produk.length,
itemBuilder: (context, index) {
final p = produk[index];
return Card(
clipBehavior: Clip.antiAlias,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Expanded(
child: Image.network(p.gambar, fit: BoxFit.cover),
),
Padding(
padding: const EdgeInsets.all(8),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(p.nama, maxLines: 1, overflow: TextOverflow.ellipsis),
Text(
formatRupiah(p.harga),
style: const TextStyle(fontWeight: FontWeight.bold),
),
],
),
),
],
),
);
},
)childAspectRatio: 3 / 4 berarti rasio lebar:tinggi tiap sel adalah 3 banding 4, jadi sel lebih tinggi daripada lebarnya, cocok untuk kartu produk vertikal. maxLines: 1 dengan ellipsis mencegah nama produk yang panjang merusak layout.
Contoh 2: Galeri Foto dengan Status Loading
Untuk gambar dari internet, selalu tangani tiga kondisi: sedang dimuat, berhasil, dan gagal.
GridView.builder(
padding: const EdgeInsets.all(12),
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 3,
crossAxisSpacing: 8,
mainAxisSpacing: 8,
),
itemCount: foto.length,
itemBuilder: (context, index) => ClipRRect(
borderRadius: BorderRadius.circular(8),
child: Image.network(
foto[index],
fit: BoxFit.cover,
loadingBuilder: (context, child, progress) {
if (progress == null) return child;
return const Center(child: CircularProgressIndicator());
},
errorBuilder: (context, error, stackTrace) {
return const ColoredBox(
color: Colors.black12,
child: Icon(Icons.broken_image, color: Colors.grey),
);
},
),
),
)Tanpa loadingBuilder, sel akan kosong melompong saat gambar diunduh dan grid terlihat berkedip. Tanpa errorBuilder, satu URL rusak bisa menampilkan kotak merah error yang merusak seluruh galeri.
Edge Cases
- Teks overflow di dalam sel: karena tinggi sel ditentukan oleh rasio, teks yang kepanjangan akan overflow (garis kuning-hitam). Solusinya:
maxLines+TextOverflow.ellipsis, atauFittedBoxuntuk teks yang harus selalu muat satu baris. - Grid di dalam halaman scroll lain: misalnya grid produk di bawah header dalam
SingleChildScrollView. Kamu butuhshrinkWrap: truedanphysics: const NeverScrollableScrollPhysics(). Tapi ingat, ini mematikan lazy loading: seluruh item dibangun sekaligus. Untuk data besar, pakaiCustomScrollView+SliverGridagar tetap lazy. childAspectRatioadalah lebar dibagi tinggi. Nilai di atas 1 berarti sel melebar, di bawah 1 berarti sel meninggi. Mudah tertukar, jadi selalu baca ulang saat layout terlihat aneh.
Kapan JANGAN Memakai GridView.builder
- Item sedikit dan statis (kurang dari 10, misalnya menu dashboard tetap):
GridView.countlebih ringkas karena tidak butuhitemBuilder. - Ukuran sel bervariasi (grid ala Pinterest): grid bawaan selalu seragam per baris. Pakai
MasonryGridViewdari packageflutter_staggered_grid_view. - Grid hanya sebagian kecil halaman scroll kompleks (header + grid + footer): pakai
SliverGriddi dalamCustomScrollViewagar semua tetap lazy dalam satu scroll.
Praktik Terbaik
- Beri
Keypada item yang stateful (misalnya kartu dengan tombol favorit) supaya state tidak tertukar saat list di-scroll atau data berubah. - Hindari widget mahal di tiap sel (bayangan tebal,
BackdropFilter, blur) untuk grid panjang: puluhan blur yang dirender bersamaan bisa menurunkan frame rate. - Selalu beri
paddingdi grid, bukan margin di tiap item: lebih konsisten dan tidak menggandakan jarak di tepi.
Catatan teknis: Butuh jumlah kolom yang menyesuaikan lebar layar (2 di HP, 4 di tablet)? Itu tugas
SliverGridDelegateWithMaxCrossAxisExtent, dibahas di modul berikutnya.
Tantangan
Katalog 2 kolom
Buat GridView 2 kolom berisi 6 kartu produk (nama 'Produk 1'..'Produk 6'), dengan jarak 12 dan rasio 3/4.