← Back to list

Menghubungkan Flutter ke HTTP & REST API

Pada pertemuan ini, kita akan belajar bagaimana aplikasi Flutter dapat berkomunikasi dengan server, mengambil data secara realtime dari…

Muhammad Dafa Falah Labib · 2026-05-26 03:34 · 0 claps · 9.6 min read
#flutter #api #https
Open on Medium ↗
Wiki topics: 📱 · Mobile Development

Menghubungkan Flutter

ke HTTP & REST API

Pada pertemuan ini, kita akan belajar bagaimana aplikasi Flutter dapat berkomunikasi dengan server, mengambil data secara realtime dari internet, melakukan parsing JSON, serta menangani proses loading dan error.

Gimana Aplikasi Bisa Ambil Data dari Internet?

Aplikasi seperti Instagram atau Google Maps bisa menampilkan data terbaru karena melakukan HTTP Request ke server lewat internet. Server lalu mengirim balik response berupa data, biasanya dalam format JSON. Di Flutter, proses ini dilakukan secara asynchronous menggunakan async dan await supaya UI tetap berjalan lancar saat menunggu data.

Beberapa konsep penting:

  • HTTP → komunikasi antara aplikasi dan server
  • REST API → aturan endpoint seperti /users atau /news
  • JSON → format data key-value dari server
  • Async/Await → menjalankan proses network tanpa nge-freeze UI

Method HTTP yang paling sering dipakai:

  • GET → ambil data
  • POST → tambah data
  • PUT → update data
  • DELETE → hapus data

Alur sederhananya: Flutter kirim request → server balas JSON + status code → Flutter parsing JSON → data ditampilkan ke UI menggunakan FutureBuilder.

Persiapan sebelum coding

Di pertemuan ini, kita sudah menyiapkan template project news, jadi kalian tidak perlu membuat project dari nol. Tugas kita nanti adalah menyambungkan aplikasi ke API dan menampilkan data agar berjalan dengan baik.

[embed]GitHub — Dafalahl/flutter-http-json-practice Contribute to Dafalahl/flutter-http-json-practice development by creating an account on GitHub.github.com

Langkah pertama, ekstrak terlebih dahulu file project Flutter yang sudah diunduh, lalu buka file pubspec.yaml.

Tambahkan dependency http pada bagian dependencies seperti berikut:

dependencies:
  flutter:
    sdk: flutter
  cupertino_icons: ^1.0.6
  http: ^1.2.0       # ← tambahkan ini

Package http digunakan untuk melakukan HTTP request ke server atau API. Setelah itu, jalankan perintah berikut di terminal untuk mengunduh package:

flutter pub get

Izinkan akses internet (Android)

Buka file berikut:

android/app/src/main/AndroidManifest.xml

Lalu tambahkan permission internet di dalam tag <manifest>:

<manifest xmlns:android="...">

  <!-- Tambahkan baris ini -->
  <uses-permission android:name="android.permission.INTERNET" />

  <application ...>
    ...
  </application>
</manifest>

Tanpa permission ini, aplikasi Android tidak bisa mengambil data dari internet.

Daftarkan diri dan dapat API Key

  1. Buka NewsAPI lalu klik tombol “Get API Key” di kanan atas.
  2. Isi form registrasi dengan First Name, Email address, dan Password. Pilih “I am an individual” dan gunakan email asli karena perlu verifikasi.
  3. Cek inbox email lalu klik link verifikasi dari NewsAPI sampai masuk ke dashboard.
  4. Salin API key yang muncul di dashboard. Bentuknya berupa string panjang seperti a1b2c3d4e5f6...
  5. Simpan API key tersebut untuk digunakan di kode Flutter.

Test Endpoint

Sekarang kita coba test endpoint langsung di browser. Ini cara paling cepat dan profesional untuk memastikan API benar-benar berfungsi.

