minimal_rest_http 1.0.0 copy "minimal_rest_http: ^1.0.0" to clipboard
minimal_rest_http: ^1.0.0 copied to clipboard

A minimal, dynamic, and configurable REST HTTP client for Flutter applications with built-in error handling, retry logic, and flexible endpoint management.

Minimal REST HTTP Package #

pub package GitHub stars License: MIT

A minimal, dynamic, and configurable REST HTTP client for Flutter applications with built-in error handling, retry logic, and flexible endpoint management. Perfect for developers who want a robust, type-safe, and easy-to-use HTTP client with comprehensive features.

✨ Features #

  • 🚀 Dynamic Configuration: Easy setup with configurable base URLs, timeouts, and settings
  • 🔐 Flexible Authentication: Multiple authentication managers (SharedPreferences, Memory, Custom)
  • 📍 Endpoint Management: Dynamic endpoint management system for better maintainability
  • 🔄 Retry Logic: Built-in retry mechanism for failed requests with configurable attempts
  • 🛡️ Error Handling: Comprehensive error handling with optional UI popups
  • 📱 Connectivity Check: Automatic internet connectivity verification before requests
  • 🎯 Type Safety: Full type safety with generic response handling
  • 📊 Logging: Configurable request/response logging for debugging
  • 🔧 Extensible: Easy to extend and customize for your specific needs
  • 🌐 Any Response Format: Works with any API response structure
  • 🎨 Custom UI Integration: Easy integration with your existing popup/dialog systems
  • 📁 File Upload: Multipart file upload support
  • Performance: Optimized for performance with connection pooling

📦 Installation #

Add this to your package's pubspec.yaml file:

dependencies:
  minimal_rest_http: ^1.0.0

Then run:

flutter pub get

🚀 Quick Start #

1. Initialize the API Service #

import 'package:minimal_rest_http/api_service.dart';

Future<void> initializeMinRestService() async {
  // Create configuration
  final config = ApiConfig(
    baseUrl: 'https://jsonplaceholder.typicode.com',
    timeoutSeconds: 30,
    enableLogging: true,
    enableErrorPopups: true,
  );

  // Create auth manager
  final authManager = SharedPreferencesAuthManager(
    tokenKey: 'access_token',
  );

  // Create endpoint manager (optional)
  final endpointManager = EndpointManager(baseUrl: config.baseUrl);
  endpointManager.addEndpoints({
    'users': '/users',
    'posts': '/posts',
    'login': '/auth/login',
  });

  // Initialize Minimal REST HTTP service
  await MinRestService.initialize(
    config: config,
    authManager: authManager,
    endpointManager: endpointManager,
  );
}

2. Make API Calls #

// Using direct endpoints
final users = await MinRestService.instance.get<List<dynamic>>(
  '/users',
  fromJson: (data) => data as List<dynamic>,
);

// Using endpoint manager
final posts = await MinRestService.instance.getByKey<List<dynamic>>(
  'posts',
  fromJson: (data) => data as List<dynamic>,
);

// POST request with authentication
final newUser = await MinRestService.instance.post<Map<String, dynamic>>(
  '/users',
  body: {
    'name': 'John Doe',
    'email': 'john@example.com',
  },
  fromJson: (data) => data as Map<String, dynamic>,
  authToken: true,
);

3. Set Up Automatic Context Detection #

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'My App',
      home: MyHomePage().withApiContext(), // Automatic context detection
    );
  }
}

⚙️ Configuration #

ApiConfig #

final config = ApiConfig(
  baseUrl: 'https://api.example.com',        // Base URL for API
  timeoutSeconds: 30,                        // Request timeout
  maxRetryAttempts: 2,                       // Retry attempts
  enableLogging: true,                       // Enable request/response logging
  enableErrorPopups: false,                  // Enable error popups
  defaultHeaders: {                          // Default headers
    'Accept': 'application/json',
  },
  authTokenKey: 'access_token',              // Key for storing auth token
);

Runtime Configuration #

final minRestService = MinRestService.instance;

// Change base URL temporarily
await minRestService.withScope((service) async {
  return await service.get('/different-endpoint', fromJson: (data) => data);
}, baseUrl: 'https://different-api.com');

