Advanced API Client

A powerful, production-ready Flutter/Dart HTTP client built on top of dio.

advanced_api_client simplifies REST API integration with built-in:

  • Authentication handling
  • Automatic token refresh
  • Retry mechanism
  • File uploads
  • Pagination support
  • Global error handling
  • Custom interceptors
  • Session termination
  • Clean architecture

Designed for scalability and real-world production apps.


โœจ Features

  • โœ… GET, POST, PUT, PATCH, DELETE
  • ๐Ÿ” Token-based authentication
  • ๐Ÿ”„ Automatic token refresh (401 handling)
  • โ™ป๏ธ Retry interceptor (connection errors + timeout support)
  • ๐Ÿ“ค Single & multiple file uploads
  • ๐Ÿ“ฆ Pagination-ready
  • ๐Ÿงฉ Custom interceptor support
  • ๐Ÿ›‘ Global error callback
  • ๐Ÿšช Session termination support
  • ๐Ÿงช Works with Flutter & pure Dart
  • ๐Ÿ†• Dynamic refresh token bodyBuilder support for passing runtime data (e.g., user_id from SharedPreferences)
  • ๐Ÿ›ก Safe retry after token refresh for file uploads

๐Ÿ“ฆ Installation

Add this to your pubspec.yaml:

dependencies:
  advanced_api_client: ^1.0.1

Then run:

flutter pub get

๐Ÿš€ Getting Started

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  await AdvancedApiClient.initialize(
    config: ApiConfig(
      baseUrl: "https://api.example.com",
      refreshConfig: RefreshConfig(
        path: "/auth/refresh",
        method: "POST",
        // Use bodyBuilder for dynamic runtime body
        bodyBuilder: () async {
          final prefs = await SharedPreferences.getInstance();
          final userId = prefs.getString("user_id") ?? "";
          return {"user_id": userId, "from_source": 1};
        },
        // or use body for static data
        body: {
          "from_source": 1
        },
        tokenParser: (data) => data["access_token"],
        headers: {
          "App-Version": "1.0.0",
          "Platform": "Flutter",
        },
      ),
    ),
  );

  runApp(MyApp());
}

Access anywhere:

final client = AdvancedApiClient.instance;

2๏ธโƒฃ Lazy Initialization (Optional)

final client = await AdvancedApiClient.getInstance();

โš™๏ธ ApiConfig

The ApiConfig class allows you to configure the API client behavior.

class ApiConfig {
  final String baseUrl;
  final RefreshConfig? refreshConfig;
  final List<Interceptor>? interceptors;
  final void Function(DioException e, RequestOptions request)? onError;
  final bool enableLogs;

  const ApiConfig({
    required this.baseUrl,
    this.refreshConfig,
    this.interceptors,
    this.onError,
    this.enableLogs = true,
  });
}

๐Ÿ”น baseUrl (Required)

Base URL of your API.

ApiConfig(
  baseUrl: "https://api.example.com",
);

๐Ÿ”น refreshConfig (Optional)

Used to automatically refresh access tokens when a 401 response is received.

Example:

ApiConfig(
  baseUrl: "https://api.example.com",
  refreshConfig: RefreshConfig(
    path: "/auth/refresh",
    method: "POST",
    // Dynamic runtime body
    bodyBuilder: () async {
    final prefs = await SharedPreferences.getInstance();
    final userId = prefs.getString("user_id") ?? "";
    return {"user_id": userId, "from_source": 1};
    },
    // Static body
    body: {"from_source": 1}
    tokenParser: (data) => data["access_token"],
  ),
);

๐Ÿ”น interceptors (Optional)

Add custom dio interceptors.

ApiConfig(
  baseUrl: "https://api.example.com",
  interceptors: [
    LogInterceptor(responseBody: true),
  ],
);

๐Ÿ”น onError (Optional)

Global error callback triggered for all request errors.

ApiConfig(
  baseUrl: "https://api.example.com",
  onError: (DioException e, RequestOptions request) async {
    final statusCode = e.response?.statusCode;

    if (statusCode == 401) {
      await AdvancedApiClient.instance.terminateSession();
    }

    if (statusCode == 500) {
      debugPrint("Server error occurred");
    }

    if (e.type == DioExceptionType.connectionError) {
      debugPrint("No internet connection");
    }
  },
);

๐ŸŒ Basic Requests

GET

await client.get(
  endpoint: "/users",
  headers: {"X-Custom-Header": "12345"},
);

POST

await client.post(
  endpoint: "/users",
  body: {"name": "John"},
  headers: {"X-Custom-Header": "12345"},
);

PUT

await client.put(
  endpoint: "/users/1",
  body: {"name": "Updated"},
  headers: {"X-Custom-Header": "12345"},
);

PATCH

await client.patch(
  endpoint: "/users/1",
  body: {"status": "active"},
  headers: {"X-Custom-Header": "12345"},
);

DELETE

await client.delete(endpoint: "/users/1");

๐Ÿ“ค File Upload Examples

final client = AdvancedApiClient.instance;

Upload Single File

await client.upload(
  endpoint: "/common/file-upload/image",
  files: {
    "image": [pickedFile.path],
  },
  headers: {"X-Custom-Header": "12345"},
);

Upload Multiple Files (Same Field)

await client.upload(
  endpoint: "/common/file-upload/image",
  files: {
    "image": filePaths, // List<String>
  },
  headers: {"X-Custom-Header": "12345"},
);

Upload Multiple Files (Different Fields)

await client.upload(
  endpoint: "/upload",
  files: {
    "profile_image": [profilePath],
    "documents": documentPaths,
  },
  fields: {
    "user_id": 1,
    "type": "verification",
  },
  headers: {"X-Custom-Header": "12345"},
);

๐Ÿšช Terminate Session

Clears tokens and cancels all pending requests:

await AdvancedApiClient.instance.terminateSession();

๐Ÿ”„ Retry Mechanism

Built-in retry interceptor supports:

  • Connection errors
  • Receive timeout
  • Configurable retry attempts
  • Optional exponential backoff

Helps reduce random network failures in production.


๐Ÿ— Architecture

AdvancedApiClient
 โ”œโ”€โ”€ AuthInterceptor
 โ”œโ”€โ”€ RetryInterceptor
 โ”œโ”€โ”€ TokenStorage
 โ”œโ”€โ”€ ApiConfig
 โ”œโ”€โ”€ RefreshConfig (supports bodyBuilder)
 โ””โ”€โ”€ ApiException

Clean separation of concerns and extensibility.


๐Ÿงช Works With

  • Flutter Mobile
  • Flutter Web
  • Flutter Desktop
  • Pure Dart projects

๐Ÿ“ License

MIT License ยฉ 2026


โค๏ธ Why Advanced API Client?

Because production apps need more than just dio.

You get:

  • Cleaner codebase
  • Centralized API management
  • Safer authentication flow
  • Easier scaling
  • Better maintainability