import 'dart:convert'; import 'package:http/http.dart' as http; typedef TokenProvider = String? Function(); typedef UnauthorizedHandler = Future Function(); class ApiService { final String baseUrl; TokenProvider? _getToken; UnauthorizedHandler? _onUnauthorized; ApiService({required this.baseUrl}); void setTokenProvider(TokenProvider provider) { _getToken = provider; } void setUnauthorizedHandler(UnauthorizedHandler handler) { _onUnauthorized = handler; } Map _buildHeaders(Map? extra) { final token = _getToken?.call(); final headers = {'accept': 'application/json', ...?extra}; if (token != null && token.isNotEmpty) { headers['Authorization'] = 'Bearer $token'; } return headers; } Future get(String endpoint, {Map? headers}) async { final response = await http.get( Uri.parse('$baseUrl$endpoint'), headers: _buildHeaders(headers), ); return _processResponse(response); } Future post( String endpoint, dynamic data, { Map? headers, }) async { final hasBody = data != null; final response = await http.post( Uri.parse('$baseUrl$endpoint'), headers: _buildHeaders(hasBody ? _jsonHeaders(headers) : headers), body: hasBody ? jsonEncode(data) : null, ); return _processResponse(response); } Future postForm(String endpoint, Map data) async { final response = await http.post( Uri.parse('$baseUrl$endpoint'), headers: _buildHeaders({ 'Content-Type': 'application/x-www-form-urlencoded', 'accept': 'application/json', }), body: data, // http package handles form-encoding for Map ); return _processResponse(response); } Future put( String endpoint, dynamic data, { Map? headers, }) async { final hasBody = data != null; final response = await http.put( Uri.parse('$baseUrl$endpoint'), headers: _buildHeaders(hasBody ? _jsonHeaders(headers) : headers), body: hasBody ? jsonEncode(data) : null, ); return _processResponse(response); } Future delete( String endpoint, { Map? headers, }) async { final response = await http.delete( Uri.parse('$baseUrl$endpoint'), headers: _buildHeaders(headers), ); return _processResponse(response); } Map _jsonHeaders(Map? extra) { return {'Content-Type': 'application/json', if (extra != null) ...extra}; } Future _processResponse(http.Response res) async { final body = res.body.isNotEmpty ? jsonDecode(res.body) : null; if (res.statusCode >= 200 && res.statusCode < 300) { return body; } if (res.statusCode == 401 && body is Map && body['detail'] == 'Not authenticated' && _onUnauthorized != null) { await _onUnauthorized!(); } throw Exception('API error ${res.statusCode}: $body'); } }