cyber_req 2.0.13
cyber_req: ^2.0.13 copied to clipboard
A flexible Flutter/Dart API client for backends with dynamic headers, secure token management, and comprehensive callbacks.
example/main.dart
import 'package:flutter/material.dart';
import 'package:path_provider/path_provider.dart';
import 'dart:io';
import 'package:cyber_req/cyber_req.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:http/http.dart' as http;
const storage = FlutterSecureStorage();
// Example unauthorized handler: This function will be called when a 401 Unauthorized response is received.
Future<void> myUnauthorizedHandler() async {
debugPrint('Authentication failed! User needs to re-authenticate.');
await storage.delete(key: 'token'); // Clear any invalid token
// Implement navigation to login screen or token refresh logic here.
}
final ApiService apiService = ApiService(
baseUrl: 'https://your-laravel-backend.com/api', // **Required**: Your API's base URL.
bearerToken: 'your_static_bearer_token_if_any', // Optional: A static bearer token to be used for all requests.
// Prefer `useStorageToken` for dynamic tokens.
httpClient: http.Client(), // Optional: Provide a custom http.Client instance. Useful for testing or custom configurations.
onUnauthorized: myUnauthorizedHandler, // Optional: Callback function for 401 Unauthorized responses.
defaultHeaders: { // Optional: Headers that will be sent with every request.
'Content-Type': 'application/json',
'X-App-Version': '1.0.0',
},
autoLogResponse: true, // Optional: Set to `false` to disable automatic logging of requests and responses. Defaults to `true`.
);
Future<void> createNewUser(String name, String email, String password) async {
try {
final response = await apiService.post(
'register', // Your API endpoint
data: {
'name': name,
'email': email,
'password': password,
},
extraHeaders: {
'X-Request-ID': 'unique-id-123', // Example of an extra header
},
// No useStorageToken here, as this might be a registration endpoint
onSuccess: (data, {statusCode}) {
debugPrint('User registered successfully: $data (Status: $statusCode)');
// Save the token if returned by the API
if (data.containsKey('token')) {
storage.write(key: 'token', value: data['token']);
debugPrint('Token saved to secure storage.');
}
},
onFailure: (error, {statusCode}) {
debugPrint('Registration failed: ${error.message} (Status: $statusCode)');
// Handle specific error messages from the backend
},
);
debugPrint('Raw POST response: $response');
} on UnauthorizedException {
debugPrint('Unauthorized access during POST. This should not happen for registration.');
} on ApiException catch (e) {
debugPrint('API Exception during POST: ${e.message} (Status: ${e.statusCode})');
} catch (e) {
debugPrint('An unexpected error occurred during POST: $e');
}
}
Future<void> fetchUserProfile(String userId) async {
try {
final response = await apiService.get(
'users/$userId', // Endpoint with path parameter
queryParams: {
'include_posts': 'true', // Example query parameter
'limit': '5',
},
useStorageToken: true, // Use the token from FlutterSecureStorage
onSuccess: (data, {statusCode}) {
debugPrint('User profile fetched: ${data['user']['name']} (Status: $statusCode)');
},
onFailure: (error, {statusCode}) {
debugPrint('User profile fetch failed: ${error.message} (Status: $statusCode)');
},
);
debugPrint('Raw GET response: $response');
} on UnauthorizedException {
debugPrint('Unauthorized access during GET.');
} on ApiException catch (e) {
debugPrint('API Exception during GET: ${e.message} (Status: ${e.statusCode})');
} catch (e) {
debugPrint('An unexpected error occurred during GET: $e');
}
}
Future<void> updateProduct(String productId, Map<String, dynamic> updates) async {
try {
final response = await apiService.put(
'products/$productId',
data: updates,
useStorageToken: true,
onSuccess: (data, {statusCode}) {
debugPrint('Product updated successfully: $data (Status: $statusCode)');
},
onFailure: (error, {statusCode}) {
debugPrint('Product update failed: ${error.message} (Status: $statusCode)');
},
);
debugPrint('Raw PUT response: $response');
} on UnauthorizedException {
debugPrint('Unauthorized access during PUT.');
} on ApiException catch (e) {
debugPrint('API Exception during PUT: ${e.message} (Status: ${e.statusCode})');
} catch (e) {
debugPrint('An unexpected error occurred during PUT: $e');
}
}
Future<void> removeComment(String commentId) async {
try {
final response = await apiService.delete(
'comments/$commentId',
useStorageToken: true,
allowedStatusCodes: [204], // API might return 204 No Content for successful deletion
onSuccess: (data, {statusCode}) {
debugPrint('Comment deleted successfully. (Status: $statusCode)');
},
onFailure: (error, {statusCode}) {
debugPrint('Comment deletion failed: ${error.message} (Status: $statusCode)');
},
);
debugPrint('Raw DELETE response: $response');
} on UnauthorizedException {
debugPrint('Unauthorized access during DELETE.');
} on ApiException catch (e) {
debugPrint('API Exception during DELETE: ${e.message} (Status: ${e.statusCode})');
} catch (e) {
debugPrint('An unexpected error occurred during DELETE: $e');
}
}
Future<void> uploadProfilePicture(String userId, File imageFile) async {
try {
final response = await apiService.postMultipart(
'users/$userId/profile-picture', // Your API endpoint for file upload
files: [
MultipartFile(
field: 'profile_picture', // The field name expected by your backend for the file
file: imageFile,
filename: 'profile.jpg', // Optional: specify a filename, otherwise it's inferred
),
],
fields: {
'description': 'User profile picture upload', // Optional: additional text fields
'user_id': userId,
},
useStorageToken: true, // Use the token from FlutterSecureStorage
onSuccess: (data, {statusCode}) {
debugPrint('Profile picture uploaded successfully: $data (Status: $statusCode)');
},
onFailure: (error, {statusCode}) {
debugPrint('Profile picture upload failed: ${error.message} (Status: $statusCode)');
},
);
debugPrint('Raw Multipart POST response: $response');
} on UnauthorizedException {
debugPrint('Unauthorized access during Multipart POST.');
} on ApiException catch (e) {
debugPrint('API Exception during Multipart POST: ${e.message} (Status: ${e.statusCode})');
} catch (e) {
debugPrint('An unexpected error occurred during Multipart POST: $e');
}
}
Future<void> saveAuthToken(String token) async {
await storage.write(key: 'token', value: token);
debugPrint('Authentication token saved securely.');
}
Future<void> clearAuthToken() async {
await storage.delete(key: 'token');
debugPrint('Authentication token cleared from secure storage.');
}
Future<void> demonstrateErrorHandling() async {
try {
// Attempt to fetch data that requires authentication
final data = await apiService.get('user/dashboard', useStorageToken: true);
debugPrint('Dashboard data: $data');
} on UnauthorizedException {
// Handle 401 Unauthorized specifically
debugPrint('Access denied. Your session may have expired. Please log in again.');
// Trigger your app's logout or token refresh flow
await storage.delete(key: 'token'); // Clear invalid token
// Navigator.pushReplacementNamed(context, '/login');
} on ApiException catch (e) {
// Handle other API errors (e.g., 400 Bad Request, 404 Not Found, 500 Internal Server Error)
debugPrint('An API error occurred: ${e.message} (Status Code: ${e.statusCode})');
if (e.statusCode == 404) {
debugPrint('Resource not found.');
} else if (e.statusCode == 403) {
debugPrint('Permission denied.');
}
// Display an error message to the user
} on SocketException {
// Handle network connectivity issues (e.g., no internet)
debugPrint('Network error: Please check your internet connection.');
} on HttpException {
// Handle general HTTP errors during communication
debugPrint('HTTP error: Failed to communicate with server.');
} on FormatException {
// Handle cases where the server response is not valid JSON
debugPrint('Invalid response format from server.');
} catch (e) {
// Catch any other unexpected errors
debugPrint('An unexpected error occurred: $e');
}
}
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Cyber Req Examples',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const ExampleScreen(),
);
}
}
class ExampleScreen extends StatefulWidget {
const ExampleScreen({super.key});
@override
State<ExampleScreen> createState() => _ExampleScreenState();
}
class _ExampleScreenState extends State<ExampleScreen> {
String _postResult = 'Loading...';
String _getResult = 'Loading...';
String _putResult = 'Loading...';
String _deleteResult = 'Loading...';
String _multipartResult = 'Loading...';
String _tokenSaveResult = 'Loading...';
String _tokenUseResult = 'Loading...';
String _tokenClearResult = 'Loading...';
String _errorHandlingResult = 'Loading...';
@override
void initState() {
super.initState();
_runAllExamples();
}
Future<void> _runAllExamples() async {
// Example: POST Request
setState(() => _postResult = 'Running POST example...');
await createNewUser('John Doe', 'john.doe@example.com', 'password123');
setState(() => _postResult = 'POST example finished. Check console for output.');
// Example: GET Request
setState(() => _getResult = 'Running GET example...');
await fetchUserProfile('1'); // Assuming user ID 1
setState(() => _getResult = 'GET example finished. Check console for output.');
// Example: PUT Request
setState(() => _putResult = 'Running PUT example...');
await updateProduct('1', {'name': 'Updated Product', 'price': 99.99});
setState(() => _putResult = 'PUT example finished. Check console for output.');
// Example: DELETE Request
setState(() => _deleteResult = 'Running DELETE example...');
await removeComment('1'); // Assuming comment ID 1
setState(() => _deleteResult = 'DELETE example finished. Check console for output.');
// Example: Multipart POST Request (requires a dummy file)
setState(() => _multipartResult = 'Running Multipart POST example...');
// Create a dummy file for upload
final directory = await getTemporaryDirectory();
final dummyFile = File('${directory.path}/dummy_image.jpg');
if (!await dummyFile.exists()) {
await dummyFile.writeAsBytes([0, 1, 2, 3]); // Write some dummy bytes
}
await uploadProfilePicture('1', dummyFile);
setState(() => _multipartResult = 'Multipart POST example finished. Check console for output.');
// Example: Token Management
setState(() => _tokenSaveResult = 'Running Token Save example...');
await saveAuthToken('my_super_secret_token');
setState(() => _tokenSaveResult = 'Token Save example finished. Check console for output.');
setState(() => _tokenUseResult = 'Running Token Use example...');
// This would typically be part of another request, but for demonstration:
// await apiService.get('protected-data', useStorageToken: true);
setState(() => _tokenUseResult = 'Token Use example finished. Check console for output.');
setState(() => _tokenClearResult = 'Running Token Clear example...');
await clearAuthToken();
setState(() => _tokenClearResult = 'Token Clear example finished. Check console for output.');
// Example: Error Handling
setState(() => _errorHandlingResult = 'Running Error Handling example...');
await demonstrateErrorHandling();
setState(() => _errorHandlingResult = 'Error Handling example finished. Check console for output.');
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Cyber Req Examples'),
),
body: SingleChildScrollView(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('POST Example: $_postResult'),
const SizedBox(height: 10),
Text('GET Example: $_getResult'),
const SizedBox(height: 10),
Text('PUT Example: $_putResult'),
const SizedBox(height: 10),
Text('DELETE Example: $_deleteResult'),
const SizedBox(height: 10),
Text('Multipart POST Example: $_multipartResult'),
const SizedBox(height: 10),
Text('Token Save Example: $_tokenSaveResult'),
const SizedBox(height: 10),
Text('Token Use Example: $_tokenUseResult'),
const SizedBox(height: 10),
Text('Token Clear Example: $_tokenClearResult'),
const SizedBox(height: 10),
Text('Error Handling Example: $_errorHandlingResult'),
],
),
),
);
}
}