62 lines
1.6 KiB
Dart
62 lines
1.6 KiB
Dart
/// HTTP client placeholder that talks to a public sample API.
|
|
library;
|
|
|
|
import 'dart:convert';
|
|
|
|
import 'package:http/http.dart' as http;
|
|
|
|
import '../models/item.dart';
|
|
|
|
/// Fetches catalog items from JSONPlaceholder.
|
|
///
|
|
/// This is intentionally a thin, replaceable client. Swap [baseUrl] and
|
|
/// [Item.fromJson] when you point the app at a real backend.
|
|
class ItemApi {
|
|
ItemApi({http.Client? client, this.baseUrl = defaultBaseUrl})
|
|
: _client = client ?? http.Client();
|
|
|
|
static const defaultBaseUrl = 'https://jsonplaceholder.typicode.com';
|
|
|
|
final http.Client _client;
|
|
final String baseUrl;
|
|
|
|
/// GET `/posts` and map each record to an [Item].
|
|
Future<List<Item>> fetchItems({int limit = 12}) async {
|
|
final uri = Uri.parse('$baseUrl/posts');
|
|
final response = await _client.get(
|
|
uri,
|
|
headers: const {'Accept': 'application/json'},
|
|
);
|
|
|
|
if (response.statusCode < 200 || response.statusCode >= 300) {
|
|
throw ItemApiException(
|
|
'Request failed (${response.statusCode})',
|
|
statusCode: response.statusCode,
|
|
);
|
|
}
|
|
|
|
final decoded = jsonDecode(response.body);
|
|
if (decoded is! List) {
|
|
throw const ItemApiException('Unexpected response shape');
|
|
}
|
|
|
|
return decoded
|
|
.whereType<Map<String, dynamic>>()
|
|
.take(limit)
|
|
.map(Item.fromJson)
|
|
.toList(growable: false);
|
|
}
|
|
|
|
void close() => _client.close();
|
|
}
|
|
|
|
class ItemApiException implements Exception {
|
|
const ItemApiException(this.message, {this.statusCode});
|
|
|
|
final String message;
|
|
final int? statusCode;
|
|
|
|
@override
|
|
String toString() => message;
|
|
}
|