api_client_dio

A simple, opinionated Dio wrapper for Flutter. Handles base URL, token injection, 401 refresh/redirect, logging, and timeouts out of the box.

Framework-agnostic — no GetX, no Riverpod, no dependency other than Dio.

Features

  • Zero boilerplate — Create a client with base URL and start making requests
  • Automatic Bearer token — Implement TokenProvider once, tokens are injected automatically
  • 401 → refresh → retry — Automatic token refresh when access token expires
  • Auto-redirect to login — Calls onUnauthorized() when refresh fails
  • Request/response logging — Toggleable via enableLogging: true
  • Configurable timeouts — connect, receive, and send timeouts
  • Full Dio support — GET, POST, PUT, PATCH, DELETE with all Dio options
  • Runtime base URL — Change API endpoint on the fly via updateBaseUrl()

Getting started

dependencies:
  api_client_dio: ^0.1.0
flutter pub get

Usage

1. Implement TokenProvider

Connect your token storage (SharedPreferences, flutter_secure_storage, etc.):

import 'package:api_client_dio/api_client_dio.dart';

class StorageTokenProvider implements TokenProvider {
  final _storage = FlutterSecureStorage();

  @override
  Future<String?> get accessToken =>
      _storage.read(key: 'access_token');

  @override
  Future<String?> get refreshToken =>
      _storage.read(key: 'refresh_token');

  @override
  Future<void> onUnauthorized() async {
    await _storage.clearAll();
    // Navigate to login page
  }

  @override
  Future<void> onTokenRefreshed({
    required String accessToken,
    String? refreshToken,
  }) async {
    await _storage.write(key: 'access_token', value: accessToken);
    if (refreshToken != null) {
      await _storage.write(key: 'refresh_token', value: refreshToken);
    }
  }
}

2. Create the client

final client = ApiClient(
  baseUrl: 'https://api.example.com',
  tokenProvider: StorageTokenProvider(),
  enableLogging: true,          // Print requests/responses
  refreshEndpoint: '/auth/refresh',  // Enable 401 → refresh → retry
);

3. Use it

// GET
final posts = await client.get('/posts');
final user = await client.get('/users/1');

// POST
final login = await client.post('/auth/login', data: {
  'email': 'user@example.com',
  'password': 'secret123',
});

// PUT
await client.put('/users/1', data: {'name': 'New Name'});

// DELETE
await client.delete('/posts/1');

// Change base URL at runtime (e.g. after login)
client.updateBaseUrl('https://api.newdomain.com');

API

ApiClient

Constructor param Type Default Description
baseUrl String (required) Base URL for all requests
tokenProvider TokenProvider? null Auth token handler
connectTimeout Duration 10s Connection timeout
receiveTimeout Duration 30s Response timeout
sendTimeout Duration 10s Request send timeout
enableLogging bool false Print request/response logs
headers Map<String, dynamic>? null Additional default headers
refreshEndpoint String? null Token refresh URL path
refreshDio Dio? null Custom Dio for refresh calls
interceptors List<Interceptor>? null Additional Dio interceptors

TokenProvider

Method Returns Description
accessToken Future<String?> Current Bearer token
refreshToken Future<String?> Current refresh token
onUnauthorized() Future<void> Called on 401 (clear & redirect)
onTokenRefreshed(accessToken, refreshToken) Future<void> Called after successful refresh

License

MIT

Libraries

api_client_dio
A simple, opinionated Dio wrapper for Flutter.