// Change timeout temporarily
await minRestService.withScope((service) async {
  return await service.get('/slow-endpoint', fromJson: (data) => data);
}, timeoutSeconds: 60);

// Persistent configuration changes
minRestService.withBaseUrl('https://new-api.com');
minRestService.withTimeout(45);

🔐 Authentication #

SharedPreferences Auth Manager #

final authManager = SharedPreferencesAuthManager(
  tokenKey: 'access_token',
);

// Set token
await authManager.setToken('your_jwt_token');

// Get token
final token = await authManager.getToken();

// Clear token
await authManager.clearToken();

// Check if token exists
final hasToken = await authManager.hasToken();

Custom Auth Manager #

final authManager = CustomAuthManager(
  getTokenFunction: () async => await getTokenFromSecureStorage(),
  setTokenFunction: (token) async => await saveTokenToSecureStorage(token),
  clearTokenFunction: () async => await clearTokenFromSecureStorage(),
  hasTokenFunction: () async => await hasTokenInSecureStorage(),
  tokenStream: tokenChangeStream,
);

📍 Endpoint Management #

final endpointManager = EndpointManager(baseUrl: 'https://api.example.com');

// Add single endpoint
endpointManager.addEndpoint('users', '/users');

// Add multiple endpoints
endpointManager.addEndpoints({
  'users': '/users',
  'posts': '/posts',
  'comments': '/comments',
  'login': '/auth/login',
  'profile': '/user/profile',
});

// Use endpoints
final users = await minRestService.getByKey<List<dynamic>>(
  'users',
  fromJson: (data) => data as List<dynamic>,
);

// Build URLs with parameters
final url = endpointManager.buildUrlWithParams(
  'users',
  queryParams: {'page': '1', 'limit': '10'},
);

🛡️ Error Handling with AppPopup #

The API Service package integrates seamlessly with your existing AppPopup system. When showErrorPopup is true, it will automatically show error popups using your AppPopup system.

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'My App',
      home: MyHomePage().withApiContext(), // Automatic context detection
    );
  }
}

Using AppPopup System #

// API calls will automatically show popups when showErrorPopup is true
Future<void> loadUsers() async {
  try {
    final response = await MinRestService.instance.getByKey<dynamic>(
      'users',
      fromJson: (data) => data,
      showErrorPopup: true, // This will show your AppPopup on error
    );

    // Handle response...
  } catch (e) {
    // Error popup is already shown by the API service
    // Handle any additional logic here if needed
  }
}

🌐 Handling Any Response Format #

The API Service package works with any response format. Here's how to handle different response structures:

Simple Array Response #

// API returns: [{"id": 1, "name": "John"}, {"id": 2, "name": "Jane"}]
final response = await MinRestService.instance.get<dynamic>(
  '/users',
  fromJson: (data) => data,
);

List<Map<String, dynamic>> users;
if (response is List) {
  users = response.cast<Map<String, dynamic>>();
} else {
  users = [];
}

Wrapped Response #

// API returns: {"success": true, "data": [{"id": 1, "name": "John"}]}
final response = await MinRestService.instance.get<dynamic>(
  '/users',
  fromJson: (data) => data,
);

List<Map<String, dynamic>> users;
if (response is Map<String, dynamic>) {
  final data = ResponseUtils.getData(response);
  if (data is List) {
    users = data.cast<Map<String, dynamic>>();
  } else {
    users = [];
  }
}

// Check if response indicates success
final isSuccess = ResponseUtils.isSuccess(response);
if (!isSuccess) {
  final errorMessage = ResponseUtils.getErrorMessage(response);
  print('API Error: $errorMessage');
}

Using ResponseUtils #

// Extract data from any response format
final data = ResponseUtils.getData(response);

// Check if response indicates success
final isSuccess = ResponseUtils.isSuccess(response);

// Get error message
final errorMessage = ResponseUtils.getErrorMessage(response);

// Check for pagination
if (ResponseUtils.hasPagination(response)) {
  final pagination = ResponseUtils.getPaginationInfo(response);
  print('Current page: ${pagination?['currentPage']}');
}

📡 HTTP Methods #

GET Request #

