Introduction
A well‑structured API client is essential for any Flutter app that communicates with a backend. GetX provides the tools to build a clean, maintainable REST API layer: dependency injection, reactive state, and lifecycle management. This guide covers how to design an API client using the repository pattern, integrate it with GetX controllers, handle errors, and add interceptors for authentication and logging.
- Choosing an HTTP Client
You can use the built‑in http package for simple needs, or dio for advanced features (interceptors, request cancellation, form data). This guide uses dio because it’s more flexible for production apps. Add the dependencies to your pubspec.yaml:
- Defining Data Models
Create model classes for your API responses. Use json_serializable or manual fromJson/toJson methods. Example:
- Creating the API Client Service
Encapsulate Dio setup in a service that extends GetxService. This service will be injected into repositories and can be made permanent. Add interceptors for logging, authentication, etc.
- Repository Pattern
Repositories abstract the data source (API, local database) and provide a clean interface for controllers. They depend on the ApiService and can be injected via GetX DI.
- Controller with Reactive State
The controller uses the repository and exposes reactive state (loading, data, error). Use StateMixin for simplicity.
- Dependency Injection with Bindings
Use bindings to register the service, repository, and controller. This ensures lazy loading and proper disposal.
- UI with Obx/obx
The UI uses the controller's state to show loading, error, or data.
- Adding Authentication Token
If your API requires authentication, you can store the token (e.g., in GetStorage) and add it to Dio interceptors. Example:
- Error Handling & Retry
For a more robust solution, you can implement retry logic on certain errors (e.g., network issues, 5xx). Use dio's RetryInterceptor or a custom one.
Best Practices
- Use a service layer for Dio – Centralizes base URL, timeouts, and interceptors.
- Implement repository pattern – Keeps API logic separate from controllers, making testing easier.
- Use
StateMixin– Reduces boilerplate for loading/error/success states. - Handle errors gracefully – Catch specific exceptions and provide user‑friendly messages.
- Add interceptors for auth and logging – Keeps request/response handling clean.
- Test the repository with mocks – Use
Mockitoto test API calls without making real network requests. - Cancel requests when needed – Use
CancelTokenfor long‑running or repetitive requests.
Common Mistakes
- ❌ Putting API calls directly in controllers – Makes testing hard and mixes concerns. ✅ Use repositories.
- ❌ Not handling Dio exceptions – May cause crashes or unhelpful errors.
✅ Catch
DioExceptionand convert to meaningful messages. - ❌ Forgetting to add interceptors for auth – Each request must include the token; centralise it.
- ❌ Not cancelling streams/subscriptions – Controllers may live beyond the view; cancel in
onClose.
FAQ
- Q: Should I use
httpordio?
A:diois more feature‑rich (interceptors, cancellation, progress, form data). For simple needs,httpis fine, butdioscales better. - Q: How to handle token refresh?
A: You can add an interceptor that checks for 401 responses, refreshes the token, and retries the original request. Usedio'sQueueInterceptoror a custom solution. - Q: How to test the repository?
A: Mock theApiServiceusingMockitoand test that the repository calls the correct endpoints and handles responses correctly. - Q: Can I use this architecture with
GetxService?
A: Yes,ApiServiceextendsGetxServiceto keep it alive across the app. Repositories and controllers areGetxControllerand are disposed when not needed. - Q: How to show a loading indicator for individual API calls?
A: Use separate reactive flags (e.g.,isLoading) or split into smaller controllers.StateMixinalready provides a global loading state; for per‑action loading, use additional booleans.
Conclusion
A well‑architected REST API client using GetX gives you clean separation of concerns, testability, and maintainability. By using services, repositories, and controllers, you can build robust networking layers that integrate seamlessly with GetX's reactive state and dependency injection.