Lompat ke konten utama
←

Flutter Dasar: Dart, Widget, dan State

Pelajaran 16 dari 18

Data dari internet dan penyimpanan lokal

FutureBuilder: memuat, berhasil, dan galat

Setelah pelajaran ini kamu bisa memprediksi tampilan FutureBuilder pada keadaan memuat, berhasil, dan galat, serta menempatkan pembuatan Future di initState.

  • ±13 menit
  • 3 latihan
  • Diperiksa 28 Okt 2026

Latihan di pelajaran ini butuh akun gratis. Progres, kode, dan catatanmu hanya tersimpan di browser ini. Masuk untuk menyimpannya

Materi

build tidak bisa ditandai async, jadi Future ditampilkan lewat widget FutureBuilder. Ia butuh dua hal: future yang ditunggu, dan fungsi builder(context, snapshot) yang dijalankan ulang setiap keadaan future berubah. snapshot.hasData bernilai true hanya bila future selesai dengan data yang tidak null. snapshot.hasError bernilai true bila future melempar galat, dan snapshot.error berisi galat itu. Selama keduanya false, builder menampilkan penanda memuat, misalnya CircularProgressIndicator.

class _JadwalState extends State<Jadwal> {
  late Future<List<Shift>> _jadwal;

  @override
  void initState() {
    super.initState();
    _jadwal = ambilJadwal(); // dibuat sekali
  }

  @override
  Widget build(BuildContext context) {
    return FutureBuilder<List<Shift>>(
      future: _jadwal,
      builder: (context, snapshot) {
        if (snapshot.hasData) {
          return ListView(children: [for (final s in snapshot.data!) Text(s.hari)]);
        } else if (snapshot.hasError) {
          return Text('${snapshot.error}');
        }
        return const CircularProgressIndicator();
      },
    );
  }
}

Future dibuat di initState dan disimpan di field State. Bila ambilJadwal() ditulis langsung di parameter future di dalam build, setiap kali build berjalan, misalnya karena setState lain di layar itu, permintaan baru terkirim dan FutureBuilder menunggu dari awal. Menyimpan Future di State membuat permintaan dijalankan sekali dan hasilnya dipakai ulang di setiap build. Resep resmi Flutter memberi alasannya: build dipanggil setiap kali tampilan perlu berubah, dan itu terjadi sangat sering.

Latihan

0/3 lulus

Jawaban diperiksa di server. Lulus berarti kamu memahami contohnya, bukan penilaian seluruh pekerjaanmu.

Latihan 1 dari 3

Server sedang mati sehingga ambilKatalog() melempar Exception('Gagal memuat katalog'). Setelah future selesai, apa yang tampil?

FutureBuilder<List<Produk>>(
  future: _katalog, // dibuat di initState dengan ambilKatalog()
  builder: (context, snapshot) {
    if (snapshot.hasData) {
      return ListView(children: [for (final p in snapshot.data!) Text(p.nama)]);
    } else if (snapshot.hasError) {
      return Text('${snapshot.error}');
    }
    return const CircularProgressIndicator();
  },
)
Pilih satu jawaban

Latihan 2 dari 3

Pengguna mengetik kata kunci teh, satu huruf demi satu huruf. Kenapa server menerima beberapa permintaan katalog, bukan satu?

class _KatalogState extends State<Katalog> {
  String _cari = '';

  @override
  Widget build(BuildContext context) {
    return Column(children: [
      TextField(onChanged: (teks) => setState(() => _cari = teks)),
      Expanded(
        child: FutureBuilder<List<Produk>>(
          future: ambilKatalog(),
          builder: (context, snapshot) {
            // menampilkan produk yang namanya memuat _cari
            return const SizedBox();
          },
        ),
      ),
    ]);
  }
}
Pilih satu jawaban

Latihan 3 dari 3

Server membalas 404, dan ambilProduk mengembalikan null. Apa yang tampil?

Future<Produk?> ambilProduk(int id) async {
  final respons = await http.get(Uri.parse('https://api.toko.example/produk/$id'));
  if (respons.statusCode == 200) {
    return Produk.fromJson(jsonDecode(respons.body) as Map<String, dynamic>);
  }
  return null;
}

// di build:
FutureBuilder<Produk?>(
  future: _produk, // dibuat di initState dengan ambilProduk(99)
  builder: (context, snapshot) {
    if (snapshot.hasData) {
      return Text(snapshot.data!.nama);
    } else if (snapshot.hasError) {
      return Text('${snapshot.error}');
    }
    return const CircularProgressIndicator();
  },
)
Pilih satu jawaban

Catatanmu

Memuat catatan…

tersimpan otomatis, ikut tercetak · 0/4.000

Lanjutkan ke