Belajar Flutter dari Awal: Menggunakan Data dari API Eksternal (Part 7)
Rifqi An
Menggunakan Data dari API Eksternal (Part 7): Ngulik Data API Pakai Flutter!
Halo sobat ngoding se-Nusantara! Balik lagi nih di seri tutorial Belajar Flutter dari Awal yang kali ini udah nyampe Part 7. Udah kerasa banget ya perjalanan ngoding kita? Dari cuma bisa bikin "Hello World", sekarang udah mulai mainan data dari internet. Mantul!
Di part-part sebelumnya, kita udah kenalan sama yang namanya API, gimana caranya ngajak ngobrol server pake HTTP request, terus cara "membaca" data JSON yang dikirim balik sama server itu. Nah, di Part 7 ini, kita bakal naik level lagi: gimana sih caranya update tampilan aplikasi kita secara otomatis setelah data dari API itu berhasil kita ambil? Atau, gimana kalo datanya ngambek alias error?
Siapkan kopi dan cemilan, karena sesi ngoding kali ini bakal seru dan (mungkin) sedikit bikin puyeng, tapi dijamin ilmunya nambah!
Daftar Isi
- Pengantar: Kenapa Masih Belajar API Aja, Min?
- Review Kilat: HTTP Request dan Parsing JSON
- Studi Kasus: Update Tampilan Setelah Data Berhasil Diambil
- Penanganan Error: Data Ngambek Nggak Mau Keluar
- Bonus Tip: Indikator Loading yang Manis
- Latihan: Misi Penyelamatan Data Nasi Goreng!
Pengantar: Kenapa Masih Belajar API Aja, Min?
Mungkin ada yang bertanya-tanya, "Min, ini API mulu, kapan move on ke yang lain?" Eits, jangan salah! Menggunakan data dari API itu kayak jembatan utama antara aplikasi kita dengan dunia luar. Hampir semua aplikasi modern, mulai dari belanja online, media sosial, sampai aplikasi ojek online, pasti ngandelin API untuk dapetin data real-time.
Bayangkan kalo aplikasi ojek online kamu nggak bisa dapetin data lokasi driver, atau aplikasi toko online kamu nggak bisa liat stok barang terbaru. Kan zonk banget, ya? Makanya, materi ini KRUSIAL banget, biar aplikasi Flutter kita nggak cuma jadi pajangan doang, tapi beneran interaktif dan berguna!
Review Kilat: HTTP Request dan Parsing JSON
Sebelum nyemplung lebih dalam, kita kilas balik dikit ya materi sebelumnya. Kita udah belajar gimana caranya pake package http di Flutter untuk ngirim request ke API. Contohnya kayak gini nih:
import 'package:http/http.dart' as http;
import 'dart:convert'; // Buat parsing JSON
Future<Map<String, dynamic>> fetchDataDariAPI() async {
final response = await http.get(Uri.parse('https://jsonplaceholder.typicode.com/posts/1'));
if (response.statusCode == 200) {
// Kalo server ngasih respons OK (status code 200)
return jsonDecode(response.body);
} else {
// Kalo ada masalah, lempar error aja biar jelas
throw Exception('Gagal memuat data. Server lagi ngambek kayaknya.');
}
}
Kode di atas intinya ngambil data dari API placeholder, terus kalo berhasil, data JSON-nya di-decode jadi Map di Dart. Nah, pertanyaannya sekarang, gimana caranya Map ini bisa nongol di UI kita?
Studi Kasus: Update Tampilan Setelah Data Berhasil Diambil
Ini dia inti dari pembahasan kita. Setelah data berhasil diambil, kita pasti pengen dong tampilan aplikasi kita langsung berubah, nunjukkin data yang baru. Ada beberapa cara, tapi kita bakal fokus ke dua yang paling sering dipake.
Tanpa FutureBuilder (Manual setState)
Cara yang paling "dasar" adalah dengan pake setState(). Kalo kamu inget materi Stateful Widget, setState() ini fungsinya buat bilang ke Flutter, "Hey, ada data yang berubah nih! Tolong di-redraw ulang ya UI-nya!"
Biasanya, kita bakal punya sebuah variabel di dalam State kita yang nyimpen data hasil dari API. Waktu data itu berhasil diambil, kita update variabelnya, lalu panggil setState().
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
class PostDisplayScreen extends StatefulWidget {
@override
_PostDisplayScreenState createState() => _PostDisplayScreenState();
}
class _PostDisplayScreenState extends State<PostDisplayScreen> {
Map<String, dynamic>? _postData; // Variabel buat nyimpen data post
bool _isLoading = false; // Buat nunjukkin status loading
String? _errorMessage; // Buat nyimpen pesan error
@override
void initState() {
super.initState();
_fetchData(); // Langsung panggil pas widget dibuat
}
Future<void> _fetchData() async {
setState(() {
_isLoading = true; // Set loading jadi true
_errorMessage = null; // Reset error message
});
try {
final response = await http.get(Uri.parse('https://jsonplaceholder.typicode.com/posts/1'));
if (response.statusCode == 200) {
setState(() {
_postData = jsonDecode(response.body);
_isLoading = false; // Data udah dapet, loading selesai
});
} else {
setState(() {
_errorMessage = 'Gagal memuat post: ${response.statusCode}';
_isLoading = false; // Gagal, loading selesai
});
}
} catch (e) {
setState(() {
_errorMessage = 'Terjadi kesalahan jaringan: $e';
_isLoading = false; // Gagal, loading selesai
});
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Detail Postingan (Manual)')),
body: Center(
child: _isLoading
? CircularProgressIndicator() // Kalo lagi loading
: _errorMessage != null
? Text('Error: $_errorMessage', style: TextStyle(color: Colors.red)) // Kalo error
: _postData == null
? Text('Belum ada data.') // Kalo data masih kosong
: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Judul: ${_postData!['title']}',
textAlign: TextAlign.center,
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold)),
SizedBox(height: 10),
Text('Isi: ${_postData!['body']}', textAlign: TextAlign.center),
SizedBox(height: 20),
ElevatedButton(
onPressed: _fetchData, // Bisa di-refresh
child: Text('Refresh Data'),
),
],
),
),
);
}
}
Pusing? Sedikit, ya. Kita harus ngatur variabel _isLoading, _postData, dan _errorMessage secara manual, terus panggil setState() di setiap perubahan. Kalo logikanya makin kompleks, kodingan kita bisa jadi "spaghetti code"!
Dengan FutureBuilder (Lebih Elegan, Katanya)
Nah, buat ngurangin pusing, Flutter punya widget sakti bernama FutureBuilder. Widget ini didesain khusus buat nanganin Future (proses asinkronus, kayak ngambil data dari API) dan bakal ngebangun UI berdasarkan status dari Future tersebut.
FutureBuilder punya tiga status utama:
ConnectionState.waiting: Lagi nunggu data. Cocok buat nampilin indikator loading.ConnectionState.done: Future-nya udah selesai, bisa berhasil atau error.- Kalo berhasil, kita bisa akses datanya lewat
snapshot.data. - Kalo error, kita bisa akses pesan error-nya lewat
snapshot.error.
Gimana caranya? Yuk, kita rombak kode yang tadi pake FutureBuilder!
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
// Fungsi buat ngambil data dari API (sama kayak sebelumnya)
Future<Map<String, dynamic>> fetchPostData() async {
final response = await http.get(Uri.parse('https://jsonplaceholder.typicode.com/posts/1'));
if (response.statusCode == 200) {
return jsonDecode(response.body);
} else {
throw Exception('Gagal memuat post. Status: ${response.statusCode}');
}
}
class PostDisplayScreenWithFutureBuilder extends StatefulWidget {
@override
_PostDisplayScreenWithFutureBuilderState createState() =>
_PostDisplayScreenWithFutureBuilderState();
}
class _PostDisplayScreenWithFutureBuilderState extends State<PostDisplayScreenWithFutureBuilder> {
late Future<Map<String, dynamic>> _futurePost; // Deklarasi Future
@override
void initState() {
super.initState();
_futurePost = fetchPostData(); // Inisialisasi Future di initState
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Detail Postingan (FutureBuilder)')),
body: Center(
child: FutureBuilder<Map<String, dynamic>>(
future: _futurePost, // Ini Future yang mau kita pantau
builder: (context, snapshot) {
// Cek status koneksi dari snapshot
if (snapshot.connectionState == ConnectionState.waiting) {
return CircularProgressIndicator(); // Lagi loading nih...
} else if (snapshot.hasError) {
return Text('Error: ${snapshot.error}', style: TextStyle(color: Colors.red)); // Ada error!
} else if (snapshot.hasData) {
// Data udah dapet, tampilkan!
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Judul: ${snapshot.data!['title']}',
textAlign: TextAlign.center,
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold)),
SizedBox(height: 10),
Text('Isi: ${snapshot.data!['body']}', textAlign: TextAlign.center),
SizedBox(height: 20),
ElevatedButton(
onPressed: () {
setState(() {
_futurePost = fetchPostData(); // Refresh data
});
},
child: Text('Refresh Data'),
),
],
);
} else {
// Kalo nggak ada data tapi nggak error juga (jarang terjadi)
return Text('Tidak ada data yang ditemukan.');
}
},
),
),
);
}
}
Gimana? Lebih rapi kan? Dengan FutureBuilder, kita nggak perlu lagi pusing ngatur _isLoading atau _errorMessage secara manual di setState(). Semuanya udah diurusin sama si FutureBuilder. Kita tinggal fokus ke tampilan untuk setiap kondisi: lagi nunggu, ada data, atau error. Mantap!
Penanganan Error: Data Ngambek Nggak Mau Keluar
Dalam dunia ngoding, apalagi yang berhubungan sama jaringan, error itu udah jadi makanan sehari-hari. Kadang server lagi down, kadang internet mati, kadang API-nya tiba-tiba berubah struktur datanya (ini yang paling bikin programmer nangis bombay pas lembur).
Untungnya, FutureBuilder udah nyediain parameter snapshot.hasError dan snapshot.error. Jadi, pas data nggak berhasil diambil (misalnya karena server ngasih status code 500, atau internet putus), kita bisa langsung tahu dan nampilin pesan error yang informatif ke user. Ini penting banget biar user nggak bingung kenapa aplikasinya cuma muter-muter aja indikator loadingnya.
Di kode FutureBuilder sebelumnya, kita udah coba nanganin error dengan Text('Error: ${snapshot.error}', style: TextStyle(color: Colors.red)). Itu udah cukup buat nampilin pesan error sederhana.
Bonus Tip: Indikator Loading yang Manis
Pas aplikasi lagi ngambil data, user pasti nggak mau liat layar kosong melompong. Makanya, indikator loading itu penting banget. Selain CircularProgressIndicator yang udah kita pake, kamu bisa explore banyak lagi indikator loading lainnya:
LinearProgressIndicator: Indikator loading berbentuk garis.- Paket pihak ketiga (third-party packages) di pub.dev, contohnya:
shimmer: Efek loading ala-ala skeleton (kayak blok abu-abu yang gerak-gerak sebelum konten asli muncul). Keren banget buat UI yang profesional.loading_indicator: Banyak pilihan animasi loading yang cantik.
Pilihannya banyak, sesuaikan aja sama kebutuhan dan estetika aplikasi kamu!
Latihan: Misi Penyelamatan Data Nasi Goreng!
Oke, biar makin paham dan ngantuknya ilang, yuk kita kerjain latihan seru!
Skenario:
Kamu adalah seorang programmer andalan di aplikasi "Warung Gaul Online". Bos kamu minta fitur baru: menampilkan daftar menu nasi goreng paling hits dari seluruh Indonesia. Tapi ada masalah! API yang disediakan (anggap aja https://api.warunggaul.com/nasigorenghits) itu agak rewel. Kadang responsnya sukses, kadang error 404 (menu nasi gorengnya lagi habis), atau malah loadingnya lama banget kayak nunggu gebetan bales chat.
Misi Kamu:
- Buatlah halaman di Flutter yang menampilkan daftar menu nasi goreng tersebut.
- Gunakan
FutureBuilderuntuk menangani proses pengambilan data dari API. - Saat data masih dalam proses diambil, tampilkan
CircularProgressIndicator. - Jika pengambilan data berhasil, tampilkan daftar menu nasi goreng (misalnya dalam
ListViewatauColumn) beserta harganya (anggap aja struktur datanya{'id': 1, 'nama': 'Nasi Goreng Gila', 'harga': 25000}). - Jika terjadi error (misalnya API ngasih status code non-200 atau ada masalah jaringan), tampilkan pesan error yang informatif dan kocak, seperti "Maaf, nasi gorengnya lagi ngambek tidak mau nongol" atau "Jaringan lagi galau, tidak bisa ambil data nasi goreng!". Jangan lupa kasih tombol "Coba Lagi" yang bisa me-refresh data.
- (Opsional) Kalo mau lebih greget, coba pakai API yang beneran ngasih error biar kamu bisa ngetes penanganan errornya. Contoh API yang sengaja error bisa kamu simulasiin sendiri, atau pake yang responsnya suka random antara sukses atau error.
Selamat berjuang, pendekar keyboard! Jangan lupa ngopi biar nggak error!
.png)