Lewati ke isi

Integrasi API: Menampilkan List Produk (GET)


Tujuan Pembelajaran

Setelah mengikuti materi ini, siswa mampu: 1. Menggunakan package HTTP dan flutter_dotenv di Flutter. 2. Mengamankan URL API menggunakan file .env. 3. Mengubah format JSON menjadi Dart Model menggunakan app.quicktype.io. 4. Membuat Service untuk mengambil data produk dari internet. 5. Menampilkan data dari API ke dalam UI menggunakan FutureBuilder.


Pertanyaan Pemantik

Di bab sebelumnya, kita membuat tampilan Toko Online dengan data "palsu" (dummy data) yang kita ketik manual di kodingan.

Bagaimana caranya agar aplikasi kita bisa menampilkan barang dagangan yang sesungguhnya dari database server?


1. Persiapan: Request URL

Kita sudah disiapkan API Server Toko Online yang berisi daftar produk.

URL API: https://online-shop-golang.vercel.app/products Method: GET

Silakan buka URL tersebut di browser atau Thunder Client. Kalian akan melihat data seperti ini:

[
  {
    "id": 1,
    "name": "Keyboard Mechanical Gaming red",
    "category": "Gaming",
    "price": 750000,
    "image_url": "https://m.media-amazon.com/images/...",
    "description": "Keyboard mechanical RBG...",
    "rating": 4.7
  },
  ...
]

2. Membuat Model Data (Cara Cepat)

Menulis model manual itu lama dan rawan salah ketik. Kita akan gunakan tools Quicktype untuk membuat model secara otomatis.

Langkah-langkah: 1. Copy SEMUA teks JSON dari URL di atas. 2. Buka situs app.quicktype.io. 3. Pastikan setting di sebelah kanan: * Name: Product * Language: Dart * Null Safety: Aktif (Make properties required/optional sesuai kebutuhan, biasanya default sudah oke). 4. Paste JSON di kolom kiri. 5. Copy kode Dart yang muncul di kolom kanan.

Implementasi di Project: 1. Buat file baru: lib/models/product_model.dart. 2. Paste kode dari Quicktype ke file tersebut.

Contoh hasil (disederhanakan):

import 'dart:convert';

List<Product> productFromJson(String str) => List<Product>.from(json.decode(str).map((x) => Product.fromJson(x)));

String productToJson(List<Product> data) => json.encode(List<dynamic>.from(data.map((x) => x.toJson())));

class Product {
    int id;
    String name;
    String category;
    int price;
    String imageUrl;
    String description;
    double rating;

    Product({
        required this.id,
        required this.name,
        required this.category,
        required this.price,
        required this.imageUrl,
        required this.description,
        required this.rating,
    });

    factory Product.fromJson(Map<String, dynamic> json) => Product(
        id: json["id"],
        name: json["name"],
        category: json["category"],
        price: json["price"],
        imageUrl: json["image_url"], // Perhatikan ini otomatis menyesuaikan snake_case ke camelCase
        description: json["description"],
        rating: json["rating"]?.toDouble(),
    );

    // ... method toJson
}


3. Konfigurasi Environment & Package

Agar url API kita rapi dan aman, kita akan menyimpannya dalam file terpisah menggunakan package flutter_dotenv.

A. Instalasi Package

  1. Buka terminal di VS Code.
  2. Jalankan perintah:
    flutter pub add http flutter_dotenv
    
  3. Tunggu proses selesai.

B. Membuat File .env

  1. Buat file baru di root folder project (sejajar dengan pubspec.yaml), beri nama .env.
  2. Isi file tersebut dengan:
    API_URL=https://online-shop-golang.vercel.app
    

C. Mendaftarkan Assets

  1. Buka file pubspec.yaml.
  2. Cari bagian assets (biasanya dikomentari).
  3. Tambahkan .env di sana:
    flutter:
      assets:
        - .env
    

D. Inisialisasi di Main

  1. Buka lib/main.dart.
  2. Ubah fungsi main() menjadi async dan load dotenv:

    import 'package:flutter/material.dart';
    import 'package:flutter_dotenv/flutter_dotenv.dart'; // Import ini
    
    Future<void> main() async {
      await dotenv.load(fileName: ".env");
      //runappnya jangan diubah
    }
    