Ketik URL berikut di search bar browser lalu ganti YOUR_KEY dengan API key milik kalian:

https://newsapi.org/v2/top-headlines?country=id&apiKey=YOUR_KEY

Endpoint seperti ini bisa kalian temukan di dokumentasi resmi NewsAPI Documentation

Kalau API berhasil terhubung, browser akan menampilkan response JSON seperti berikut:

{
  "status": "ok",
  "sources": [
    {
      "id": "abc-news",
      "name": "ABC News",
      "description": "Your trusted source for breaking news...",
      "url": "https://abcnews.go.com",
      "category": "general",
      "language": "en",
      "country": "us"
    },
    // ... sources/media lainnya
  ]
}

Jika muncul "status": "ok", berarti API key valid dan NewsAPI sudah siap digunakan di project Flutter kalian.

Konfigurasi API Key di Kode Flutter

Sekarang kita akan memasukkan API Key NewsAPI ke dalam project Flutter. Karena template project sudah disiapkan, kamu tidak perlu membuat file baru.

Buka file:

lib/news_services.dart

Di dalam class NewsService, cari variabel _apiKey lalu ganti dengan API key milikmu:

class NewsService {
  // Ganti dengan API Key dari dashboard NewsAPI
  static const String _apiKey = 'YOUR_API_KEY';

  static const String _baseUrl = 'https://newsapi.org/v2';

  // ... fungsi fetch
}

_baseUrl digunakan sebagai alamat dasar server NewsAPI. Pemisahan seperti ini membuat kode lebih rapi dan lebih mudah dikelola kalau suatu saat endpoint berubah.

Memahami Data Model (news_model.dart)

Data dari API awalnya masih berupa JSON mentah. Supaya lebih mudah digunakan di Flutter, data tersebut harus diubah menjadi object Dart. Proses ini disebut JSON Parsing.

Di file:

lib/news_model.dart

bagian terpentingnya adalah constructor fromJson():

NewsModel.fromJson(Map<String, dynamic> json) {
  id = json['id'];
  name = json['name'];
  description = json['description'];
  url = json['url'];
  category = json['category'];
  language = json['language'];
  country = json['country'];
}

Saat API mengirim response JSON, Flutter akan mengubahnya menjadi Map<String, dynamic> menggunakan jsonDecode().

fromJson() bertugas mengambil nilai dari setiap key JSON lalu memasukkannya ke dalam property object Dart secara otomatis.

Contohnya:

name = json['name'];

berarti data dari key "name" akan dimasukkan ke variabel name.

Setelah diparsing, data bisa diakses lebih nyaman menggunakan dot notation:

news.name

bukan:

news['name']

Proses Transformasi dan Filtering Data

Di file:

lib/news_services.dart

terdapat proses penting sebelum data dikirim ke UI:

return sourcesJson
    .map((json) => NewsModel.fromJson(json))
    .where((source) => source.name != '[Removed]')
    .toList();

Baris ini melakukan tiga proses sekaligus.

.map()

Mengubah setiap JSON mentah menjadi object NewsModel.

.map((json) => NewsModel.fromJson(json))

Tanpa proses ini, data masih berupa Map biasa.

.where()

Digunakan untuk filtering data.

.where((source) => source.name != '[Removed]')

Kadang NewsAPI masih mengirim source lama yang sudah dihapus sistem dengan nama [Removed].

Filter ini membuang data rusak agar tidak tampil di aplikasi.

.toList()

Mengubah hasil akhir menjadi List<NewsModel>.

.toList()

Karena hasil .map() dan .where() masih berupa iterable, bukan list final.

Menampilkan Data dengan FutureBuilder

Halaman utama aplikasi dikontrol oleh:

lib/news_list_screen.dart

Karena aplikasi mengambil data dari internet, kita menggunakan StatefulWidget.

Saat halaman pertama dibuka, initState() otomatis dipanggil:

@override
void initState() {
  super.initState();
  _loadNews();
}

