This repository is focused on learning and implementing the Dio package in Flutter/Dart for handling HTTP requests, API communication, interceptors, CRUD operations, and clean API architecture.
Learned how to add and configure the Dio package and create a centralized Dio instance for API communication.
Key concepts:
Dio()BaseOptionsbaseUrlheadersconnectTimeoutreceiveTimeout- Request/response configuration
- Centralized API configuration
Learned how to send API requests and handle different types of responses.
final response = await dio.get('/users');final response = await dio.post(
'/users',
data: {
'name': 'Radhe',
'email': 'radhe@example.com',
},
);final response = await dio.put(
'/users/1',
data: {
'name': 'Updated Name',
},
);final response = await dio.patch(
'/users/1',
data: {
'name': 'Updated Name',
},
);final response = await dio.delete('/users/1');Learned how to work with API responses returned as:
ListMap- Nested
ListandMap - Raw JSON
- JSON converted into models
Example:
final response = await dio.get('/users');
final data = response.data;For a list response:
List data = response.data;For a map response:
Map<String, dynamic> data = response.data;Learned how to convert API JSON responses into Dart models instead of working with raw maps everywhere.
Example:
final user = UserModel.fromJson(response.data);For multiple objects:
final users = (response.data as List)
.map((json) => UserModel.fromJson(json))
.toList();This makes the application more structured, readable, and type-safe.
Implemented and understood the complete CRUD flow using Dio.
| Operation | HTTP Method | Purpose |
|---|---|---|
| Create | POST |
Create new data |
| Read | GET |
Fetch data |
| Update | PUT |
Replace/update complete data |
| Partial Update | PATCH |
Update specific fields |
| Delete | DELETE |
Remove data |
Learned how to send headers with API requests.
Example:
Options(
headers: {
'Authorization': 'Bearer token',
'Content-Type': 'application/json',
},
)Headers can be configured globally or provided for individual requests.
Learned how to configure a common baseUrl so API endpoints do not need to repeat the complete URL.
BaseOptions(
baseUrl: 'https://example.com/api',
)Then requests become:
dio.get('/users');instead of:
dio.get('https://example.com/api/users');Learned how to use Options and BaseOptions to configure requests.
Important options include:
baseUrlheaderscontentTyperesponseTypesendTimeoutreceiveTimeoutconnectTimeout
Example:
Options(
headers: {
'Authorization': 'Bearer token',
},
contentType: 'application/json',
)One of the most important things learned in this repository is Dio Interceptors.
Interceptors allow us to execute logic before a request is sent, after a response is received, or when an error occurs.
There are three major interceptor callbacks:
onRequest
onResponse
onErrorUsed to inspect or modify a request before it is sent to the server.
Example:
onRequest: (options, handler) {
print('Request URL: ${options.uri}');
print('Request Method: ${options.method}');
print('Request Headers: ${options.headers}');
print('Request Data: ${options.data}');
handler.next(options);
}This is useful for debugging and adding things such as authentication tokens.
Used to inspect the response received from the server.
onResponse: (response, handler) {
print('Response Status: ${response.statusCode}');
print('Response Data: ${response.data}');
handler.next(response);
}Used to handle and debug API errors.
onError: (error, handler) {
print('Error: ${error.message}');
print('Status Code: ${error.response?.statusCode}');
print('Response: ${error.response?.data}');
handler.next(error);
}Created a centralized file such as:
mydio.dart
or
dio_injection_container.dart
The purpose of this file is to create and configure the Dio instance in one place.
Instead of configuring Dio repeatedly throughout the application, all common configuration is handled centrally.
My Dio configuration handles:
- Dio initialization
- Base URL
- Headers
- Request configuration
- Response configuration
- Error handling
- Interceptors
- API debugging
- Common options
Example structure:
final dio = Dio(
BaseOptions(
baseUrl: 'https://example.com/api',
headers: {
'Content-Type': 'application/json',
},
),
);
dio.interceptors.add(
InterceptorsWrapper(
onRequest: (options, handler) {
print('REQUEST: ${options.method} ${options.uri}');
print('HEADERS: ${options.headers}');
print('DATA: ${options.data}');
handler.next(options);
},
onResponse: (response, handler) {
print('RESPONSE: ${response.statusCode}');
print('DATA: ${response.data}');
handler.next(response);
},
onError: (error, handler) {
print('ERROR: ${error.message}');
print('STATUS: ${error.response?.statusCode}');
print('DATA: ${error.response?.data}');
handler.next(error);
},
),
);This makes API debugging much easier because I can see what is going through the API at a central location.
I also learned how to use Dio inside a Clean Architecture structure instead of putting API logic directly inside UI/screens.
A typical structure used in the project:
lib/
│
├── core/
│ └── network/
│ ├── mydio.dart
│ └── dio_injection_container.dart
│
├── data/
│ ├── models/
│ ├── datasources/
│ └── repositories/
│
├── domain/
│ ├── entities/
│ ├── repositories/
│ └── usecases/
│
└── presentation/
├── pages/
└── widgets/
The main idea is to keep:
UI → UseCase → Repository → DataSource → Dio → API
This keeps API communication separated from the presentation layer.
A major focus of this repository was making API debugging easier.
For every request, I can inspect things such as:
========== REQUEST ==========
METHOD: GET
URL: https://example.com/api/users
HEADERS: {...}
DATA: null
========== RESPONSE ==========
STATUS: 200
DATA: [...]
========== ERROR ==========
STATUS: 404
MESSAGE: Not Found
DATA: {...}
This makes it much easier to identify:
- Wrong endpoint
- Wrong HTTP method
- Missing headers
- Incorrect request body
- Authentication problems
- Server errors
- Unexpected response data
- Parsing/model issues
GET → Fetch data
POST → Create data
PUT → Update/replace data
PATCH → Partially update data
DELETE → Delete data
Through this repository, I learned how to use Dio beyond simply making API requests.
I learned how to:
- Configure Dio
- Create a reusable Dio instance
- Configure
BaseOptions - Configure
baseUrl - Manage headers
- Use request/response options
- Perform GET, POST, PUT, PATCH and DELETE requests
- Handle List and Map JSON responses
- Convert JSON into Dart models
- Use interceptors
- Handle request, response and error interceptors
- Debug API communication centrally
- Structure networking code using Clean Architecture
- Separate API/data logic from presentation logic
The main goal of this repository was to understand how production-style API communication can be organized in Flutter using Dio and Clean Architecture, rather than just learning individual HTTP methods.