dart_acdc library

ACDC - Advanced Client for Dio Communication

A production-ready HTTP client library built on top of Dio, providing:

Authentication:

  • Automatic token injection and refresh (OAuth 2.1 compliant)
  • Proactive refresh before token expiry
  • Reactive refresh on 401 responses
  • Concurrent request queuing during refresh
  • Token revocation on logout
  • Support for custom token refresh logic

Caching:

  • HTTP-compliant caching with Cache-Control support
  • User-based cache isolation
  • Optional encrypted cache storage
  • In-memory cache layer
  • Offline support with stale-while-revalidate

Error Handling:

  • Structured exception hierarchy
  • Developer-friendly error messages
  • Automatic retry with exponential backoff
  • Network error classification

Logging:

  • Environment-aware logging (debug vs release)
  • Sensitive data redaction
  • Custom logger integration
  • Request duration tracking

Quick Start

Zero-config usage (minimal setup):

import 'package:dart_acdc/dart_acdc.dart';

final dio = AcdcClientBuilder()
  .withBaseUrl('https://api.example.com')
  .build();

final response = await dio.get('/users');

With authentication:

// Implement TokenProvider to store/retrieve tokens
class MyTokenProvider implements TokenProvider {
  @override
  Future<String?> getAccessToken() async => // ... load from secure storage

  @override
  Future<String?> getRefreshToken() async => // ... load from secure storage

  @override
  Future<void> setTokens({
    required String accessToken,
    String? refreshToken,
    DateTime? accessExpiry,
    DateTime? refreshExpiry,
  }) async {
    // ... save to secure storage
  }

  @override
  Future<void> clearTokens() async {
    // ... clear from secure storage
  }

  @override
  Future<DateTime?> getAccessTokenExpiry() async => // ... optional

  @override
  Future<DateTime?> getRefreshTokenExpiry() async => // ... optional
}

final dio = AcdcClientBuilder()
  .withBaseUrl('https://api.example.com')
  .withTokenProvider(MyTokenProvider())
  .withTokenRefreshEndpoint(
    url: 'https://auth.example.com/oauth/token',
    clientId: 'my-client-id',
  )
  .withTokenRevocationEndpoint('https://auth.example.com/oauth/revoke')
  .build();

// Make authenticated requests
final response = await dio.get('/protected-resource');

// Logout
await dio.auth.logout();

With custom configuration:

final dio = AcdcClientBuilder()
  .withBaseUrl('https://api.example.com')
  .withTimeout(Duration(seconds: 30))
  .withLogLevel(LogLevel.debug)
  .withCache(CacheConfig(
    ttl: Duration(hours: 2),
    encrypted: true,
  ))
  .build();

Exception Handling

All HTTP and network errors are converted to typed exceptions:

try {
  final response = await dio.get('/users');
} on AcdcAuthException catch (e) {
  // Handle 401/403 errors
  print('Authentication error: ${e.message}');
} on AcdcServerException catch (e) {
  // Handle 5xx errors
  print('Server error: ${e.message}');
} on AcdcNetworkException catch (e) {
  // Handle network failures
  print('Network error: ${e.errorType}');
} on AcdcClientException catch (e) {
  // Handle 4xx errors (other than 401/403)
  print('Client error: ${e.message}');
}

Custom Logger

Integrate with your logging system:

class MyLogDelegate implements AcdcLogDelegate {
  @override
  void log(String message, LogLevel level, Map<String, dynamic> metadata) {
    // Forward to your logging system
    myLogger.log(level, message, metadata);
  }
}

final dio = AcdcClientBuilder()
  .withLogDelegate(MyLogDelegate())
  .build();

Advanced Features

Force token refresh:

await dio.auth.refreshNow();

Clear cache:

await dio.auth.clearCache();

Custom token refresh:

final dio = AcdcClientBuilder()
  .withTokenProvider(myTokenProvider)
  .withCustomTokenRefresh((refreshToken) async {
    // Your custom refresh logic
    final response = await myCustomRefreshCall(refreshToken);
    return TokenRefreshResult(
      accessToken: response.accessToken,
      refreshToken: response.newRefreshToken,
    );
  })
  .build();

Security Best Practices

  1. Token Storage: Use platform secure storage (iOS Keychain, Android Keystore)
  2. OAuth 2.1: This library follows OAuth 2.1 for public clients (no client_secret)
  3. Cache Encryption: Enable for sensitive data with CacheConfig(encrypted: true)
  4. HTTPS Only: Always use HTTPS URLs in production

Supported Dart/Flutter Versions

  • Dart SDK: >=3.6.0 <4.0.0
  • Flutter: >=3.38.0

License

See LICENSE file for details.

Classes

AcdcAuthManager
Manager for authentication operations.
AcdcCacheManager
Manager for cache operations.
AcdcClientBuilder
Immutable builder for creating pre-configured Dio HTTP clients.
AcdcLogDelegate
Interface for custom logging implementations.
CacheConfig
Configuration for HTTP response caching.
CertificatePinningConfig
Configuration for Certificate Pinning.
NetworkInfo
Interface for monitoring network connectivity.
SecureTokenProvider
A secure implementation of TokenProvider using FlutterSecureStorage.
TokenProvider
Interface for managing authentication tokens.
TokenRefreshResult
Result of a token refresh operation.

Enums

CacheOperation
Cache operation types for categorization.
LogLevel
Log verbosity levels.
NetworkErrorType
Network error types for categorization.
NetworkStatus
Represents the network connection status.

Extensions

AcdcAuth on Dio
Extension on Dio to access authentication manager.
AcdcCache on Dio
Extension on Dio to access cache manager.

Exceptions / Errors

AcdcAuthException
Exception for authentication and authorization errors.
AcdcCacheException
Exception for cache-related errors.
AcdcClientException
Exception for client-side errors.
AcdcException
Base exception class for all Dart-ACDC exceptions.
AcdcNetworkException
Exception for network-related errors.
AcdcSecurityException
Exception thrown when a security check fails, such as Certificate Pinning.
AcdcServerException
Exception for server-side errors.