https://github.com/moein-dev/dio_flow
A powerful Flutter package that enhances Dio HTTP client with built-in support for caching, authentication, pagination, error handling, and standardized JSON utilities.
https://github.com/moein-dev/dio_flow
Last synced: 11 months ago
JSON representation
A powerful Flutter package that enhances Dio HTTP client with built-in support for caching, authentication, pagination, error handling, and standardized JSON utilities.
- Host: GitHub
- URL: https://github.com/moein-dev/dio_flow
- Owner: Moein-dev
- License: mit
- Created: 2025-03-02T09:44:15.000Z (over 1 year ago)
- Default Branch: main
- Last Pushed: 2025-03-17T01:27:14.000Z (over 1 year ago)
- Last Synced: 2025-03-17T01:35:51.756Z (over 1 year ago)
- Language: Dart
- Homepage: https://pub.dev/packages/dio_flow
- Size: 352 KB
- Stars: 1
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
Awesome Lists containing this project
README
# 🌊 Dio Flow
[](https://pub.dev/packages/dio_flow)
[](https://opensource.org/licenses/MIT)
[](https://flutter.dev)
A powerful Flutter package that enhances Dio HTTP client with built-in support for caching, authentication, pagination, error handling, and standardized JSON utilities. Built for modern Flutter applications that need robust API integration.
## 📋 Table of Contents
- [Features](#-features)
- [Installation](#-installation)
- [Getting Started](#-getting-started)
- [Core Components](#-core-components)
- [Making Requests](#-making-requests)
- [Response Handling](#-response-handling)
- [Authentication](#-authentication)
- [Advanced Features](#-advanced-features)
- [Best Practices](#-best-practices)
- [Troubleshooting](#-troubleshooting)
- [Contributing](#-contributing)
- [License](#-license)
## ✨ Features
- **🚀 Modern HTTP Client**: Built on top of Dio with enhanced features
- **🔄 Smart Response Handling**: Automatic conversion of responses to strongly-typed models
- **💾 Intelligent Caching**: Built-in response caching with configurable TTL
- **🔑 Token Management**: Robust authentication with token refresh support
- **🔁 Auto-Retry**: Configurable retry logic for failed requests
- **⚡ Rate Limiting**: Prevent API throttling with built-in rate limiting
- **📶 Network Awareness**: Automatic handling of connectivity changes
- **📊 Request Metrics**: Built-in performance tracking
- **🔍 Detailed Logging**: Complete request/response logging with cURL commands
- **📄 Pagination Support**: Built-in utilities for handling paginated responses
- **🛡️ Type Safety**: Strong typing throughout the library
- **🎯 Error Handling**: Comprehensive error handling with typed error responses
## 📦 Installation
Add to your pubspec.yaml:
```yaml
dependencies:
dio_flow: ^1.1.7
```
## 🚀 Getting Started
### Basic Setup
```dart
import 'package:dio_flow/dio_flow.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// 1. Configure the client
DioFlowConfig.initialize(
baseUrl: 'https://api.example.com',
connectTimeout: const Duration(seconds: 30),
receiveTimeout: const Duration(seconds: 30),
sendTimeout: const Duration(seconds: 30),
);
// 2. Initialize the client
await ApiClient.initialize();
runApp(MyApp());
}
```
## 🎯 Core Components
### DioRequestHandler
The main class for making HTTP requests:
```dart
// GET request
final response = await DioRequestHandler.get(
'users',
parameters: {'role': 'admin'},
requestOptions: RequestOptionsModel(
hasBearerToken: true,
shouldCache: true,
retryOptions: RetryOptions(
maxAttempts: 3,
retryInterval: const Duration(seconds: 1),
),
),
);
// POST request with typed response
final loginResponse = await DioRequestHandler.post(
'auth/login',
data: {
'email': 'user@example.com',
'password': '********',
},
requestOptions: RequestOptionsModel(
hasBearerToken: false,
),
);
```
### Response Models
All responses are wrapped in typed models:
```dart
if (response.isSuccess) {
final data = response.data;
// Handle success
} else {
final error = response.error;
switch (error.errorType) {
case ErrorType.network:
// Handle network error
break;
case ErrorType.validation:
// Handle validation error
break;
case ErrorType.unauthorized:
// Handle auth error
break;
// ... handle other error types
}
}
```
### Interceptors
The package includes several built-in interceptors:
1. **MetricsInterceptor**: Tracks request performance
2. **RateLimitInterceptor**: Prevents API throttling
3. **DioInterceptor**: Handles authentication and headers
4. **RetryInterceptor**: Manages request retries
5. **ConnectivityInterceptor**: Handles network state
6. **CacheInterceptor**: Manages response caching
## 🔑 Authentication
The package provides robust token management with persistent storage:
```dart
// Initialize token manager (call this in your main.dart)
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await TokenManager.initialize();
runApp(MyApp());
}
// Setting tokens with persistence
await TokenManager.setTokens(
accessToken: 'your_access_token',
refreshToken: 'your_refresh_token',
expiry: DateTime.now().add(Duration(hours: 1)),
);
// Getting access token (automatically handles refresh if needed)
final token = await TokenManager.getAccessToken();
// Clearing tokens
await TokenManager.clearTokens();
```
Key features:
- Persistent token storage using SharedPreferences
- Automatic token loading on app initialization
- Token expiry tracking and automatic refresh
- Secure token management with proper error handling
- Asynchronous operations for better performance
### Protected Requests
```dart
// Make authenticated request
final response = await DioRequestHandler.get(
'user/profile',
requestOptions: RequestOptionsModel(
hasBearerToken: true, // This will automatically include the token
),
);
// Handle token expiration
if (response.error?.errorType == ErrorType.unauthorized) {
// Token expired, handle refresh or logout
}
```
## 🌐 Endpoint Configuration
### Basic Endpoints
```dart
// Register endpoints
EndpointProvider.instance.register('login', '/auth/login');
EndpointProvider.instance.register('users', '/api/users');
// Use registered endpoints
final response = await DioRequestHandler.post(
'login',
data: {'email': email, 'password': password},
);
```
### Dynamic Endpoints
```dart
// Register endpoint with parameters
EndpointProvider.instance.register('user_details', '/api/users/{id}');
// Use with path parameters
final response = await DioRequestHandler.get(
'user_details',
pathParameters: {'id': '123'},
);
```
## 🔄 Advanced Features
### Caching
```dart
// Enable caching for a request
final response = await DioRequestHandler.get(
'users',
requestOptions: RequestOptionsModel(
shouldCache: true,
cacheMaxAge: const Duration(minutes: 5),
),
);
// Clear cache
await ApiClient.clearCache();
```
### Pagination
```dart
// Using pagination utilities
final paginatedResponse = await DioRequestHandler.get(
'posts',
parameters: {
'page': 1,
'per_page': 20,
},
);
final pagination = PaginationHelper.fromResponse(paginatedResponse);
final hasMore = pagination.hasNextPage;
final totalPages = pagination.totalPages;
```
### JSON Utilities
```dart
// Safe JSON parsing
final jsonData = JsonUtils.tryParseJson(rawJson);
// Access nested values safely
final nestedValue = JsonUtils.getNestedValue(
jsonData,
'user.profile.name',
'Default Name',
);
```
### Request Queueing
```dart
// Queue multiple requests
final responses = await Future.wait([
DioRequestHandler.get('users'),
DioRequestHandler.get('posts'),
DioRequestHandler.get('comments'),
]);
// Handle rate limiting automatically
final rateLimitedResponse = await DioRequestHandler.get(
'high-frequency-endpoint',
requestOptions: RequestOptionsModel(
shouldRateLimit: true,
rateLimit: 30, // requests per minute
),
);
```
### Custom Response Types
```dart
class PaginatedResponse {
final List items;
final int total;
final int page;
PaginatedResponse.fromJson(
Map json,
T Function(Map) converter,
) : items = (json['data'] as List)
.map((item) => converter(item))
.toList(),
total = json['total'] ?? 0,
page = json['page'] ?? 1;
}
// Use with typed response
final response = await DioRequestHandler.get>(
'users',
converter: (json) => PaginatedResponse.fromJson(
json,
(item) => User.fromJson(item),
),
);
```
## 🛠️ Best Practices
1. **Initialize Early**:
```dart
void main() async {
await ApiClient.initialize();
// ... rest of your app initialization
}
```
2. **Handle Errors Consistently**:
```dart
try {
final response = await DioRequestHandler.get('endpoint');
if (response.isSuccess) {
// Handle success
} else {
// Use the typed error handling
handleError(response.error);
}
} catch (e) {
// Handle unexpected errors
}
```
3. **Use Type-Safe Responses**:
```dart
class UserResponse {
final String id;
final String name;
UserResponse.fromJson(Map json)
: id = json['id'],
name = json['name'];
}
final response = await DioRequestHandler.get(
'users/me',
converter: (json) => UserResponse.fromJson(json),
);
```
### Error Handling Patterns
```dart
// Create a reusable error handler
Future handleApiResponse(ResponseModel response) async {
if (response.isSuccess) {
return response.data as T;
}
switch (response.error?.errorType) {
case ErrorType.network:
throw NetworkException(response.error!.message);
case ErrorType.unauthorized:
await handleUnauthorized();
throw AuthException(response.error!.message);
case ErrorType.validation:
throw ValidationException(response.error!.message);
default:
throw ApiException(response.error?.message ?? 'Unknown error');
}
}
// Use in your code
try {
final users = await handleApiResponse>(
await DioRequestHandler.get('users'),
);
// Use users data
} on NetworkException catch (e) {
// Handle network error
} on AuthException catch (e) {
// Handle auth error
} on ValidationException catch (e) {
// Handle validation error
} on ApiException catch (e) {
// Handle other API errors
}
```
### Repository Pattern
```dart
class UserRepository {
Future getCurrentUser() async {
final response = await DioRequestHandler.get(
'users/me',
requestOptions: RequestOptionsModel(
hasBearerToken: true,
shouldCache: true,
cacheMaxAge: const Duration(minutes: 5),
),
converter: (json) => User.fromJson(json),
);
return handleApiResponse(response);
}
Future updateProfile(UserUpdateRequest request) async {
final response = await DioRequestHandler.put(
'users/me',
data: request.toJson(),
requestOptions: RequestOptionsModel(hasBearerToken: true),
);
await handleApiResponse(response);
}
}
```
## 🔍 Troubleshooting
Common issues and solutions:
1. **Authentication Issues**:
- Ensure `hasBearerToken` is set correctly in `RequestOptionsModel`
- Check if tokens are properly managed in `TokenManager`
2. **Caching Problems**:
- Verify `shouldCache` is enabled in request options
- Check cache duration settings
- Try clearing cache with `ApiClient.clearCache()`
3. **Network Errors**:
- Check connectivity status
- Verify retry options are configured
- Examine cURL logs for request details
### Debug Mode
```dart
// Enable detailed logging
DioFlowConfig.initialize(
baseUrl: 'https://api.example.com',
debugMode: true, // This will enable detailed logging
);
// Log specific requests
final response = await DioRequestHandler.get(
'users',
requestOptions: RequestOptionsModel(
shouldLogRequest: true, // Log this specific request
),
);
```
## 🤝 Contributing
Contributions are welcome! Please read our [Contributing Guide](CONTRIBUTING.md) for details on our code of conduct and the process for submitting pull requests.
## 📄 License
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.