Remote Client is a Dio-powered HTTP client for Dart and Flutter that packages retry logic, authentication, structured errors, connectivity checks, and logging into one cohesive API. Configure once, reuse anywhere, and keep network code predictable across your app.
- HTTP verbs (GET/POST/PUT/PATCH/DELETE) with typed responses
- Multipart upload, streaming download, and cancellation support
- Token-based auth with refresh handling and request transformation hooks
- Retry policies with exponential backoff + jitter
- Connectivity service with configurable caching and fallbacks
- Clean
Either<Failure, BaseResponse<T>>error model with sealed failures - No external dependencies besides Dio
dependencies: remote_client: ^1.0.0
```dart
import 'package:remote_client/remote_client.dart';
---
## Quick Start
### Minimal client
```dart
final client = RemoteClientFactory.create(
baseUrl: 'https://api.example.com',
enableLogging: true,
);
final result = await client.get('/users');
result.fold(
(failure) => debugPrint('Error: ${failure.errorMessage}'),
(response) => debugPrint('Success: ${response.data}'),
);
class MyTokenProvider implements TokenProvider {
@override
String? getAccessToken() => secureStorage.read('token');
@override
bool get hasValidToken => getAccessToken()?.isNotEmpty == true;
}
class MyUnauthorizedHandler implements UnauthorizedHandler {
@override
Future<void> handleUnauthorized() async => authService.refreshOrSignOut();
}
final client = RemoteClientFactory.builder()
.baseUrl('https://api.example.com')
.withAuth(
tokenProvider: MyTokenProvider(),
unauthorizedHandler: MyUnauthorizedHandler(),
locale: 'en-US',
)
.withRetry(RetryPolicy.defaultPolicy)
.enableLogging()
.build();| Component | Purpose |
|---|---|
RemoteClientFactory |
Fluent builder to assemble clients with auth, retry, logging, connectivity, and hooks. |
RemoteClient |
Typed request methods (get, post, put, patch, delete, multiPartPost, download). |
BaseResponse<T> |
Structured response data (data, statusCode, message, meta). |
Failure |
Exhaustive sealed errors (timeouts, network, HTTP codes, cancellations, unexpected). |
NetworkConfig |
Base URL, timeouts, headers, connection pooling. |
RetryPolicy |
Prebuilt or custom retry rules (max attempts, backoff, jitter, retryable codes). |
RequestTimeoutConfig |
Global or per-request timeout presets (quick, normal, extended, file upload/download). |
TransformationHooks |
Modify request payloads and responses (encryption, mapping, normalization). |
ResponseParser |
Default, direct, or custom parsers for various API payload shapes. |
ConnectivityService |
Lightweight connectivity checks with TTL caching and custom hosts. |
Tip
All request methods return Future<Either<Failure, BaseResponse<T>>>, making success/error handling explicit and testable.
final users = await client.get<List<User>>(
'/users',
queryParams: {'page': 1, 'limit': 20},
fromJson: (json) => (json as List)
.map((item) => User.fromJson(item as Map<String, dynamic>))
.toList(),
);await client.post(
'/users',
data: {'name': 'Ada Lovelace'},
options: Options(headers: {'X-Client': 'mobile'}),
timeout: RequestTimeoutConfig.quick,
);final upload = await client.multiPartPost(
'/files',
data: FormData.fromMap({
'file': await MultipartFile.fromFile(path, filename: 'doc.pdf'),
}),
onSendProgress: (sent, total) => debugPrint('$sent / $total'),
timeout: RequestTimeoutConfig.fileUpload,
);
final download = await client.download(
'https://example.com/report.pdf',
'/local/report.pdf',
onReceiveProgress: (received, total) => debugPrint('$received / $total'),
timeout: RequestTimeoutConfig.fileDownload,
);-
Authentication
- Provide a
TokenProviderto inject tokens. - Implement
UnauthorizedHandlerfor refresh flows or sign-out. - Optional
localeheader per request.
- Provide a
-
Connectivity
ConnectivityServiceImplwith default DNS hosts and cache TTL.ConnectivityServiceImpl.withHosts([...])for custom probes.- Call
clearCache()to force re-checks.
-
Retry Policies
RetryPolicy.defaultPolicy,aggressive,conservative, ornoRetry.- Override parameters (max retries, delays, jitter, status codes, predicates).
-
Response Parsing
DefaultResponseParserfor wrapped payloads ({success, data, message, meta}).DirectResponseParserfor bare JSON objects.- Implement
ResponseParserto handle custom envelopes.
-
Transformation Hooks
TransformationHooks(onRequestTransform: ..., onResponseTransform: ...)to encrypt, normalize, or map fields.- Hooks are invoked per request with endpoint context.
-
Logging
- Enable via factory (
enableLogging()). - Swap
LoggingInterceptorfor custom log sinks or levels.
- Enable via factory (
-
Timeouts
- Configure defaults with
NetworkConfig. - Use presets (
quick,normal,extended,fileUpload,fileDownload) or build custom viaRequestTimeoutConfig.
- Configure defaults with
For a complete guided setup, check the example app in example/lib/main.dart.
RemoteClient createTestClient() => RemoteClientFactory.create(
baseUrl: 'https://test-api.example.com',
retryPolicy: RetryPolicy.noRetry,
enableLogging: false,
);Mock RemoteClient or the underlying contracts (TokenProvider, ConnectivityService, ResponseParser) to isolate units in your tests.
Contributions are welcome—open an issue or pull request with your proposal. Make sure tests pass and include coverage for new behavior.
Licensed under the MIT License.