Overview #
Hampir setiap aplikasi seluler modern memerlukan koneksi jaringan untuk berfungsi secara optimal. Baik itu untuk mengambil daftar produk terbaru di aplikasi e-commerce, mengirimkan pesan obrolan di aplikasi sosial media, memproses transaksi pembayaran secara real-time, maupun memperbarui profil pengguna di server backend. Komunikasi jaringan bertindak sebagai jembatan yang menghubungkan aplikasi klien (dalam hal ini, aplikasi Flutter kita) dengan dunia luar melalui server API (Application Programming Interface).
Di ekosistem Flutter, komunikasi jaringan ini sebagian besar dibangun di atas protokol HTTP (HyperText Transfer Protocol) atau HTTPS (HTTP Secure) yang berjalan di atas protokol transport TCP/IP. Untuk mengembangkan aplikasi yang andal, cepat, dan mudah dipelihara, kita tidak hanya dituntut untuk mengetahui cara memanggil API, melainkan juga harus memahami bagaimana siklus hidup komunikasi tersebut berjalan, bagaimana status code diolah, serta bagaimana mengarsiteki layer networking secara bersih.
Dalam dokumen overview ini, kita akan membedah konsep-konsep mendasar dari jaringan di Flutter, menganalisis metode HTTP, memahami metadata seperti header dan format pertukaran data JSON, hingga merancang arsitektur berlapis yang siap digunakan dalam skala produksi.
Siklus Hidup Request-Response HTTP #
Komunikasi antara aplikasi Flutter (klien) dan server API (backend) selalu mengikuti pola transaksi tunggal yang disebut siklus hidup Request-Response. Aplikasi Flutter bertindak sebagai pengambil inisiatif yang mengirimkan permintaan (request), sementara server backend bertindak sebagai penyedia layanan yang memproses permintaan tersebut dan mengembalikan jawaban (response).
Berikut adalah diagram alir sederhana yang menggambarkan bagaimana data mengalir antara aplikasi Flutter kita dan server API selama siklus transaksi HTTP berlangsung:
graph TD
classDef default stroke:#333,stroke-width:2px;
A["Aplikasi Flutter (Klien)"] -->|"Kirim Request (Method, URL, Headers, Body)"| B["Server API (Backend)"]
B -->|"Proses Logika & Query Database"| B
B -->|"Kirim Response (Status Code, Headers, Body)"| AMari kita telusuri langkah demi langkah apa yang sebenarnya terjadi di balik layar ketika aplikasi kita memicu sebuah panggilan API:
- Resolusi DNS (Domain Name System): Flutter membaca URL yang kita minta (misal:
https://api.tokokita.com/v1/produk). Sistem operasi perangkat akan menerjemahkan nama domainapi.tokokita.commenjadi alamat IP numerik (seperti104.26.10.12) melalui server DNS. - TCP Handshake: Setelah alamat IP diketahui, klien akan membuka koneksi ke server melalui protokol TCP (Transmission Control Protocol) dengan mengirimkan sinyal sinkronisasi (SYN, SYN-ACK, ACK) untuk menjamin koneksi fisik telah terjalin dengan aman.
- SSL/TLS Handshake (untuk HTTPS): Karena kita menggunakan protokol aman HTTPS, klien dan server akan melakukan pertukaran sertifikat keamanan untuk memverifikasi identitas server dan menyepakati kunci enkripsi simetris yang akan digunakan untuk mengamankan data selama perjalanan.
- Pengiriman Request: Klien mengirimkan payload HTTP request yang berisi:
- HTTP Method: Instruksi jenis operasi yang diinginkan (GET, POST, dll).
- Path/URL: Lokasi spesifik sumber daya yang diminta di server.
- Headers: Metadata tambahan seperti token keamanan atau tipe format data.
- Body: Payload data mentah (biasanya JSON) yang dikirimkan (jika ada).
- Pemrosesan Server: Server menerima request, memeriksa autentikasi header, menjalankan logika bisnis backend (misal: membaca database), dan menyusun payload jawaban.
- Pengembalian Response: Server mengirimkan paket data HTTP response kembali ke perangkat pengguna, yang terdiri atas:
- HTTP Status Code: Angka penunjuk hasil operasi (misal: 200 OK, 401 Unauthorized, 404 Not Found).
- Headers: Metadata dari server (seperti jenis server, cache control, atau ukuran data).
- Body: Konten utama data yang diminta (biasanya dalam string berformat JSON).
- Pembersihan & Penutupan: Klien menerima response, menganalisis data, dan koneksi TCP ditutup atau disimpan sementara di pool koneksi (keep-alive) untuk digunakan kembali pada request berikutnya guna menghemat daya baterai dan waktu handshake.
Metode HTTP (HTTP Methods) & Karakteristiknya #
Setiap kali kita mengirimkan request, kita wajib menentukan jenis aksi yang ingin kita jalankan melalui HTTP Methods (sering disebut sebagai kata kerja HTTP atau HTTP Verbs). Metode ini membantu server memahami maksud dari permintaan kita secara semantik.
Berikut adalah lima metode HTTP yang paling sering kita gunakan dalam pembuatan aplikasi RESTful API beserta karakteristik teknisnya:
GET --> Membaca / mengambil data dari server.
Tidak diperbolehkan mengirimkan data melalui Body (hanya Query Parameters).
Idempotent: Ya | Safe: Ya
POST --> Membuat data baru di server.
Data baru dikirimkan di dalam Body request.
Idempotent: Tidak | Safe: Tidak
PUT --> Memperbarui seluruh data (mengganti objek lama secara total).
Jika data yang dicari tidak ada, server bisa membuat data baru.
Idempotent: Ya | Safe: Tidak
PATCH --> Memperbarui sebagian kecil field dari data yang ada.
Hanya mengirimkan field yang berubah saja di dalam Body.
Idempotent: Tidak | Safe: Tidak
DELETE --> Menghapus data spesifik dari server berdasarkan ID.
Biasanya tidak memerlukan Body request.
Idempotent: Ya | Safe: Tidak
Memahami Idempotent dan Safe #
Di dalam protokol HTTP, terdapat dua konsep penting terkait karakteristik metode:
- Safe Methods (Metode Aman): Metode dikatakan aman jika metode tersebut tidak mengubah state data apa pun di server (read-only). Contohnya adalah
GET. MemanggilGET /produksebanyak 100 kali tidak akan mengurangi stok barang atau mengubah harga di database server. - Idempotent Methods (Metode Idempotent): Metode dikatakan idempotent jika efek dari membuat satu request sukses sama dengan efek dari membuat beberapa request sukses secara beruntun.
DELETEadalah idempotent. Menghapus produk dengan ID 123 satu kali akan menghapus produk tersebut. Menghapusnya kembali untuk kedua atau ketiga kali tidak akan memberikan efek samping baru di database server (server tetap mengonfirmasi produk terhapus atau memberi tahu data sudah tidak ada).POSTbukan idempotent. Jika kita menekan tombol “Bayar” yang mengirimkan requestPOST /checkoutsebanyak tiga kali karena koneksi internet lambat, server berpotensi membuat tiga transaksi pembayaran yang berbeda di database (double charging). Itulah mengapa kita harus sangat berhati-hati dan menonaktifkan tombol submit di UI selama proses POST asinkron berjalan.
Memahami Status Code HTTP #
Ketika server mengembalikan response, bagian pertama yang harus kita periksa adalah Status Code. Status code berupa kode angka 3 digit yang dikelompokkan menjadi lima rumpun standar industri berdasarkan digit pertamanya.
Mari kita pelajari rumpun status code tersebut beserta contoh kasus yang sering kita hadapi di aplikasi Flutter:
1. Rumpun 2xx (Success) #
Menunjukkan bahwa permintaan yang dikirimkan oleh aplikasi kita telah diterima, dipahami, dan diproses dengan sukses oleh server.
200 OK: Permintaan berhasil. Server mengembalikan data yang kita minta di dalam body response. Biasanya digunakan untuk respons metodeGET,PUT, danPATCH.201 Created: Permintaan berhasil dan server telah membuat sumber daya baru di database. Biasanya dikembalikan setelah pemanggilan metodePOST(misal: pendaftaran user baru).204 No Content: Permintaan sukses diproses tetapi server sengaja tidak mengembalikan konten apa pun di dalam body response. Sangat sering digunakan untuk respons metodeDELETE.
2. Rumpun 3xx (Redirection) #
Menunjukkan bahwa aplikasi kita perlu melakukan tindakan tambahan (seperti berpindah URL) untuk menyelesaikan permintaan tersebut.
301 Moved Permanently: URL yang diminta telah dipindahkan secara permanen ke lokasi baru. Klien harus otomatis mengarahkan request ke URL baru tersebut.304 Not Modified: Respons ini sangat berguna untuk efisiensi kuota data internet. Server memberi tahu bahwa data yang kita minta belum mengalami perubahan sejak pengambilan terakhir kita (berdasarkan header ETag atau Last-Modified). Aplikasi Flutter kita dapat langsung mengambil data lama dari memori cache lokal tanpa perlu mengunduh ulang payload yang besar.
3. Rumpun 4xx (Client Error) #
Menunjukkan bahwa terdapat kesalahan dari sisi klien (aplikasi Flutter kita), seperti salah mengirimkan format data, token kedaluwarsa, atau meminta data yang tidak ada di server.
400 Bad Request: Server tidak dapat memproses request karena format pengiriman data dari kita salah (misal: JSON rusak atau field wajib tidak diisi).401 Unauthorized: Klien belum terautentikasi. Biasanya terjadi karena kita lupa menyematkan token akses (Bearer token) di header Authorization, atau token yang kita gunakan sudah kedaluwarsa.403 Forbidden: Klien sudah berhasil login, namun akun kita tidak memiliki hak akses yang cukup untuk membuka halaman atau data tersebut (misal: user biasa mencoba membuka dashboard Admin).404 Not Found: Halaman atau data yang kita cari tidak ada di server (misal: produk dengan ID 99999 tidak ditemukan di database).422 Unprocessable Entity: Request kita dipahami, namun server menolak memprosesnya karena gagal lolos validasi bisnis di backend (misal: menginput format password yang terlalu mudah atau nomor HP yang sudah terdaftar).
4. Rumpun 5xx (Server Error) #
Menunjukkan kesalahan di sisi server backend, di mana server menyadari bahwa ia mengalami masalah internal atau tidak mampu memproses request yang sebenarnya valid.
500 Internal Server Error: Terjadi kesalahan fatal yang tidak terduga di dalam baris kode backend server (seperti database crash atau error null pointer di server).502 Bad Gateway: Server gerbang pembatas (seperti Nginx atau Caddy) gagal menerima respons balik yang valid dari server aplikasi backend utama di belakang layar.503 Service Unavailable: Server sedang mati sementara karena kelebihan beban kerja (overload) atau sedang dalam proses pemeliharaan sistem (maintenance).
Peran Headers dalam Jaringan #
Headers adalah metadata dalam bentuk pasangan kunci-nilai (key-value pair) yang disematkan di awal request maupun response. Headers digunakan untuk bertukar informasi administratif di luar konten data utama.
Berikut adalah beberapa header penting yang wajib kita ketahui dan konfigurasikan di aplikasi Flutter kita:
Headers pada Request (Klien ke Server) #
{
// Memberi tahu server bahwa body data yang kita kirimkan berformat JSON
'Content-Type': 'application/json; charset=UTF-8',
// Memberi tahu server bahwa kita mengharapkan respons dalam format JSON saja
'Accept': 'application/json',
// Menyematkan token akses pengguna untuk verifikasi keamanan (Bearer Auth)
'Authorization': 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...',
// Memberi tahu server bahasa preferensi pengguna untuk melokalisasi teks error
'Accept-Language': 'id-ID',
// Menyediakan ID unik untuk mempermudah tracking log request di server
'X-Request-ID': '9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d',
}
Headers pada Response (Server ke Klien) #
{
// Format data yang dikirimkan server kepada kita
'Content-Type': 'application/json; charset=utf-8',
// Ukuran panjang konten dalam satuan bytes
'Content-Length': '4329',
// Instruksi penyimpanan cache untuk mengoptimalkan performa data klien
'Cache-Control': 'public, max-age=3600',
// Batasan sisa kuota hit API yang diperbolehkan bagi IP kita (Rate Limiting)
'X-RateLimit-Remaining': '47',
}
JSON: Format Data untuk Komunikasi API #
JSON (JavaScript Object Notation) adalah format pertukaran data berbasis teks ringan yang telah menjadi standar universal dalam pengembangan REST API. JSON mudah dibaca oleh manusia dan sangat mudah diurai (parse) oleh berbagai bahasa pemrograman, termasuk Dart.
Struktur JSON terdiri atas dua elemen utama: objek (ditandai { }) yang merepresentasikan pasangan key-value, dan array (ditandai [ ]) yang merepresentasikan kumpulan data berurutan.
Berikut adalah contoh data JSON yang dikirimkan oleh server backend:
{
"id": 101,
"nama": "Kursi Kerja Ergonomis",
"harga": 1250000.0,
"stok": 15,
"kategori": ["furnitur", "kantor"],
"dimensi": {
"tinggi": 120.0,
"lebar": 60.0
}
}
Decoding & Encoding JSON di Dart #
Secara bawaan, pustaka standar Dart dart:convert menyediakan fungsi jsonDecode() untuk mengubah string JSON menjadi objek Map dinamis di Dart, dan jsonEncode() untuk mengubah Map Dart menjadi String JSON.
import 'dart:convert';
// Contoh Decoding (Mengubah String dari API menjadi Map Dart)
void prosesDataDariApi(String stringJsonDariServer) {
final Map<String, dynamic> dataMap = jsonDecode(stringJsonDariServer);
final int id = dataMap['id']; // 101
final String nama = dataMap['nama']; // 'Kursi Kerja Ergonomis'
final List kategoris = dataMap['kategori']; // ['furnitur', 'kantor']
final double tinggi = dataMap['dimensi']['tinggi']; // 120.0
print('Produk: $nama dengan id $id');
}
// Contoh Encoding (Mengubah data Map Dart menjadi String JSON untuk dikirim ke API)
String siapkanDataUntukDikirim() {
final Map<String, dynamic> bodyRequest = {
'nama': 'Meja Belajar Lipat',
'harga': 350000.0,
'stok': 5,
};
// Mengonversi Map menjadi String teks JSON
final String payloadString = jsonEncode(bodyRequest);
return payloadString;
}
Meskipun parsing manual menggunakan Map dinamis (Map<String, dynamic>) seperti di atas sangat mudah untuk aplikasi kecil, cara ini sangat rawan memicu bug typo penulisan key (misalnya menulis dataMap['name'] alih-alih dataMap['nama']). Oleh karena itu, untuk aplikasi skala produksi, kita wajib mengonversi Map tersebut menjadi objek model kelas (Data Model Class) yang aman tipe data (type-safe). Hal ini akan kita bahas tuntas pada artikel berikutnya tentang Serialisasi JSON.
Arsitektur Berlapis (Layered Architecture) Layer Jaringan #
Menuliskan kode pemanggilan API secara acak di dalam widget UI adalah resep instan untuk melahirkan kode yang berantakan (spaghetti code). Jaringan aplikasi kita harus diisolasi ke dalam beberapa lapisan arsitektur dengan tanggung jawab yang terdefinisi dengan sangat ketat.
Berikut adalah diagram arsitektur berlapis yang membagi alur komunikasi jaringan dari widget terluar hingga pustaka HTTP Client terdalam:
graph TD
classDef default stroke:#333,stroke-width:2px;
UI["Layer UI (Widget & Screen)"] -->|"Kirim aksi pengguna"| SM["Layer State Management (Notifier & Bloc)"]
SM -->|"Panggil metode bisnis"| Repo["Layer Repository (Abstraksi Data)"]
Repo -->|"Ambil data remote"| DS["Layer Data Source (Remote & Local)"]
DS -->|"Panggil API"| Client["HTTP Client (Dio & http)"]
Client -. "Kembalikan HTTP Response" .-> DS
DS -. "Kembalikan Model / DTO" .-> Repo
Repo -. "Pancarkan State Terstruktur" .-> SM
SM -. "Rebuild antarmuka otomatis" .-> UIMari kita urai peran penting dari masing-masing lapisan arsitektur di atas:
- Layer UI (Widget & Screen): Bagian terluar dari aplikasi kita. Tugasnya murni hanya untuk menampilkan data yang siap pakai dan mengirimkan aksi interaksi fisik pengguna (seperti klik tombol) ke layer state management.
- Layer State Management (Notifier / Bloc / Store): Pengendali logika tampilan. Lapisan ini memanggil metode di Repository, mengelola status pemuatan data (loading), dan memancarkan state yang terstruktur agar UI dapat digambar ulang secara presisi.
- Layer Repository (Abstraksi Data): Bertindak sebagai mediator tunggal (Single Entry Point) untuk urusan data. Repository mengabstraksi dari mana data tersebut berasal. State management tidak perlu tahu apakah data tersebut diambil dari internet (Remote Data Source) atau diambil dari penyimpanan database lokal (Local Data Source) saat internet mati.
- Layer Data Source (Remote & Local): Pelaksana teknis pengambilan data mentah.
RemoteDataSourcebertugas melakukan hit API jaringan menggunakan HTTP Client, mengurai data mentah, dan memetakan error HTTP.LocalDataSourcebertugas mengelola basis data lokal seperti SQLite atau Hive. - Layer HTTP Client (Dio / http): Lapisan terbawah yang bertugas melakukan pekerjaan fisik pengiriman paket byte data melalui protokol TCP/IP ke internet. Lapisan ini dikonfigurasi secara terpusat untuk mengurusi masalah timeout, interceptor token akses, pemadaman sertifikat SSL, dan pencatatan log (logging network).
Peta Ekosistem Jaringan (Networking Ecosystem) Flutter #
Ekosistem Dart dan Flutter memiliki banyak sekali pustaka pihak ketiga (packages) berkualitas tinggi yang dikembangkan oleh komunitas untuk menyederhanakan tugas-tugas jaringan kita.
Berikut adalah peta ekosistem pustaka jaringan yang dapat kita gunakan sesuai dengan kebutuhan proyek kita:
1. Pustaka HTTP Client (Pemroses Request) #
http(Official package dari tim Dart): Pustaka yang sangat ringan, sederhana, dan mudah digunakan. Sangat cocok untuk aplikasi berskala kecil atau paket pustaka Dart murni yang tidak ingin memiliki dependensi berat.dio: Pustaka HTTP client paling populer di dunia Flutter untuk skala produksi. Pustaka ini sangat bertenaga dan mendukung fitur-fitur tingkat lanjut seperti interceptor, pembatalan request (cancel token), pelacakan progres unggahan berkas (upload progress), pengulangan otomatis (auto retry), dan penguncian antrean request (lock/unlock).chopper: Pustaka HTTP client alternatif yang menggunakan konsep penulisan berbasis anotasi generator seperti pustaka Retrofit di Android Native.
2. Pustaka Serialisasi JSON (Pengolah Data) #
dart:convert: Pustaka bawaan Dart untuk konversi string dasar ke Map.json_serializable: Pustaka generator kode standar industri untuk otomatis menghasilkan fungsi generator dari-dan-ke JSON (fromJson&toJson) guna menghindari kesalahan ketik manual.freezed: Pustaka modern yang sangat populer untuk membuat kelas model data imutabel (immutable data classes) yang secara otomatis terintegrasi dengan fungsi serialisasi JSON.
3. Pustaka Penyimpanan Data Lokal & Autentikasi (Layer Cache & Security) #
flutter_secure_storage: Menyimpan data sensitif (seperti JWT Access Token atau Refresh Token) ke dalam area penyimpanan aman sistem operasi (Keychain di iOS dan Keystore di Android).hive/drift(SQLite): Digunakan di layerLocalDataSourceuntuk menyimpan data cache lokal dalam format NoSQL atau SQL relasional agar aplikasi tetap dapat diakses secara offline.connectivity_plus: Pustaka untuk mendeteksi perubahan status koneksi internet perangkat (apakah terhubung ke Wi-Fi, jaringan seluler, atau tidak ada koneksi).
REST API vs GraphQL #
Saat merancang arsitektur komunikasi jaringan aplikasi kita, kita biasanya akan dihadapkan pada dua pilihan arsitektur API utama: REST (Representational State Transfer) dan GraphQL. Masing-masing memiliki kelebihan dan kekurangan yang perlu disesuaikan dengan kebutuhan tim kita.
1. REST API #
REST API membagi fungsionalitas server ke dalam banyak endpoint URL yang berbeda berdasarkan entitas datanya (misal: GET /api/users, GET /api/products, POST /api/orders).
- Kelebihan: Sangat sederhana, familiar bagi sebagian besar pengembang, memiliki dukungan caching HTTP yang sangat matang di level CDN (Content Delivery Network), dan mudah diuji secara langsung melalui browser atau alat bantu seperti Postman.
- Kekurangan: Rawan terjadi masalah over-fetching (server mengembalikan terlalu banyak properti data yang sebenarnya tidak dibutuhkan oleh layar UI) dan under-fetching (klien terpaksa melakukan beberapa panggilan API ke endpoint berbeda untuk dapat merender satu halaman layar yang utuh).
2. GraphQL #
GraphQL memusatkan semua komunikasi melalui satu endpoint tunggal (biasanya POST /graphql). Klien mengirimkan dokumen permintaan berupa struktur data spesifik yang mendefinisikan secara tepat kolom-kolom apa saja yang ingin diambil dari server.
- Kelebihan: Sangat efisien karena sama sekali tidak ada masalah over-fetching atau under-fetching. Klien meminta data secara dinamis sesuai kebutuhan UI saat itu. Dilengkapi dengan sistem skema (strongly-typed schema) yang menjadi dokumentasi hidup bagi tim frontend dan backend.
- Kekurangan: Memiliki kurva belajar yang lebih curam, membutuhkan konfigurasi library yang cukup kompleks di Flutter (seperti
graphql_flutteratauferry), dan tidak dapat memanfaatkan caching HTTP standar secara langsung karena semua request menggunakan metodePOST.
Sebagai panduan umum, pilihlah REST API jika aplikasi kita memiliki kebutuhan standar, tim backend sudah terbiasa dengan REST, atau aplikasi kita membutuhkan performa caching CDN yang agresif. Pilihlah GraphQL jika aplikasi kita memiliki relasi data yang sangat kompleks, memiliki banyak jenis klien yang dinamis, atau membutuhkan sinkronisasi data real-time bawaan melalui subscription.
Ringkasan #
- Siklus Request-Response adalah inti dari komunikasi jaringan di mana aplikasi Flutter mengirimkan permintaan (request) berisi method, URL, headers, dan body, lalu server mengembalikan jawaban (response) berupa status code, headers, dan body.
- Karakteristik Metode HTTP harus dipahami secara tepat:
GETbersifat aman (safe), sedangkanPUTdanDELETEbersifat idempotent. Gunakan metode sesuai semantiknya.- Status Code HTTP adalah indikator utama hasil operasi: rumpun 2xx menandakan kesuksesan, 4xx menandakan kesalahan dari aplikasi kita, dan 5xx menandakan gangguan internal di server backend.
- Headers bertindak sebagai metadata administratif yang sangat penting untuk mengirimkan token keamanan (
Authorization) dan menegaskan format data (Content-Type).- JSON adalah standar teks ringan pertukaran data API. Gunakan pustaka
dart:convertuntuk parsing dasar, dan gunakan serialisasi bertipe data aman untuk skala produksi.- Arsitektur Berlapis (Layered Architecture) wajib diimplementasikan guna memisahkan tanggung jawab visual UI dari urusan pemanggilan fisik data jaringan demi kemudahan testing dan pemeliharaan kode.
- Dio adalah pustaka HTTP client pilihan utama untuk aplikasi skala produksi karena dilengkapi dengan fitur interceptor, penanganan timeout, dan auto retry yang bertenaga.
- REST vs GraphQL dipilih berdasarkan trade-off: REST unggul dalam kesederhanaan dan caching CDN, sementara GraphQL unggul dalam efisiensi pengambilan data yang fleksibel.
← Sebelumnya: State Management Best Practice Berikutnya: Dio & HTTP →