flutterhttpapiMenengah3 mnt baca

HTTP POST: Mengirim Data ke API

Kirim data form dan JSON ke server dengan POST, tangani loading dan error saat submit.

POST dengan body JSON

Kebanyakan API modern menerima JSON. Encode map Dart menjadi string JSON dan set header content type:

dart
Future<Produk> tambahProduk({required String nama, required int harga}) async {
  final uri = Uri.parse('https://api.contoh.id/produk');
  final response = await http.post(
    uri,
    headers: {'Content-Type': 'application/json'},
    body: jsonEncode({'nama': nama, 'harga': harga}),
  );

  if (response.statusCode == 201) {
    return Produk.fromJson(jsonDecode(response.body) as Map<String, dynamic>);
  }
  throw Exception('Gagal menambah produk: ${response.statusCode}');
}

Status 201 (Created) adalah respons standar untuk POST yang berhasil membuat resource baru.

Pola submit di UI

Saat submit, nonaktifkan tombol dan tampilkan loading agar user tidak menekan dua kali:

dart
class _TambahState extends State<Tambah> {
  bool _loading = false;
  final _namaController = TextEditingController();
  final _hargaController = TextEditingController();

  Future<void> _submit() async {
    if (_loading) return;
    setState(() => _loading = true);
    try {
      final produk = await tambahProduk(
        nama: _namaController.text.trim(),
        harga: int.tryParse(_hargaController.text) ?? 0,
      );
      if (!mounted) return;
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('${produk.nama} tersimpan')),
      );
      Navigator.pop(context, true);
    } catch (e) {
      if (!mounted) return;
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('Gagal: $e')),
      );
    } finally {
      if (mounted) setState(() => _loading = false);
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Tambah Produk')),
      body: Padding(
        padding: const EdgeInsets.all(16),
        child: Column(
          children: [
            TextField(controller: _namaController, decoration: const InputDecoration(labelText: 'Nama')),
            const SizedBox(height: 12),
            TextField(controller: _hargaController, decoration: const InputDecoration(labelText: 'Harga'), keyboardType: TextInputType.number),
            const SizedBox(height: 16),
            SizedBox(
              width: double.infinity,
              child: ElevatedButton(
                onPressed: _loading ? null : _submit,
                child: _loading
                    ? const SizedBox(height: 20, width: 20, child: CircularProgressIndicator(strokeWidth: 2))
                    : const Text('Simpan'),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

Pola try/catch/finally dengan mounted check adalah template standar untuk setiap aksi async yang menyentuh UI setelahnya.

Validasi sebelum kirim

Jangan mengandalkan validasi server saja. Cek di client dulu: nama tidak kosong, harga lebih dari nol. Ini menghemat request gagal dan memberi feedback instan.

Catatan teknis: jsonEncode hanya bisa meng-encode tipe dasar (String, num, bool, null, List, Map). Kalau kamu meneruskan objek custom langsung, ia akan throw. Selalu konversi ke toJson() dulu. Dan untuk API yang butuh token, jangan hardcode token di kode, simpan di secure storage dan kirim lewat header Authorization: Bearer ....