8.2 KiB
8.2 KiB
API Integration - Quick Reference Card
Essential Files
| File | Purpose | Key Methods |
|---|---|---|
api_constants.dart |
API config | baseUrl, endpoints, timeouts |
dio_client.dart |
HTTP client | get(), post(), put(), delete() |
network_info.dart |
Connectivity | isConnected, onConnectivityChanged |
exceptions.dart |
Error types | 20+ exception classes |
failures.dart |
Domain errors | Failure classes for UI |
product_remote_datasource.dart |
Product API | fetchProducts(), searchProducts() |
category_remote_datasource.dart |
Category API | fetchCategories() |
Quick Setup (5 Steps)
// 1. In main.dart - Initialize dependencies
import 'core/di/injection_container.dart' as di;
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await di.initDependencies();
runApp(const MyApp());
}
// 2. Configure API URL in api_constants.dart
static const String baseUrl = 'https://your-api.com';
// 3. Enable mock data for testing (optional)
static const bool useMockData = true;
// 4. Get data source from service locator
final productDS = sl<ProductRemoteDataSource>();
// 5. Fetch data
final products = await productDS.fetchProducts();
Common Operations
Fetch All Products
final dataSource = sl<ProductRemoteDataSource>();
final products = await dataSource.fetchProducts();
Search Products
final results = await dataSource.searchProducts('laptop');
Fetch by Category
final products = await dataSource.fetchProductsByCategory('electronics');
Fetch Single Product
final product = await dataSource.fetchProductById('123');
Fetch Categories
final categoryDS = sl<CategoryRemoteDataSource>();
final categories = await categoryDS.fetchCategories();
Check Connectivity
final networkInfo = sl<NetworkInfo>();
final isConnected = await networkInfo.isConnected;
Error Handling Patterns
Basic Try-Catch
try {
final products = await dataSource.fetchProducts();
// Use products
} on NoInternetException {
// Show "No internet" message
} on ServerException catch (e) {
// Show "Server error: ${e.message}"
} on NetworkException catch (e) {
// Show "Network error: ${e.message}"
}
With Network Check
final networkInfo = sl<NetworkInfo>();
if (!await networkInfo.isConnected) {
// Load from cache or show offline UI
return;
}
try {
final products = await dataSource.fetchProducts();
// Use products
} catch (e) {
// Handle error
}
Repository Pattern (Recommended)
Future<Either<Failure, List<Product>>> getProducts() async {
if (!await networkInfo.isConnected) {
return Left(NoInternetFailure());
}
try {
final products = await remoteDataSource.fetchProducts();
return Right(products);
} on ServerException catch (e) {
return Left(ServerFailure(e.message, e.statusCode));
} on NetworkException catch (e) {
return Left(NetworkFailure(e.message));
}
}
Exception → Failure Mapping
| Exception | Failure | UI Message |
|---|---|---|
NoInternetException |
NoInternetFailure |
"No internet connection" |
TimeoutException |
TimeoutFailure |
"Request timeout" |
ServerException (500+) |
ServerFailure |
"Server error" |
UnauthorizedException (401) |
UnauthorizedFailure |
"Please login" |
NotFoundException (404) |
NotFoundFailure |
"Not found" |
ValidationException (422) |
ValidationFailure |
"Invalid data" |
Configuration Options
API Constants
// In api_constants.dart
// Base URL
static const String baseUrl = 'https://api.example.com';
// Timeouts (milliseconds)
static const int connectTimeout = 30000;
static const int receiveTimeout = 30000;
// Retry
static const int maxRetries = 3;
static const int retryDelay = 1000;
// Mock data toggle
static const bool useMockData = false;
DioClient Headers
final dioClient = DioClient();
// Add auth token
dioClient.updateAuthToken('your-jwt-token');
// Add custom header
dioClient.addHeader('X-Custom', 'value');
// Remove auth token
dioClient.removeAuthToken();
Riverpod Integration
Provider Setup
@riverpod
class Products extends _$Products {
@override
Future<List<ProductModel>> build() async {
final dataSource = sl<ProductRemoteDataSource>();
return await dataSource.fetchProducts();
}
Future<void> refresh() async {
state = const AsyncValue.loading();
state = await AsyncValue.guard(() async {
final dataSource = sl<ProductRemoteDataSource>();
return await dataSource.fetchProducts();
});
}
}
UI Usage
Consumer(
builder: (context, ref, child) {
final productsAsync = ref.watch(productsProvider);
return productsAsync.when(
data: (products) => ProductList(products),
loading: () => CircularProgressIndicator(),
error: (error, stack) => ErrorWidget(error),
);
},
)
API Endpoints Reference
Products
| Method | Endpoint | Purpose |
|---|---|---|
| GET | /products |
Get all products |
| GET | /products/:id |
Get single product |
| GET | /products/category/:id |
Get by category |
| GET | /products/search?q=query |
Search products |
| POST | /products/sync |
Bulk sync |
Categories
| Method | Endpoint | Purpose |
|---|---|---|
| GET | /categories |
Get all categories |
| GET | /categories/:id |
Get single category |
| POST | /categories/sync |
Bulk sync |
Expected Response Formats
Products List
{
"products": [
{
"id": "1",
"name": "Product Name",
"price": 29.99,
"categoryId": "cat1",
"stockQuantity": 100,
"isAvailable": true,
"createdAt": "2024-01-01T00:00:00Z",
"updatedAt": "2024-01-01T00:00:00Z"
}
]
}
Categories List
{
"categories": [
{
"id": "1",
"name": "Electronics",
"productCount": 25,
"createdAt": "2024-01-01T00:00:00Z"
}
]
}
Error Response
{
"message": "Error message",
"error": "Detailed error"
}
Testing Quick Start
Use Mock Data Source
// In api_constants.dart
static const bool useMockData = true;
// Or directly
final mockDS = ProductRemoteDataSourceMock();
final products = await mockDS.fetchProducts();
Mock Network Status
final mockNetwork = NetworkInfoMock(isConnected: false);
final isConnected = await mockNetwork.isConnected; // false
Common Issues & Solutions
| Issue | Solution |
|---|---|
| Connection timeout | Increase timeout in api_constants.dart |
| SSL certificate error | Configure badCertificateCallback (dev only) |
| 401 Unauthorized | Update auth token with updateAuthToken() |
| Mock data not working | Check useMockData = true |
| Parsing error | Verify response format matches model |
Performance Tips
- Enable caching: Use Hive to cache API responses
- Pagination: Request data in chunks (20-50 items)
- Debounce search: Wait 300ms before searching
- Image optimization: Use
cached_network_image - Background sync: Sync during app idle time
- Cancel requests: Use
CancelTokenfor expensive operations
Debug Logging
Logs are automatically generated:
REQUEST[GET] => PATH: /products
Headers: {Content-Type: application/json}
RESPONSE[200] => PATH: /products
Data: {...}
Disable in production by removing LoggingInterceptor.
Security Checklist
- Use HTTPS (not HTTP)
- Store API keys securely (use
flutter_secure_storage) - Implement token refresh for expired tokens
- Validate SSL certificates in production
- Don't log sensitive data in production
- Use environment variables for API URLs
- Implement rate limiting on client side
Next Steps
- ✅ API integration complete
- → Create Repository layer
- → Implement Use Cases
- → Wire up Riverpod Providers
- → Build UI with error handling
- → Add offline sync
- → Implement authentication
- → Add unit tests
Quick Links:
- Full Guide:
API_INTEGRATION_GUIDE.md - Architecture:
API_ARCHITECTURE.md - Examples:
examples/api_usage_example.dart
Status: ✅ Ready to Use