image

01 Dec 2025

9K

35K

Flutter & REST API dengan Dio dan Interceptors

Pengembangan aplikasi modern seringkali melibatkan interaksi dengan layanan backend melalui RESTful API. Flutter, sebagai framework UI yang populer, memerlukan pustaka HTTP yang efisien dan fleksibel untuk menangani komunikasi ini. Dio menonjol sebagai pilihan utama di ekosistem Flutter, menawarkan fitur-fitur canggih seperti interceptor yang sangat berguna untuk mengelola permintaan dan respons.

Pendahuluan: Integrasi REST API di Flutter

RESTful API adalah arsitektur standar untuk membangun layanan web, memungkinkan komunikasi antara klien (aplikasi Flutter) dan server menggunakan metode HTTP standar (GET, POST, PUT, DELETE). Mengelola permintaan, respons, otentikasi, dan penanganan kesalahan secara konsisten bisa menjadi tantangan. Di sinilah pustaka HTTP yang kuat seperti Dio, bersama dengan kemampuan interceptor-nya, memainkan peran krusial.

Mengapa Memilih Dio?

Dio adalah HTTP client yang kuat untuk Dart, mendukung RequestOptions, Interceptors, FormData, Request Cancellation, File downloading, Timeout, dan banyak lagi. Beberapa alasan utama memilih Dio:

  • Interceptors: Memungkinkan Anda untuk mencegat dan memodifikasi permintaan atau respons sebelum diproses atau dikirim.
  • API Fleksibel: Mudah digunakan untuk berbagai jenis permintaan HTTP.
  • Penanganan Kesalahan Komprehensif: Memudahkan penanganan berbagai jenis kesalahan jaringan dan server.
  • Pembatalan Permintaan: Kemampuan untuk membatalkan permintaan yang sedang berlangsung.
  • Dukungan FormData: Unggah file dengan mudah.

Persiapan: Menambahkan Dio ke Proyek Flutter

Pertama, tambahkan Dio sebagai dependensi di file pubspec.yaml Anda:


dependencies:
  flutter:
    sdk: flutter
  dio: ^5.x.x # Gunakan versi terbaru yang stabil

Kemudian jalankan flutter pub get di terminal Anda.

Dasar Penggunaan Dio

Berikut adalah contoh dasar inisialisasi dan penggunaan Dio untuk melakukan permintaan GET dan POST.

Inisialisasi Dio


import 'package:dio/dio.dart';

final dio = Dio();

// Atau dengan BaseOptions
final dioWithBaseOptions = Dio(
  BaseOptions(
    baseUrl: 'https://api.example.com',
    connectTimeout: const Duration(seconds: 5),
    receiveTimeout: const Duration(seconds: 3),
    headers: {
      'Content-Type': 'application/json',
      'Accept': 'application/json',
    },
  ),
);

Permintaan GET


Future fetchData() async {
  try {
    final response = await dioWithBaseOptions.get('/users');
    if (response.statusCode == 200) {
      print('Data: ${response.data}');
    } else {
      print('Gagal mengambil data: ${response.statusCode}');
    }
  } on DioException catch (e) {
    print('Error Dio: ${e.message}');
    if (e.response != null) {
      print('Response Error: ${e.response?.data}');
    }
  } catch (e) {
    print('Error umum: $e');
  }
}

Permintaan POST


Future postData(Map data) async {
  try {
    final response = await dioWithBaseOptions.post('/users', data: data);
    if (response.statusCode == 201) {
      print('Data berhasil dikirim: ${response.data}');
    } else {
      print('Gagal mengirim data: ${response.statusCode}');
    }
  } on DioException catch (e) {
    print('Error Dio: ${e.message}');
    if (e.response != null) {
      print('Response Error: ${e.response?.data}');
    }
  } catch (e) {
    print('Error umum: $e');
  }
}

Memahami Interceptors