Fungsi _loadNews() menjalankan request API:

void _loadNews() {
  setState(() {
    _newsFuture = _newsService.fetchSources();
  });
}

Kenapa Menggunakan FutureBuilder?

Karena request internet membutuhkan waktu.

FutureBuilder memantau status dari _newsFuture lalu otomatis memperbarui tampilan berdasarkan kondisi data.

FutureBuilder<List<NewsModel>>(
  future: _newsFuture,
  builder: (context, snapshot) {

    // Loading
    if (snapshot.connectionState == ConnectionState.waiting) {
      return const Center(
        child: CircularProgressIndicator(),
      );
    }

    // Error
    if (snapshot.hasError) {
      return const Center(
        child: Text('Gagal memuat berita'),
      );
    }

    // Empty data
    if (!snapshot.hasData || snapshot.data!.isEmpty) {
      return const Center(
        child: Text('Tidak ada berita tersedia.'),
      );
    }

    // Success
    final news = snapshot.data!;

    return ListView.builder(
      itemCount: news.length,
      itemBuilder: (context, index) {
        // Render item berita
      },
    );
  },
)

Empat Kondisi yang Ditangani FutureBuilder

Loading

Saat data masih diambil dari internet.

ConnectionState.waiting

UI menampilkan loading spinner.

Error

Saat request gagal.

snapshot.hasError

Misalnya:

  • internet mati
  • API key salah
  • server error

Empty

Saat request berhasil tapi data kosong.

snapshot.data!.isEmpty

Success

Saat data berhasil diterima dan siap ditampilkan.

final news = snapshot.data!;

Pull-to-Refresh dengan RefreshIndicator

Supaya user bisa refresh berita terbaru cukup dengan swipe ke bawah, kita bungkus ListView menggunakan RefreshIndicator.

RefreshIndicator(
  onRefresh: () async {
    _loadNews();
  },
  child: ListView.builder(
    itemCount: news.length,
    itemBuilder: (context, index) {
      // Render berita
    },
  ),
)

Saat user menarik layar ke bawah:

  1. _loadNews() dipanggil ulang.
  2. API request dijalankan lagi.
  3. Data terbaru langsung muncul di layar.

Inilah alasan kombinasi FutureBuilder dan RefreshIndicator sangat sering dipakai di Flutter untuk aplikasi berbasis API realtime.

Tantangan

  1. Buat aplikasi daftar film menggunakan data api dari https://www.themoviedb.org
  2. Aplikasi dapat menampilkan daftar film hingga detail film
  3. Minimal data yang ditampilkan yaitu nama, deskripsi dan gambar

Daftar & Ambil API Key di TMDB

Buka TMDB Official Website lalu daftar atau login menggunakan akun kalian.

Setelah berhasil login:

  • masuk ke menu Settings
  • pilih API
  • klik Create API Key

Isi data yang diminta lalu submit. Karena cuma untuk belajar, isi sederhana aja sudah cukup.

