Dio is a powerful HTTP client for Dart and Flutter, created by Chinese engineer Wenda Wang. The library provides an advanced API with support for interceptors, FormData, file uploads, and request cancellation. According to pub.dev, 2025, Dio is the most popular HTTP client in the Flutter ecosystem with over 8 thousand stars on GitHub.
Key Takeaways
Dio is a powerful HTTP client library for the Dart language, most widely used in Flutter applications. Dio provides a rich API with support for interceptors, global configuration, transformers, FormData, file uploads, and flexible timeout management, making it the primary choice for networking in the Flutter community.
The library was created by Wenda Wang in 2018 as an alternative to the built-in dart:io HttpClient, which lacked many modern features: unified configuration for all requests, interceptors, and automatic serialization. By 2025, Dio surpassed the http package from the Dart team in popularity, taking first place among HTTP clients in the Flutter ecosystem according to pub.dev.
Dio supports three adapters: DartNativeAdapter (default on Android, iOS, Desktop), BrowserAdapter (on Web), and IOAdapter. The adapter is automatically selected depending on the platform. Dio also provides a unified interface for all Flutter platforms — Android, iOS, Web, macOS, Windows, and Linux.
Dio's architecture is built on a handler chain. Each request passes through a sequence of interceptors that can modify the request (InterceptorsWrapper.onRequest), the response (onResponse), or handle an error (onError). After the interceptors, the request goes to transformers (Transformer), which transform the data before sending.
A Dio instance is configured through a BaseOptions object containing the base URL, default headers, timeouts, response type (JSON, stream, plain), query parameters, and data format. These settings apply to all requests but can be overridden in a specific request. BaseOptions provides a single configuration point for the entire application, simplifying endpoint changes or adding global headers.
Each Dio request returns Response<T>, where T is the data type after transformer processing. By default, Dio automatically converts JSON responses to Map<String, dynamic>. For typed responses, Dio is used together with serialization packages: json_serializable, freezed, or built_value. Response contains data, headers, statusCode, requestOptions, and extra data.
Basic configuration is created via Dio(BaseOptions). You can set a baseUrl for all requests, connectTimeout and receiveTimeout, content-type and accept headers, as well as queryParameters. All these parameters apply to every request, eliminating code duplication and centralizing network settings management.
Dio supports two serialization modes: JSON by default (responseType: ResponseType.json) and streaming (ResponseType.stream). In stream mode, Response.data returns a ResponseBody that can be read in chunks. This is convenient for large payload files where loading everything into memory is undesirable. The plain mode returns a raw string without automatic JSON parsing.
Interceptors are Dio's key mechanism for intercepting and modifying requests, responses, and errors. They fully replace OkHttp's Interceptor and Ktor's plugins, but with a Dart-specific API and async support via Future. Interceptors can be added both in the global Dio configuration and for individual requests.
| Interceptor Method | Purpose | Usage Example |
|---|---|---|
| onRequest | Modify request before sending | Adding an authorization token |
| onResponse | Handle successful response | Converting data to DTO objects |
| onError | Handle request error | Automatic retry on 503 |
The built-in LogInterceptor logs every request: method, URL, headers, body, and execution time. It has two modes: compact (one line per request) and full (complete information with body). LogInterceptor is especially useful during development, but it is recommended to disable it in release builds using conditional imports or a global flag.
Custom interceptors are created through the InterceptorsWrapper class. You can override one, two, or all three methods (onRequest, onResponse, onError). Dio executes interceptors strictly in the order they are added to the interceptors list. If an interceptor does not call handler.next(), the chain is interrupted, and the response/error does not reach the application.
For authentication in Dio, an interceptor is used that adds a Bearer token to the Authorization header. If the server returns 401, the interceptor in onError attempts to refresh the token via a refresh request and retries the original request with the new token. This pattern is called token refresh interceptor and is implemented through DioException by checking response?.statusCode == 401.
Dio provides built-in support for retry logic via the dio_smart_retry package or a custom RetryInterceptor. Retry is important for mobile applications: when the connection is lost for 2-3 seconds, Dio throws a DioException with type connectionTimeout or connectionError. RetryInterceptor catches this exception and retries the request up to 3 times with exponential backoff (1s, 2s, 4s), improving application reliability in unstable network conditions.
Let's look at a basic GET request using Dio. An instance is created with BaseOptions, setting the base URL and timeouts. The request is executed via the get() method, returning a Response with data in Map format.
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['login'])
For a POST request with a JSON body, a Map object or a custom DTO is passed. Dio automatically serializes the Map to JSON via jsonEncode. For typed DTOs, the queryParameters option, data field, or a custom Transformer is used.
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'])
A custom interceptor adds a Bearer token to every request. The onRequest method fires before sending, modifying the headers. On a 401 response, the interceptor can refresh the token and retry the request using the dio.fetch(requestOptions) method.
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 simplifies file uploads through FormData. To send a file, a MultipartFile is created from File, Bytes, or AssetBundle. FormData automatically sets the multipart/form-data header with the correct boundary and encoding. Dio supports upload progress via onSendProgress.
For file downloads, the download() method is used, which saves the data stream directly to a file. Dio supports resuming interrupted downloads via the Range header, which is especially useful for large files. Download progress is tracked through onReceiveProgress, allowing you to display a progress bar in the UI.
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('Upload: $progress%')
},
)
// File download
await dio.download(
'https://example.com/file.zip',
'/storage/emulated/0/Download/file.zip',
onReceiveProgress: (received, total) {
print('Download: ${received / total * 100}%')
},
)
Incorrect error handling is the most common issue. Dio throws a DioException (formerly DioError) for any problems: network unavailability, timeout, HTTP errors 4xx/5xx. Many developers only catch generic Exception, losing information about the error type and the ability to handle it specifically. Use DioException.type to determine the cause of the failure.
Ignoring CancelToken leads to request leaks. If a user leaves a screen while a request is still running, Dio wastes resources and may attempt to update a destroyed State. Always create a CancelToken for each request and cancel it in dispose(). CancelToken generates a DioException with type cancel, which must be handled properly.
Lack of retry logic for temporary failures. On mobile devices, the network is often briefly unavailable. Implement an interceptor with automatic request retry on timeout or 503/502 response. Use RetryInterceptor from the dio_smart_retry package or write a custom interceptor with exponential backoff between attempts.
Frequently Asked Questions
Dio provides interceptors, global BaseOptions configuration, FormData, upload progress, and CancelToken. The http package from the Dart team is minimalistic, without interceptors or global configuration. Dio is used in large projects, while http is used for simple scripts.
By default, Dio converts JSON to Map using jsonDecode. For typed serialization, use the json_serializable or freezed packages. Create a custom interceptor that converts response.data to DTO via fromJson() in onResponse.
Create a CancelToken and pass it in the request options. Calling token.cancel() interrupts the request and throws a DioException with type cancel. CancelToken supports canceling multiple requests simultaneously, which is convenient for canceling all requests when leaving a screen.
Yes, Dio works on all six Flutter platforms: Android, iOS, Web, macOS, Windows, and Linux. Each platform uses an adaptive HTTP client: DartNativeAdapter (native platforms) and BrowserAdapter (Web). A unified API for all platforms is a key advantage of Dio in Flutter projects.
Dio does not manage cookies automatically. For cookie support, use the dio_cookie_manager package together with cookie_jar. CookieManager intercepts Set-Cookie and Cookie headers and saves cookies in PersistCookieJar for automatic sending in subsequent requests to the same domain.
Summary
We will develop a mobile application turnkey
IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.
Read also