Dio là một HTTP client mạnh mẽ cho Dart và Flutter, được phát triển bởi kỹ sư người Trung Quốc Wenda Wang. Thư viện cung cấp API nâng cao với hỗ trợ interceptor, FormData, tải lên tệp và hủy yêu cầu. Theo pub.dev, 2025, Dio là HTTP client phổ biến nhất trong hệ sinh thái Flutter với hơn 8 nghìn sao trên GitHub.
Những điểm chính
Dio là một thư viện HTTP client mạnh mẽ cho ngôn ngữ Dart, được sử dụng rộng rãi nhất trong các ứng dụng Flutter. Dio cung cấp API phong phú với hỗ trợ interceptor, cấu hình toàn cục, transformer, FormData, tải lên tệp và quản lý thời gian chờ linh hoạt, khiến nó trở thành lựa chọn hàng đầu cho kết nối mạng trong cộng đồng Flutter.
Thư viện được tạo ra bởi Wenda Wang vào năm 2018 như một giải pháp thay thế cho HttpClient tích hợp sẵn của dart:io, vốn thiếu nhiều tính năng hiện đại: cấu hình thống nhất cho tất cả yêu cầu, interceptor và tuần tự hóa tự động. Đến năm 2025, Dio đã vượt qua gói http của nhóm Dart về mức độ phổ biến, chiếm vị trí đầu tiên trong số các HTTP client trong hệ sinh thái Flutter theo pub.dev.
Dio hỗ trợ ba bộ điều hợp: DartNativeAdapter (mặc định trên Android, iOS, Desktop), BrowserAdapter (trên Web) và IOAdapter. Bộ điều hợp được tự động chọn tùy theo nền tảng. Dio cũng cung cấp giao diện thống nhất cho tất cả nền tảng Flutter — Android, iOS, Web, macOS, Windows và Linux.
Kiến trúc của Dio được xây dựng trên chuỗi trình xử lý. Mỗi yêu cầu đi qua một chuỗi các interceptor có thể sửa đổi yêu cầu (InterceptorsWrapper.onRequest), phản hồi (onResponse) hoặc xử lý lỗi (onError). Sau các interceptor, yêu cầu đến transformer (Transformer), nơi chuyển đổi dữ liệu trước khi gửi.
Một phiên bản Dio được cấu hình thông qua đối tượng BaseOptions chứa URL cơ sở, tiêu đề mặc định, thời gian chờ, loại phản hồi (JSON, stream, plain), tham số truy vấn và định dạng dữ liệu. Các cài đặt này áp dụng cho tất cả yêu cầu nhưng có thể được ghi đè trong một yêu cầu cụ thể. BaseOptions cung cấp điểm cấu hình duy nhất cho toàn bộ ứng dụng, đơn giản hóa việc thay đổi điểm cuối hoặc thêm tiêu đề toàn cục.
Mỗi yêu cầu trong Dio trả về Response<T>, trong đó T là kiểu dữ liệu sau khi xử lý bởi transformer. Theo mặc định, Dio tự động chuyển đổi phản hồi JSON thành Map<String, dynamic>. Đối với phản hồi có kiểu, Dio được sử dụng cùng với các gói tuần tự hóa: json_serializable, freezed hoặc built_value. Response chứa data, headers, statusCode, requestOptions và dữ liệu bổ sung.
Cấu hình cơ bản được tạo qua Dio(BaseOptions). Có thể đặt baseUrl cho tất cả yêu cầu, connectTimeout và receiveTimeout, tiêu đề content-type và accept, cũng như queryParameters. Tất cả các tham số này áp dụng cho mọi yêu cầu, loại bỏ trùng lặp mã và tập trung quản lý cài đặt mạng.
Dio hỗ trợ hai chế độ tuần tự hóa: JSON mặc định (responseType: ResponseType.json) và luồng (ResponseType.stream). Ở chế độ luồng, Response.data trả về ResponseBody có thể đọc thành từng phần. Điều này thuận tiện cho các tệp tải trọng lớn khi không muốn tải mọi thứ vào bộ nhớ. Chế độ plain trả về chuỗi thô mà không phân tích JSON tự động.
Interceptor là cơ chế chính của Dio để chặn và sửa đổi yêu cầu, phản hồi và lỗi. Chúng thay thế hoàn toàn Interceptor của OkHttp và plugin của Ktor, nhưng với API dành riêng cho Dart và hỗ trợ bất đồng bộ qua Future. Interceptor có thể được thêm cả trong cấu hình toàn cục của Dio và cho các yêu cầu riêng lẻ.
| Phương thức interceptor | Mục đích | Ví dụ sử dụng |
|---|---|---|
| onRequest | Sửa đổi yêu cầu trước khi gửi | Thêm mã thông báo ủy quyền |
| onResponse | Xử lý phản hồi thành công | Chuyển đổi dữ liệu thành đối tượng DTO |
| onError | Xử lý lỗi yêu cầu | Tự động thử lại khi 503 |
LogInterceptor tích hợp ghi nhật ký mỗi yêu cầu: phương thức, URL, tiêu đề, nội dung và thời gian thực thi. Nó có hai chế độ: nhỏ gọn (một dòng mỗi yêu cầu) và đầy đủ (thông tin đầy đủ với nội dung). LogInterceptor đặc biệt hữu ích trong quá trình phát triển, nhưng nên tắt nó trong bản phát hành thông qua import có điều kiện hoặc cờ toàn cục.
Interceptor tùy chỉnh được tạo qua lớp InterceptorsWrapper. Có thể ghi đè một, hai hoặc cả ba phương thức (onRequest, onResponse, onError). Dio thực thi các interceptor theo đúng thứ tự chúng được thêm vào danh sách interceptor. Nếu một interceptor không gọi handler.next(), chuỗi bị gián đoạn và phản hồi hoặc lỗi không đến được ứng dụng.
Để xác thực trong Dio, một interceptor được sử dụng để thêm mã thông báo Bearer vào tiêu đề Authorization. Nếu máy chủ trả về 401, interceptor trong onError cố gắng làm mới mã thông báo qua yêu cầu làm mới và thử lại yêu cầu ban đầu với mã thông báo mới. Mẫu này được gọi là token refresh interceptor và được triển khai qua DioException bằng cách kiểm tra response?.statusCode == 401.
Dio cung cấp hỗ trợ tích hợp cho logic thử lại qua gói dio_smart_retry hoặc RetryInterceptor tùy chỉnh. Thử lại rất quan trọng đối với ứng dụng di động: khi kết nối bị mất trong 2-3 giây, Dio ném DioException với loại connectionTimeout hoặc connectionError. RetryInterceptor bắt ngoại lệ này và thử lại yêu cầu tối đa 3 lần với backoff hàm mũ (1s, 2s, 4s), cải thiện độ tin cậy của ứng dụng trong điều kiện mạng không ổn định.
Hãy xem yêu cầu GET cơ bản qua Dio. Một phiên bản được tạo với BaseOptions, đặt URL cơ sở và thời gian chờ. Yêu cầu được thực thi qua phương thức get(), trả về Response với dữ liệu ở định dạng Map.
final dio = Dio(BaseOptions(
baseUrl: 'https://api.github.com',
connectTimeout: Duration(seconds: 15),
receiveTimeout: Duration(seconds: 15),
headers: {
'Accept': 'application/vnd.github.v3+json',
},
))
final response = await dio.get('/users/octocat')
print(response.data['đăng nhập'])
Đối với yêu cầu POST với nội dung JSON, một đối tượng Map hoặc DTO tùy chỉnh được truyền vào. Dio tự động tuần tự hóa Map thành JSON qua jsonEncode. Đối với DTO có kiểu, sử dụng tùy chọn queryParameters, trường data hoặc Transformer tùy chỉnh.
final data = {
'name': 'my-project',
'description': 'Created via Dio',
'private': false,
}
final response = await dio.post(
'/user/repos',
data: data,
options: Options(
contentType: ContentType.json.value,
),
)
print(response.data['ID'])
Một interceptor tùy chỉnh thêm mã thông báo Bearer vào mỗi yêu cầu. Phương thức onRequest kích hoạt trước khi gửi, sửa đổi tiêu đề. Khi phản hồi 401, interceptor có thể làm mới mã thông báo và thử lại yêu cầu qua phương thức dio.fetch(requestOptions).
class AuthInterceptor extends InterceptorsWrapper {
final String token
AuthInterceptor(this.token)
@override
void onRequest(
RequestOptions options,
RequestInterceptorHandler handler,
) {
options.headers['Authorization'] = 'Bearer $token'
handler.next(options)
}
}
dio.interceptors.add(AuthInterceptor('ghp_abc123'))
Dio đơn giản hóa việc tải lên tệp qua FormData. Để gửi tệp, một MultipartFile được tạo từ File, Bytes hoặc AssetBundle. FormData tự động đặt tiêu đề multipart/form-data với ranh giới và mã hóa chính xác. Dio hỗ trợ tiến trình tải lên qua onSendProgress.
Để tải xuống tệp, phương thức download() được sử dụng, lưu luồng dữ liệu trực tiếp vào tệp. Dio hỗ trợ tiếp tục tải xuống bị gián đoạn qua tiêu đề Range, đặc biệt hữu ích cho các tệp lớn. Tiến trình tải xuống được theo dõi qua onReceiveProgress, cho phép hiển thị thanh tiến trình trong giao diện người dùng.
final formData = FormData.fromMap({
'file': await MultipartFile.fromFile(
'/path/to/photo.jpg',
filename: 'photo.jpg',
),
'description': 'Profile photo',
})
await dio.post(
'/upload',
data: formData,
onSendProgress: (sent, total) {
final progress = sent / total * 100
print('Tải lên: $progress%')
},
)
// Tải xuống tệp
await dio.download(
'https://example.com/file.zip',
'/storage/emulated/0/Download/file.zip',
onReceiveProgress: (received, total) {
print('Tải xuống: ${received / total * 100}%')
},
)
Xử lý lỗi không đúng là vấn đề phổ biến nhất. Dio ném DioException (trước đây là DioError) cho bất kỳ vấn đề nào: mất mạng, hết thời gian chờ, lỗi HTTP 4xx/5xx. Nhiều nhà phát triển chỉ bắt Exception chung, mất thông tin về loại lỗi và khả năng xử lý cụ thể. Sử dụng DioException.type để xác định nguyên nhân thất bại.
Bỏ qua CancelToken dẫn đến rò rỉ yêu cầu. Nếu người dùng rời khỏi màn hình trong khi yêu cầu vẫn đang chạy, Dio lãng phí tài nguyên và có thể cố gắng cập nhật State đã bị hủy. Luôn tạo CancelToken cho mỗi yêu cầu và hủy nó trong dispose(). CancelToken tạo DioException với loại cancel, cần được xử lý đúng cách.
Thiếu logic thử lại cho lỗi tạm thời. Trên thiết bị di động, mạng thường không khả dụng trong thời gian ngắn. Triển khai interceptor với tự động thử lại yêu cầu khi hết thời gian chờ hoặc phản hồi 503/502. Sử dụng RetryInterceptor từ gói dio_smart_retry hoặc viết interceptor tùy chỉnh với backoff hàm mũ giữa các lần thử.
Câu hỏi thường gặp
Dio cung cấp interceptor, cấu hình BaseOptions toàn cục, FormData, tiến trình tải lên và CancelToken. Gói http từ nhóm Dart tối giản, không có interceptor hay cấu hình toàn cục. Dio được sử dụng trong các dự án lớn, trong khi http được dùng cho các tập lệnh đơn giản.
Theo mặc định, Dio chuyển đổi JSON thành Map bằng jsonDecode. Để tuần tự hóa có kiểu, sử dụng các gói json_serializable hoặc freezed. Tạo interceptor tùy chỉnh chuyển đổi response.data thành DTO qua fromJson() trong onResponse.
Tạo CancelToken và truyền nó vào tùy chọn yêu cầu. Gọi token.cancel() sẽ ngắt yêu cầu và ném DioException với loại cancel. CancelToken hỗ trợ hủy nhiều yêu cầu cùng lúc, thuận tiện để hủy tất cả yêu cầu khi rời khỏi màn hình.
Có, Dio hoạt động trên tất cả sáu nền tảng Flutter: Android, iOS, Web, macOS, Windows và Linux. Mỗi nền tảng sử dụng HTTP client thích ứng: DartNativeAdapter (nền tảng gốc) và BrowserAdapter (Web). API thống nhất cho tất cả nền tảng là lợi thế chính của Dio trong các dự án Flutter.
Dio không tự động quản lý cookie. Để hỗ trợ cookie, sử dụng gói dio_cookie_manager cùng với cookie_jar. CookieManager chặn các tiêu đề Set-Cookie và Cookie và lưu cookie vào PersistCookieJar để tự động gửi trong các yêu cầu tiếp theo đến cùng miền.
Tổng kết
Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay
IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.
Đọc thêm