Contoh aman:

  • Application NameFlutter Movie App
  • Application URL[https://example.com](https://example.com)
  • Type of UseMobile Application
  • Application SummaryLearning Flutter and REST API integration using TMDB API.

Setelah selesai, TMDB akan menampilkan API Key yang nanti digunakan di project Flutter untuk mengambil data film.

Tambahkan Package http

Buat project Flutter baru lalu buka file pubspec.yaml. Tambahkan dependency http pada bagian dependencies:

dependencies:
  flutter:
    sdk: flutter
  http: ^1.2.1   # tambahkan baris ini

Setelah itu, jalankan perintah berikut di terminal:

flutter pub get

Susunan File yang Akan Dibuat

lib/
main.dart  ← entry point aplikasi
movie_model.dart  ← struktur data film
movie_service.dart  ← logika pemanggilan API
movie_list_screen.dart  ← halaman daftar film
movie_detail_screen.dart  ← halaman detail film

Membuat MovieModel

Sebelum mengambil data dari API, kita perlu membuat model untuk menampung data film dari TMDB.

Endpoint /movie/popular dari TMDB mengembalikan data JSON seperti ini:

{
  "results": [
    {
      "id": 1022789,
      "title": "Inside Out 2",
      "overview": "Teenager Riley's mind headquarters...",
      "poster_path": "/vpnVM9B6NMmQpWeZvzLvDESb2QY.jpg",
      "vote_average": 7.6,
      "release_date": "2024-06-11"
    }
  ]
}

Buat file baru:

lib/movie_model.dart

Lalu buat class berikut:

class MovieModel {
  final int? id;
  final String? title;
  final String? overview;
  final String? posterPath;
  final double? voteAverage;
  final String? releaseDate;

  MovieModel({
    this.id,
    this.title,
    this.overview,
    this.posterPath,
    this.voteAverage,
    this.releaseDate,
  });

  factory MovieModel.fromJson(Map<String, dynamic> json) {
    return MovieModel(
      id: json['id'],
      title: json['title'],
      overview: json['overview'],
      posterPath: json['poster_path'],
      voteAverage: (json['vote_average'] as num?)?.toDouble(),
      releaseDate: json['release_date'],
    );
  }

  // URL lengkap poster film
  String get posterUrl =>
      'https://image.tmdb.org/t/p/w500$posterPath';
}

TMDB tidak mengirim URL gambar secara penuh. Field poster_path hanya berisi path seperti:

/abc.jpg

Karena itu kita perlu menggabungkannya dengan:

https://image.tmdb.org/t/p/w500

Getter posterUrl digunakan supaya URL gambar lengkap bisa dibuat otomatis saat digunakan di Flutter.

Membuat MovieService

File ini bertugas mengambil data film dari API TMDB lalu mengubah response JSON menjadi List<MovieModel>.

Buat file baru:

lib/movie_service.dart

isi dengan kode berikut:

import 'dart:convert';
import 'package:http/http.dart' as http;
import 'movie_model.dart';

class MovieService {
  static const String _apiKey =
      'GANTI_DENGAN_API_KEY_KAMU';

  static const String _baseUrl =
      'https://api.themoviedb.org/3';

  // Ambil daftar film populer
  Future<List<MovieModel>> fetchPopularMovies() async {
    final uri = Uri.parse(
      '$_baseUrl/movie/popular?api_key=$_apiKey&language=id-ID',
    );

    final response = await http.get(uri).timeout(
      const Duration(seconds: 10),
      onTimeout: () => throw Exception('Request timeout.'),
    );

    if (response.statusCode == 200) {
      final Map<String, dynamic> data =
          jsonDecode(response.body);

      final List<dynamic> results = data['results'];

      return results
          .map((json) => MovieModel.fromJson(json))
          .toList();
    } else if (response.statusCode == 401) {
      throw Exception('API Key tidak valid.');
    } else {
      throw Exception(
        'Gagal memuat data. Status: ${response.statusCode}',
      );
    }
  }
}

Jangan lupa ganti:

  static const String _apiKey =
      'GANTI_DENGAN_API_KEY_KAMU';

Fungsi fetchPopularMovies() bekerja dalam beberapa tahap:

  • membuat URL endpoint TMDB
  • mengirim HTTP GET request
  • mengubah JSON menjadi MovieModel
  • menangani error jika request gagal

Membuat Halaman Daftar Film

Halaman ini akan menjadi halaman utama aplikasi. Data film diambil dari API menggunakan FutureBuilder, lalu ditampilkan dalam bentuk list berisi poster, judul, dan rating film.

Buat file baru: lib/movie_list_screen.dart

isinya

import 'package:flutter/material.dart';
import 'movie_model.dart';
import 'movie_service.dart';
import 'movie_detail_screen.dart';

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

  @override
  State<MovieListScreen> createState() =>
      _MovieListScreenState();
}

