import 'dart:convert'; import 'package:dio/dio.dart'; import 'package:flutter/foundation.dart'; /// API Client for connecting to the FastAPI backend class ApiClient { static ApiClient? _instance; static ApiClient get instance => _instance ??= ApiClient._(); late Dio _dio; String _baseUrl = 'http://localhost:8000'; ApiClient._() { _dio = Dio(BaseOptions( baseUrl: _baseUrl, connectTimeout: const Duration(seconds: 10), receiveTimeout: const Duration(seconds: 10), headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', }, )); // Add interceptors for logging in debug mode if (kDebugMode) { _dio.interceptors.add(LogInterceptor( requestBody: true, responseBody: true, error: true, )); } // Error handling interceptor _dio.interceptors.add(InterceptorsWrapper( onError: (error, handler) { if (error.response != null) { debugPrint('API Error: ${error.response?.statusCode} - ${error.response?.data}'); } else { debugPrint('API Error: ${error.message}'); } handler.next(error); }, )); } /// Initialize with custom base URL void initialize({String? baseUrl}) { final url = baseUrl ?? getBaseUrl(); _baseUrl = url; _dio.options.baseUrl = _baseUrl; } /// Get base URL from environment or use default static String getBaseUrl() { const url = String.fromEnvironment('API_BASE_URL'); return url.isNotEmpty ? url : 'http://localhost:8000'; } /// Get current base URL String get baseUrl => _baseUrl; /// GET request Future> get( String path, { Map? queryParameters, Options? options, }) async { try { return await _dio.get( path, queryParameters: queryParameters, options: options, ); } catch (e) { if (e is DioException) { rethrow; } throw DioException( requestOptions: RequestOptions(path: path), error: e, ); } } /// POST request Future> post( String path, { dynamic data, Map? queryParameters, Options? options, }) async { try { return await _dio.post( path, data: data, queryParameters: queryParameters, options: options, ); } catch (e) { if (e is DioException) { rethrow; } throw DioException( requestOptions: RequestOptions(path: path), error: e, ); } } /// PUT request Future> put( String path, { dynamic data, Map? queryParameters, Options? options, }) async { try { return await _dio.put( path, data: data, queryParameters: queryParameters, options: options, ); } catch (e) { if (e is DioException) { rethrow; } throw DioException( requestOptions: RequestOptions(path: path), error: e, ); } } /// DELETE request Future> delete( String path, { dynamic data, Map? queryParameters, Options? options, }) async { try { return await _dio.delete( path, data: data, queryParameters: queryParameters, options: options, ); } catch (e) { if (e is DioException) { rethrow; } throw DioException( requestOptions: RequestOptions(path: path), error: e, ); } } /// Check if API is available Future checkConnection() async { try { final response = await _dio.get('/health'); return response.statusCode == 200; } catch (e) { return false; } } }