flutterfutureasyncMenengah3 mnt baca

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

dart
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

dart
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, seperti future: _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 di initState, misalnya late final _futureCuaca = _ambilCuaca(); lalu future: _futureCuaca. Untuk refresh manual, buat ulang Future di dalam setState.