class _MovieListScreenState
    extends State<MovieListScreen> {

  final MovieService _service = MovieService();

  late Future<List<MovieModel>> _movieFuture;

  @override
  void initState() {
    super.initState();

    _movieFuture =
        _service.fetchPopularMovies();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text(
          '🎬 Film Populer',
          style: TextStyle(
            fontWeight: FontWeight.bold,
          ),
        ),
      ),
      body: FutureBuilder<List<MovieModel>>(
        future: _movieFuture,
        builder: (context, snapshot) {

          // Loading
          if (snapshot.connectionState ==
              ConnectionState.waiting) {
            return const Center(
              child: CircularProgressIndicator(),
            );
          }

          // Error
          if (snapshot.hasError) {
            return Center(
              child: Text(
                'Error: ${snapshot.error}',
              ),
            );
          }

          // Data kosong
          if (!snapshot.hasData ||
              snapshot.data!.isEmpty) {
            return const Center(
              child: Text('Tidak ada film.'),
            );
          }

          // Tampilkan list film
          final movies = snapshot.data!;

          return ListView.builder(
            itemCount: movies.length,
            itemBuilder: (context, index) {

              final movie = movies[index];

              return ListTile(
                contentPadding:
                    const EdgeInsets.all(12),

                leading: ClipRRect(
                  borderRadius:
                      BorderRadius.circular(8),

                  child: Image.network(
                    movie.posterUrl,
                    width: 50,
                    height: 75,
                    fit: BoxFit.cover,

                    errorBuilder: (_, __, ___) =>
                        const Icon(
                          Icons.movie,
                          size: 50,
                        ),
                  ),
                ),

                title: Text(
                  movie.title ??
                      'Tanpa Judul',

                  style: const TextStyle(
                    fontWeight:
                        FontWeight.bold,
                  ),
                ),

                subtitle: Text(
                  '⭐ ${movie.voteAverage?.toStringAsFixed(1)}',
                ),

                onTap: () {
                  Navigator.push(
                    context,
                    MaterialPageRoute(
                      builder: (_) =>
                          MovieDetailScreen(
                            movie: movie,
                          ),
                    ),
                  );
                },
              );
            },
          );
        },
      ),
    );
  }
}

FutureBuilder digunakan untuk menangani beberapa kondisi otomatis:

  • loading saat request berjalan
  • error saat request gagal
  • data kosong
  • data berhasil dimuat

Membuat Halaman Detail Film

Halaman ini digunakan untuk menampilkan detail lengkap film yang dipilih dari halaman list.

Buat file baru: lib/movie_detail_screen.dart

isinya:

import 'package:flutter/material.dart';
import 'movie_model.dart';

class MovieDetailScreen extends StatelessWidget {
  final MovieModel movie;

