htpio ✨

htpio is the next-gen HTTP client for Flutter mobile apps and cross-platform Dart — designed to simplify networking, handle mobile-specific challenges, and boost developer productivity with a smile 😊

🌟 Features

  • Modular Middleware System
  • In-App Debug Console — like DevTools, but mobile
  • Smart Offline Mode for flaky networks
  • Pause/Resume File Downloads
  • Auto Auth Token Handling — plug & play
  • Mobile-Friendly Error Middleware
  • Typed Requests & Responses
  • Mock API Mode for instant testing
  • Intelligent Retry with Backoff
  • Caching, Timeout & Cancel
  • Built for Flutter — light, fast, joyful

🧠 Why htpio?

Other packages are great for basic requests, but htpio goes beyond:

  • Designed for mobile realities (offline, errors, retries)
  • Great DX with emoji-friendly debug UI
  • Mock & test with ease

💻 Compatibility

  • Flutter (iOS & Android)
  • Dart CLI & Server

Tasks You Can Perform with the htpio Package

1. Basic HTTP Requests

You can perform all standard HTTP requests (GET, POST, PUT, DELETE) with type-safe responses:

// GET request example
final client = HtpioClient();
final response = await client.getRequest<List<Product>>(
  endpoint: 'https://api.example.com/products',
  fromJson: (json) => (json['data'] as List)
      .map((item) => Product.fromJson(item))
      .toList(),
);

// POST request example
final response = await client.postRequest<User>(
  endpoint: 'https://api.example.com/users',
  data: {'name': 'John', 'email': '[email protected]'},
  fromJson: (json) => User.fromJson(json),
);

2. Authentication

You can handle authentication using the built-in token interceptor:

final authInterceptor = AuthTokenInterceptor(
  token: 'your-auth-token',  // Initial token
  headerName: 'Authorization',
  tokenPrefix: 'Bearer',
);

final client = HtpioClient();
client.addInterceptor(authInterceptor);

// Later, update the token
authInterceptor.setToken('new-token');

// Or clear it
authInterceptor.clearToken();

3. Request/Response Interception

You can intercept and modify requests and responses using interceptors:

class LoggingInterceptor extends HtpioInterceptor {
  @override
  Future<HtpioRequest> onRequest(HtpioRequest request) async {
    print('🚀 Request: ${request.method} ${request.url}');
    return request;
  }

  @override
  Future<HtpioResponse> onResponse(HtpioResponse response) async {
    print('✅ Response: ${response.statusCode}');
    return response;
  }
}

final client = HtpioClient();
client.addInterceptor(LoggingInterceptor());

4. Middleware for Request Processing

You can use middleware to process requests and responses:

class TimingMiddleware extends HtpioMiddleware {
  late DateTime _startTime;
  
  @override
  Future<void> beforeRequest(HtpioRequest request) async {
    _startTime = DateTime.now();
  }
  
  @override
  Future<void> afterResponse(HtpioResponse response) async {
    final duration = DateTime.now().difference(_startTime);
    print('Request took ${duration.inMilliseconds}ms');
  }
}

final client = HtpioClient();
client.use(TimingMiddleware());

5. Automatic Retry on Failure

You can automatically retry failed requests using the RetryInterceptor:

final retryInterceptor = RetryInterceptor(
  maxRetries: 3,
  baseDelay: Duration(seconds: 1),
  useExponentialBackoff: true,
  retryableStatusCodes: [408, 429, 500, 502, 503, 504],
);

final client = HtpioClient();
client.addInterceptor(retryInterceptor);

6. Response Caching

You can cache responses to reduce network requests:

final cache = HtpioCache(defaultTtl: Duration(minutes: 10));

// Check cache before making a request
String cacheKey = 'GET:https://api.example.com/data';
final cachedResponse = cache.get<MyData>(cacheKey);

if (cachedResponse != null) {
  return cachedResponse;
} else {
  final response = await client.getRequest<MyData>(...);
  cache.set(cacheKey, response);
  return response;
}

7. File Downloads with Progress Tracking

You can download files with progress tracking:

final downloadManager = HtpioDownloadManager();