Interceptor adalah mekanisme yang memungkinkan Anda untuk menyadap (intercept) dan memodifikasi permintaan atau respons HTTP sebelum mereka mencapai tujuan akhir atau sebelum dikembalikan ke pemanggil. Ini sangat powerful untuk tugas-tugas cross-cutting seperti:

  • Logging: Mencatat detail permintaan dan respons untuk debugging.
  • Otentikasi: Menambahkan token otentikasi ke header permintaan secara otomatis.
  • Penanganan Kesalahan Global: Mengelola error secara terpusat, seperti menampilkan pesan error generik atau me-refresh token.
  • Cache: Mengimplementasikan strategi caching.
  • Retry Mechanism: Otomatis mencoba ulang permintaan yang gagal.

Menerapkan Interceptors Kustom

Anda dapat menambahkan interceptor ke instans Dio menggunakan metode interceptors.add(). Ada tiga jenis interceptor utama:

  • RequestInterceptor
  • ResponseInterceptor
  • ErrorInterceptor

Atau Anda bisa menggunakan kelas Interceptor yang mengimplementasikan ketiganya.

Contoh 1: Logging Interceptor

Interceptor ini mencatat detail permintaan dan respons ke konsol.


class LoggingInterceptor extends Interceptor {
  @override
  void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
    print('REQUEST[${options.method}] => PATH: ${options.path}');
    print('HEADERS: ${options.headers}');
    print('DATA: ${options.data}');
    super.onRequest(options, handler);
  }

  @override
  void onResponse(Response response, ResponseInterceptorHandler handler) {
    print('RESPONSE[${response.statusCode}] => PATH: ${response.requestOptions.path}');
    print('DATA: ${response.data}');
    super.onResponse(response, handler);
  }

  @override
  void onError(DioException err, ErrorInterceptorHandler handler) {
    print('ERROR[${err.response?.statusCode}] => PATH: ${err.requestOptions.path}');
    print('MESSAGE: ${err.message}');
    if (err.response != null) {
      print('RESPONSE DATA: ${err.response?.data}');
    }
    super.onError(err, handler);
  }
}

// Menambahkan interceptor ke Dio
final dioWithInterceptors = Dio(
  BaseOptions(
    baseUrl: 'https://api.example.com',
    connectTimeout: const Duration(seconds: 5),
    receiveTimeout: const Duration(seconds: 3),
  ),
);
dioWithInterceptors.interceptors.add(LoggingInterceptor());

Contoh 2: Authentication Interceptor

Interceptor ini menambahkan token otentikasi ke setiap permintaan.


class AuthInterceptor extends Interceptor {
  final String? _authToken;

  AuthInterceptor(this._authToken);

  @override
  void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
    if (_authToken != null) {
      options.headers['Authorization'] = 'Bearer $_authToken';
    }
    super.onRequest(options, handler);
  }
}

// Contoh penggunaan dengan token dummy
String? userToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...'; // Ambil dari SharedPreferences atau provider
dioWithInterceptors.interceptors.add(AuthInterceptor(userToken));

Contoh 3: Refresh Token Interceptor (Konseptual)

Untuk skenario token otentikasi yang kedaluwarsa, Anda bisa membuat interceptor yang mendeteksi status 401, mencoba me-refresh token, lalu mengulang permintaan asli.


class RefreshTokenInterceptor extends Interceptor {
  final Dio _dio;
  final Function() _refreshTokenCallback; // Callback untuk memanggil logika refresh token

  RefreshTokenInterceptor(this._dio, this._refreshTokenCallback);

