cyber_req 2.0.13 copy "cyber_req: ^2.0.13" to clipboard
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'),
          ],
        ),
      ),
    );
  }
}
2
likes
150
points
39
downloads

Publisher

unverified uploader

Weekly Downloads

A flexible Flutter/Dart API client for backends with dynamic headers, secure token management, and comprehensive callbacks.

Homepage

Documentation

API reference

License

MIT (license)

Dependencies

flutter, flutter_secure_storage, http

More

Packages that depend on cyber_req