4. Membuat Service API

Kita akan membuat "Pelayan" yang mengambil data, tapi sekarang URL-nya diambil dari .env.

  1. Buat folder lib/services.
  2. Buat file lib/services/product_service.dart.
  3. Tulis kode berikut:
import 'package:http/http.dart' as http;
import 'package:flutter_dotenv/flutter_dotenv.dart';
import '../models/product_model.dart'; 

class ProductService {
  // Ambil URL dari .env
  final String baseUrl = dotenv.env['API_URL'] ?? 'https://online-shop-golang.vercel.app';

  // Fungsi untuk mengambil list produk
  Future<List<Product>> getProducts() async {
    // 1. Tentukan alamat lengkap (gunakan baseUrl)
    var url = Uri.parse('$baseUrl/products');

    // 2. Lakukan Request GET
    var response = await http.get(url);

    // 3. Cek status apakah berhasil (200 OK)
    if (response.statusCode == 200) {
      // 4. Ubah respon JSON mentah menjadi List Objek Product
      return productFromJson(response.body);
    } else {
      // Jika gagal
      throw Exception('Gagal mengambil data produk');
    }
  }
}

5. Integrasi ke UI (FutureBuilder)

Sekarang kita akan menghubungkan Service tadi ke Halaman Produk (ui yang sudah kalian buat sebelumnya).

Kita akan menggunakan FutureBuilder. Widget ini sangat sakti karena bisa menangani 3 kondisi otomatis: 1. Loading: Saat data sedang diambil. 2. Success: Saat data berhasil didapat. 3. Error: Saat data gagal diambil (misal tidak ada internet).

Langkah-langkah: 1. Buka file halaman produk kalian (misalnya home_page.dart atau product_list_page.dart). 2. Panggil service di dalam class. 3. Ganti ListView manual kalian dengan FutureBuilder.

Contoh Kode:

import 'package:flutter/material.dart';
import '../services/product_service.dart';
import '../models/product_model.dart';

class HomePage extends StatefulWidget {
  const HomePage({super.key});

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  // Panggil service
  final ProductService productService = ProductService();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text("Toko Online API")),
      body: FutureBuilder<List<Product>>(
        // Minta data ke service
        future: productService.getProducts(),
        builder: (context, snapshot) {

          // 1. KONDISI LOADING
          if (snapshot.connectionState == ConnectionState.waiting) {
            return const Center(child: CircularProgressIndicator());
          }

          // 2. KONDISI ERROR
          if (snapshot.hasError) {
            return Center(child: Text("Error: ${snapshot.error}"));
          }

          // 3. KONDISI SUKSES (Data Ada)
          if (snapshot.hasData) {
            List<Product> products = snapshot.data!;

            return GridView.builder( // atau ListView.builder
              gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
                crossAxisCount: 2,
                childAspectRatio: 0.7,
              ),
              itemCount: products.length,
              itemBuilder: (context, index) {
                final product = products[index];

                // TAMPILKAN CARD PRODUK KALIAN DISINI
                return Card(
                  child: Column(
                    children: [
                      // Gambar dari Network
                      Expanded(
                        child: Image.network(
                          product.imageUrl,
                          fit: BoxFit.cover,
                          // Tambahkan errorBuilder untuk jaga-jaga gambar rusak
                          errorBuilder: (ctx, error, stack) => const Icon(Icons.broken_image),
                        ),
                      ),
                      Text(product.name),
                      Text("Rp ${product.price}"),
                    ],
                  ),
                );
              },
            );
          }

          return const Center(child: Text("Tidak ada data."));
        },
      ),
    );
  }
}

Tugas Praktikum

  1. Lakukan langkah-langkah configurasi .env dengan teliti.
  2. Pastikan Internet Aktif saat menjalankan aplikasi.
  3. Jika terjadi error "File .env not found", coba stop dan run ulang project kalian.
  4. Tantangan: Tambahkan fitur RefreshIndicator agar user bisa swipe ke bawah untuk reload data!

Ringkasan

  • flutter_dotenv digunakan untuk menyimpan konfigurasi rahasia/global.
  • FutureBuilder memudahkan kita menangani state asynchronous (Loading, Error, Data).