  const MovieDetailScreen({
    super.key,
    required this.movie,
  });

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(
          movie.title ?? 'Detail Film',
        ),
      ),

      body: SingleChildScrollView(
        child: Column(
          crossAxisAlignment:
              CrossAxisAlignment.start,

          children: [

            // Poster film
            SizedBox(
              width: double.infinity,
              height: 300,

              child: Image.network(
                movie.posterUrl,
                fit: BoxFit.cover,

                errorBuilder: (_, __, ___) =>
                    const Icon(
                      Icons.movie,
                      size: 80,
                    ),
              ),
            ),

            // Informasi film
            Padding(
              padding:
                  const EdgeInsets.all(16),

              child: Column(
                crossAxisAlignment:
                    CrossAxisAlignment.start,

                children: [

                  // Judul film
                  Text(
                    movie.title ?? '-',

                    style: const TextStyle(
                      fontSize: 22,
                      fontWeight:
                          FontWeight.bold,
                    ),
                  ),

                  const SizedBox(height: 8),

                  // Rating & tanggal rilis
                  Row(
                    children: [

                      const Icon(
                        Icons.star,
                        color: Colors.amber,
                        size: 18,
                      ),

                      const SizedBox(width: 4),

                      Text(
                        movie.voteAverage
                                ?.toStringAsFixed(1) ??
                            '-',
                      ),

                      const SizedBox(width: 16),

                      const Icon(
                        Icons.calendar_today,
                        size: 16,
                      ),

                      const SizedBox(width: 4),

                      Text(
                        movie.releaseDate ?? '-',
                      ),
                    ],
                  ),

                  const Divider(height: 32),

                  // Label sinopsis
                  const Text(
                    'Sinopsis',

                    style: TextStyle(
                      fontSize: 16,
                      fontWeight:
                          FontWeight.bold,
                    ),
                  ),

                  const SizedBox(height: 8),

                  // Deskripsi film
                  Text(
                    movie.overview
                                ?.isNotEmpty ==
                            true
                        ? movie.overview!
                        : 'Tidak ada sinopsis tersedia.',

                    style: const TextStyle(
                      fontSize: 15,
                      height: 1.6,
                    ),
                  ),
                ],
              ),
            ),
          ],
        ),
      ),
    );
  }
}

Saat user menekan salah satu film di halaman list, objek MovieModel langsung dikirim ke MovieDetailScreen melalui konstruktor:

MovieDetailScreen(movie: movie)

Karena data sudah dikirim dari halaman sebelumnya, halaman detail tidak perlu melakukan request API lagi.

Flutter juga otomatis menambahkan tombol back pada AppBar, sehingga user bisa kembali ke halaman daftar film menggunakan Navigator.pop().

Membuat main.dart

File ini adalah entry point aplikasi Flutter. Tugasnya hanya menjalankan MaterialApp dan menampilkan MovieListScreen sebagai halaman utama.

Buka file lib/main.dart lalu ganti semua isinya menjadi:

import 'package:flutter/material.dart';
import 'movie_list_screen.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Movie App',

      debugShowCheckedModeBanner: false,

      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(
          seedColor: Colors.indigo,
        ),
        useMaterial3: true,
      ),

      home: const MovieListScreen(),
    );
  }
}

Alur Keseluruhan Aplikasi

Saat aplikasi dibuka:

  • main() menjalankan MyApp
  • MovieListScreen tampil sebagai halaman utama
  • initState() memanggil fetchPopularMovies()
  • request API dikirim ke server TMDB

Selama request berjalan:

  • FutureBuilder menampilkan loading spinner

Ketika data berhasil diterima:

  • JSON diparsing menjadi List<MovieModel>
  • ListView.builder menampilkan daftar film

Saat user menekan salah satu film:

  • Navigator.push() membuka MovieDetailScreen
  • objek MovieModel dikirim ke halaman detail

Saat tombol back ditekan:

  • Flutter kembali ke halaman list sebelumnya
  • data tetap tersimpan di memori tanpa request ulang ke API

Gambar Aplikasi

Kesimpulan

Pada pertemuan ini, kita telah belajar mengambil data dari REST API menggunakan Flutter, melakukan parsing JSON ke object Dart, menampilkan data dengan FutureBuilder, serta membuat navigasi antar halaman menggunakan data dari API. Dengan konsep ini, kalian sudah bisa mulai membuat aplikasi Flutter berbasis data realtime dari internet.


메타데이터
post_id
1d9e8bbfb1b7
slug
menghubungkan-flutter-ke-http-rest-api-1d9e8bbfb1b7
url
https://medium.com/@dafafalah1616/menghubungkan-flutter-ke-http-rest-api-1d9e8bbfb1b7
canonical_url
https://medium.com/@dafafalah1616/menghubungkan-flutter-ke-http-rest-api-1d9e8bbfb1b7
author_url
https://medium.com/@dafafalah1616
status
ok
fetched_at
2026-06-09 14:34:10