Networking Layer 🚀
A robust, flexible, and lightweight networking layer for Flutter applications, built on top of Dio. It simplifies API interactions, supports both REST and GraphQL, and provides multiple response handling styles to suit your project's needs.
Features ✨
- REST & GraphQL Support: Handle both API styles seamlessly.
- Dual Response Styles: Choose between functional (Either style) or imperative (Direct style) response handling.
- Centralized Configuration: Easily initialize and manage settings via
AppConstants. - Built-in Error Handling: Unified handling for timeouts, connection errors, and status codes.
- Dynamic Translation: Catchy error messages with a hook for your own translation logic.
- Interceptor Ready: Pre-configured logging, auth token injection, and retry logic.
- File Support: Easy-to-use methods for downloading and uploading files.
🚀 Getting Started
Add the following to your pubspec.yaml:
dependencies:
networking_layer: # use latest version
🛠 Initialization
Initialize the networking layer in your startup logic (e.g., main.dart) using AppConstants. This ensures your baseUrl and other configurations are set globally.
import 'package:networking_layer/networking_layer.dart';
void main() {
AppConstants.init();
runApp(MyApp());
}
You can customize AppConstants or call DioServices.instance.init() directly for more control:
DioServices.instance.init(
DioConfig(
baseUrl: 'https://api.example.com',
getToken: () => 'your_auth_token',
translate: (key) => MyTranslator.tr(key),
onTokenExpired: () => handleLogout(),
onConnectionError: () => showNoInternetDialog(),
),
);
📖 Usage Styles
1. Direct Style (Imperative)
Perfect for simple scenarios where you want to handle the response manually.
Future<void> fetchData() async {
HelperResponse response = await DioServices.instance.get('/users');
if (response.success) {
var data = response.data; // Use your data
debugPrint("Success: ${data.length} items found.");
} else {
debugPrint("Error: ${response.message}");
}
}
2. ApiClient Mixin (Functional / Either Style)
Recommended for larger projects. It returns Either<HelperResponse, T>, forcing you to handle both success and error cases cleanly.
class UserRepository with ApiClient {
Future<Either<HelperResponse, User>> getUser(String id) async {
return await get<User>(
'/users/$id',
fromJson: (json) => User.fromJson(json),
);
}
}
// In your UI/Bloc
final result = await userRepository.getUser('1');
result.fold(
(error) => showError(error.message),
(user) => showProfile(user),
);
🌌 GraphQL Support
Making GraphQL queries or mutations is just as easy:
Future<Either<HelperResponse, User>> getUserProfile(String userId) async {
const query = r'''
query GetUser($id: ID!) {
user(id: $id) {
id
name
}
}
''';
return await graphQLQuery<User>(
query,
variables: {'id': userId},
dataKey: 'user', // Key in the JSON response
fromJson: (json) => User.fromJson(json),
);
}
🌐 Dynamic Translation
You can update the translation logic at any time without re-initializing the whole service:
DioServices.globalTranslate = (key) => myTheme.isArabic ? keysAr[key] : keysEn[key];
📂 File Handling
Uploading Files
await upload<String>(
'/upload/profile',
formData: {
'file': await MultipartFile.fromFile(imagePath),
},
fromJson: (json) => json['url'],
);
Downloading Files
await download(
'https://example.com/file.pdf',
'/local/path/file.pdf',
);
🛠 Advanced Features
- Auth Token Update: Use
DioServices.instance.updateAuthToken(token)to update headers dynamically after login. - Retry Logic: Automatically retries on 500+ errors or network timeouts (configurable).
- Custom Logging: Professional console logging for requests and responses.
🤝 Contributing
Contributions are welcome! Feel free to open issues or submit pull requests.
📜 License
This project is licensed under the MIT License - see the LICENSE file for details.