// Listen to download progress
downloadManager.progressStream.listen((progress) {
  print('Download progress: ${(progress.progress * 100).toStringAsFixed(1)}%');
});

// Start a download
final file = await downloadManager.downloadFile(
  url: 'https://example.com/large-file.zip',
  savePath: '/path/to/save/file.zip',
  headers: {'Authorization': 'Bearer token'},
  onProgress: (progress) {
    // Update UI with progress
    setState(() {
      downloadProgress = progress;
    });
  },
);

// Pause, resume, or cancel downloads
downloadManager.pauseDownload('https://example.com/large-file.zip');
downloadManager.resumeDownload('https://example.com/large-file.zip');
downloadManager.cancelDownload('https://example.com/large-file.zip');

8. Mock Server for Testing

You can create mock responses for testing without actual network requests:

final mockServer = MockServer();
mockServer.enable();

// Register a mock response
mockServer.registerMock(
  'https://api.example.com/users',
  {'id': 1, 'name': 'Test User'},
  statusCode: 200,
  method: 'GET',
  delay: Duration(milliseconds: 300), // Simulate network delay
);

// Register sequential responses
mockServer.registerSequentialMocks(
  'https://api.example.com/status',
  [
    MockResponse(data: {'status': 'pending'}, statusCode: 200),
    MockResponse(data: {'status': 'processing'}, statusCode: 200),
    MockResponse(data: {'status': 'completed'}, statusCode: 200),
  ],
);

9. Offline Mode Support

You can queue requests when offline and execute them when back online:

final offlineMode = OfflineMode(checkInterval: Duration(seconds: 10));
final client = HtpioClient();
client.use(offlineMode);

// Listen to connectivity changes
offlineMode.connectivityStream.listen((isOnline) {
  print('Device is ${isOnline ? 'online' : 'offline'}');
});

// Check current status
if (offlineMode.isOnline) {
  print('Device is currently online');
} else {
  print('Device is currently offline');
  print('Queued requests: ${offlineMode.queuedRequestsCount}');
}

// Manually retry queued requests
await offlineMode.retryQueuedRequests();

10. Connectivity Monitoring

You can monitor network connectivity:

// Check if device is online
final isOnline = await ConnectivityHelper.isOnline();

// More reliable check with multiple hosts
final isReliablyOnline = await ConnectivityHelper.isOnlineReliable(
  hosts: ['google.com', 'cloudflare.com', '8.8.8.8'],
  timeout: Duration(seconds: 3),
);

// Listen to connectivity changes
ConnectivityHelper.onStatusChange.listen((isOnline) {
  print('Connection status changed: ${isOnline ? 'online' : 'offline'}');
});

// Check if connected to WiFi
final isWiFi = await ConnectivityHelper.isWiFiConnected();

11. Debugging Support

You can debug network requests with a visual console overlay:

final debugConsole = DebugConsole();
debugConsole.log('Starting network request...');

// In your Flutter widget tree
Overlay(
  initialEntries: [
    OverlayEntry(
      builder: (context) => Stack(
        children: [
          // Your app UI
          YourAppWidget(),
          // Debug overlay
          debugConsole.overlay(),
        ],
      ),
    ),
  ],
)

12. File Uploads

You can upload files with additional form data:

final response = await client.postSingleFileWithDataRequest<UploadResult>(
  endpoint: 'https://api.example.com/upload',
  fileJsonKey: 'file',
  file: File('/path/to/image.jpg'),
  data: {'description': 'Profile picture'},
  fromJson: (json) => UploadResult.fromJson(json),
);

// Upload multiple files
final response = await client.postFilesWithDataRequest<UploadResult>(
  endpoint: 'https://api.example.com/upload-multiple',
  fileJsonKey: 'files',
  files: [File('/path/to/file1.jpg'), File('/path/to/file2.jpg')],
  data: {'album': 'Vacation 2023'},
  fromJson: (json) => UploadResult.fromJson(json),
);

The htpio package provides a comprehensive set of tools for handling HTTP requests in Dart and Flutter applications, with features comparable to popular packages like http and dio, but with additional functionality for caching, offline support, mocking, and debugging.