  @override
  void onError(DioException err, ErrorInterceptorHandler handler) async {
    if (err.response?.statusCode == 401) {
      // Coba refresh token
      try {
        await _refreshTokenCallback(); // Memanggil fungsi refresh token
        // Setelah token di-refresh, coba lagi permintaan asli dengan token baru
        final RequestOptions requestOptions = err.requestOptions;
        // Asumsi token baru sudah disimpan dan AuthInterceptor akan mengambilnya
        final Response response = await _dio.fetch(requestOptions);
        return handler.resolve(response);
      } catch (e) {
        // Gagal refresh token, lanjutkan error asli
        super.onError(err, handler);
      }
    } else {
      super.onError(err, handler);
    }
  }
}
// Integrasi dengan Dio
// final dio = Dio(...);
// Function() refreshTokenFunction = () async { /* logic untuk refresh token */ };
// dio.interceptors.add(AuthInterceptor(sharedPreferences.getString('accessToken'))); // Harus bisa diupdate
// dio.interceptors.add(RefreshTokenInterceptor(dio, refreshTokenFunction));

Struktur Proyek untuk API Service

Untuk menjaga kode tetap bersih dan mudah dikelola, disarankan untuk mengabstraksi logika API ke dalam kelas-kelas service terpisah.


// lib/services/api_service.dart
import 'package:dio/dio.dart';
import '../interceptors/logging_interceptor.dart';
import '../interceptors/auth_interceptor.dart';
// ... import interceptor lainnya

class ApiService {
  final Dio _dio;

  ApiService() : _dio = Dio(
    BaseOptions(
      baseUrl: 'https://api.example.com',
      connectTimeout: const Duration(seconds: 5),
      receiveTimeout: const Duration(seconds: 3),
      headers: {
        'Content-Type': 'application/json',
        'Accept': 'application/json',
      },
    ),
  ) {
    _dio.interceptors.add(LoggingInterceptor());
    // _dio.interceptors.add(AuthInterceptor(null)); // Token bisa diupdate kemudian
    // _dio.interceptors.add(RefreshTokenInterceptor(_dio, () => _refreshToken()));
  }

  // Metode untuk memperbarui token otentikasi secara dinamis
  void updateAuthToken(String? token) {
    // Cari AuthInterceptor dan perbarui tokennya, atau buat ulang Dio jika perlu
    // Ini mungkin memerlukan pendekatan yang lebih canggih, misalnya menggunakan Provider/Bloc
    // Atau memastikan AuthInterceptor mengambil token dari sumber yang dapat diupdate (misal SharedPrefs)
  }

  Future getUsers() async {
    return await _dio.get('/users');
  }

  Future createUser(Map userData) async {
    return await _dio.post('/users', data: userData);
  }

  // Future _refreshToken() async {
  //   // Logika untuk memanggil API refresh token
  //   // Simpan token baru dan perbarui AuthInterceptor
  // }
}

// Penggunaan di UI atau Business Logic
// final apiService = ApiService();
// try {
//   final response = await apiService.getUsers();
//   print(response.data);
// } catch (e) {
//   print(e);
// }

Kesimpulan

Dio adalah pilihan yang sangat baik untuk mengelola permintaan REST API di aplikasi Flutter berkat fitur-fitur canggihnya, terutama Interceptors. Dengan Interceptors, Anda dapat dengan mudah mengimplementasikan logika global untuk logging, otentikasi, penanganan kesalahan, dan banyak lagi, menghasilkan kode yang lebih bersih, mudah dipelihara, dan skalabel. Menggabungkan Dio dengan struktur proyek yang terorganisir akan meningkatkan efisiensi pengembangan secara signifikan.

Related Articles

Dec 19, 2025

Flutter & Firebase Auth: Seamless Social Media Login

Flutter & Firebase Auth: Seamless Social Media Login In today's digital landscape, user authentication is a critical component of almost every application. Pro

Dec 19, 2025

Building a Widget List with Sticky

Building a Widget List with Sticky Header in Flutter Creating dynamic and engaging user interfaces is crucial for modern applications. One common UI pattern th

Dec 19, 2025

Mastering Transform Scale & Rotate Animations in Flutter

Mastering Transform Scale & Rotate Animations in Flutter Flutter's powerful animation framework allows developers to create visually stunning and highly intera