FutureBuilder: Menunggu Data Asinkron
Tampilkan loading, data, dan error dari Future dengan FutureBuilder tanpa setState manual.
Masalah data asinkron
Banyak data datang terlambat: dari API, database, atau file. Kamu tidak bisa menaruh await di dalam build karena build harus sinkron dan cepat. FutureBuilder adalah widget yang mendengarkan Future lalu me-rebuild dirinya sendiri saat status berubah.
Struktur dasar
class CuacaHariIni extends StatelessWidget {
const CuacaHariIni({super.key});
Future<String> _ambilCuaca() async {
await Future.delayed(const Duration(seconds: 2));
return 'Cerah 30C';
}
@override
Widget build(BuildContext context) {
return FutureBuilder<String>(
future: _ambilCuaca(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(child: CircularProgressIndicator());
}
if (snapshot.hasError) {
return Center(child: Text('Gagal: ${snapshot.error}'));
}
if (snapshot.hasData) {
return Center(
child: Text('Cuaca: ${snapshot.data}', style: const TextStyle(fontSize: 20)),
);
}
return const Center(child: Text('Tidak ada data'));
},
);
}
}AsyncSnapshot memberi tahu semuanya: connectionState (waiting, active, done), hasData, hasError, dan error.
Contoh realistis: daftar dari API
FutureBuilder<List<Produk>>(
future: repository.ambilProduk(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(child: CircularProgressIndicator());
}
if (snapshot.hasError) {
return PusatError(
pesan: 'Gagal memuat produk',
onCobaLagi: () => setState(() {}),
);
}
final produk = snapshot.data ?? [];
if (produk.isEmpty) {
return const Center(child: Text('Belum ada produk'));
}
return ListView.builder(
itemCount: produk.length,
itemBuilder: (context, i) => ListTile(title: Text(produk[i].nama)),
);
},
)Urutannya selalu sama: cek loading dulu, lalu error, lalu data kosong, terakhir data ada. Konsisten dengan urutan ini di seluruh aplikasi.
Catatan teknis: Jebakan paling umum adalah membuat Future di dalam
build, sepertifuture: _ambilCuaca()pada contoh pertama. Setiap rebuild membuat Future baru dan request diulang, bisa menyebabkan loading tak berujung atau request ganda ke API. Solusinya: simpan Future di field State dan inisialisasi diinitState, misalnyalate final _futureCuaca = _ambilCuaca();lalufuture: _futureCuaca. Untuk refresh manual, buat ulang Future di dalamsetState.