final data = await minRestService.get<Map<String, dynamic>>(
  '/endpoint',
  fromJson: (data) => data as Map<String, dynamic>,
  authToken: true,
  queryParams: {'param1': 'value1'},
);

POST Request #

final result = await minRestService.post<Map<String, dynamic>>(
  '/endpoint',
  body: {'key': 'value'},
  fromJson: (data) => data as Map<String, dynamic>,
  authToken: true,
);

Multipart POST (File Upload) #

final result = await minRestService.postMultipart<Map<String, dynamic>>(
  endpoint: '/upload',
  fromJson: (data) => data as Map<String, dynamic>,
  fileKey: 'file',
  filePath: '/path/to/file.jpg',
  fields: {'description': 'My file'},
  authToken: true,
);

PUT Request #

final result = await minRestService.put<Map<String, dynamic>>(
  '/endpoint/1',
  {'key': 'updated_value'},
  (data) => data as Map<String, dynamic>,
  authToken: true,
);

DELETE Request #

final result = await minRestService.delete<Map<String, dynamic>>(
  '/endpoint/1',
  (data) => data as Map<String, dynamic>,
  authToken: true,
);

🔧 Advanced Usage #

Custom Configuration per Request #

// Use different base URL for specific request
await minRestService.withScope((service) async {
  return await service.get('/external-api', fromJson: (data) => data);
}, baseUrl: 'https://external-api.com');

// Use different timeout for specific request
await minRestService.withScope((service) async {
  return await service.get('/slow-endpoint', fromJson: (data) => data);
}, timeoutSeconds: 120);

Connectivity Check #

// Check connectivity
final hasConnection = await ConnectivityHelper.hasInternetConnection();

// Get detailed connectivity status
final status = await ConnectivityHelper.getConnectivityStatus();
print('Connection type: ${status['connectionTypes']}');

// Listen to connectivity changes
ConnectivityHelper.connectivityStream.listen((results) {
  print('Connectivity changed: $results');
});

📱 Example Project #

Check out the example/ directory for a complete working example that demonstrates:

  • Basic API service setup
  • Authentication handling
  • Error handling with popups
  • Different response formats
  • File upload functionality

🚀 Migration from Hardcoded API Services #

Before (Hardcoded) #

class UrlHelper {
  static const String BaseUrl = "https://api.example.com";
  static const String users = "/users";
  static const String posts = "/posts";
}

// Usage
final response = await http.get(Uri.parse('${UrlHelper.BaseUrl}${UrlHelper.users}'));

After (Dynamic) #

// Setup
final endpointManager = EndpointManager(baseUrl: 'https://api.example.com');
endpointManager.addEndpoints({
  'users': '/users',
  'posts': '/posts',
});

// Usage
final response = await apiService.getByKey<Map<String, dynamic>>(
  'users',
  fromJson: (data) => data as Map<String, dynamic>,
);

🎯 Best Practices #

  1. Initialize Early: Initialize the API service in your app's main function
  2. Use Endpoint Manager: Define all endpoints in one place for better maintainability
  3. Handle Errors: Always handle errors appropriately for better user experience
  4. Type Safety: Use proper type definitions for your response models
  5. Configuration: Use different configurations for different environments (dev, staging, prod)
  6. Logging: Enable logging in development, disable in production
  7. Timeouts: Set appropriate timeouts based on your API's response times

📚 Documentation #

🤝 Contributing #

Contributions are welcome! Please feel free to submit a Pull Request.

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add some amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

📄 License #

This project is licensed under the MIT License - see the LICENSE file for details.

👨‍💻 Author #

MD Tangim Haque - @imtangim

⭐ Show Your Support #

Give a ⭐️ if this project helped you!

🐛 Report Issues #

If you find any issues or have suggestions, please open an issue.


Made with ❤️ by MD Tangim Haque

0
likes
150
points
0
downloads

Publisher

unverified uploader

Weekly Downloads

A minimal, dynamic, and configurable REST HTTP client for Flutter applications with built-in error handling, retry logic, and flexible endpoint management.

Repository (GitHub)
View/report issues

Documentation

API reference

License

MIT (license)

Dependencies

connectivity_plus, flutter, get, http, http_parser, retry, shared_preferences

More

Packages that depend on minimal_rest_http

Packages that implement minimal_rest_http