DEV Community

Mambaur Roziq
Mambaur Roziq

Posted on

Dio vs HTTP in Flutter: A Practical, Clear Comparison

When building Flutter applications that interact with APIs, two of the most commonly used packages are http and dio. Both allow you to perform HTTP requests and handle responses, but they serve different needs and offer different levels of flexibility and scalability depending on the complexity of your application.

This article will help you understand why you may want to use dio over http in Flutter, what benefits dio brings to real-world applications, and what trade-offs you should consider.

Understanding http in Flutter

The http package is a lightweight and minimalistic HTTP client for Flutter and Dart. It is straightforward to use for making GET, POST, PUT, and DELETE requests. However, it is a low-level package, providing only the basic building blocks for network communication without advanced features.

For example, fetching data from an API using http looks like this:

final response = await http.get(Uri.parse('https://api.example.com/data'));
if (response.statusCode == 200) {
  final data = jsonDecode(response.body);
  print(data);
}
Enter fullscreen mode Exit fullscreen mode

You must manually handle JSON decoding, error management, and token handling. While this is acceptable for small, simple applications, it can lead to repetitive code and higher maintenance in more complex projects.

Understanding dio in Flutter

dio is a feature-rich HTTP client designed for scalability and ease of use in real-world Flutter applications. It comes with advanced capabilities out of the box, including automatic JSON decoding, request and response interceptors, file uploads and downloads with progress reporting, request cancellation, and flexible configuration options like base URLs and global headers.

Fetching the same data with dio looks like this:

final dio = Dio();
final response = await dio.get('https://api.example.com/data');
print(response.data);
Enter fullscreen mode Exit fullscreen mode

Here, dio automatically decodes the JSON response, simplifying your code and reducing the risk of repetitive parsing logic scattered across your project.

Key Benefits of Using dio Over http

Automatic JSON Decoding

With http, you are responsible for decoding JSON from the response body manually using jsonDecode. dio, on the other hand, automatically decodes JSON responses and makes the data directly accessible, reducing boilerplate and potential decoding errors throughout your codebase.

import 'package:dio/dio.dart';

final dio = Dio();
final response = await dio.get('https://jsonplaceholder.typicode.com/posts/1');

print(response.data['title']);
Enter fullscreen mode Exit fullscreen mode

Global Configuration Support

In dio, you can configure a base URL, default headers, connection and receive timeouts, and other settings globally. This removes the need to configure these settings in every request, ensuring consistency and reducing duplication in your networking code.

final dio = Dio(BaseOptions(
  baseUrl: 'https://api.example.com',
  connectTimeout: Duration(seconds: 10),
  headers: {'Authorization': 'Bearer YOUR_TOKEN'},
));
Enter fullscreen mode Exit fullscreen mode

With http, such configurations need to be handled manually on each request, increasing boilerplate and potential inconsistencies in headers or timeouts across your requests.

Interceptors for Request and Response Handling

dio offers interceptors, which allow you to inspect, modify, or handle requests, responses, and errors globally before they reach your app logic. This is particularly useful for adding authentication tokens automatically, logging requests and responses for debugging, or handling specific status codes (such as redirecting users to a login page on 401 Unauthorized errors) without duplicating code.

While http does not have built-in support for interceptors, achieving similar functionality requires wrapping your request logic in custom methods, which increases complexity and reduces clarity.

final dio = Dio();

dio.interceptors.add(InterceptorsWrapper(
  onRequest: (options, handler) {
    options.headers['Authorization'] = 'Bearer token123';
    print('Requesting: ${options.uri}');
    return handler.next(options);
  },
  onResponse: (response, handler) {
    print('Received: ${response.statusCode}');
    return handler.next(response);
  },
  onError: (error, handler) {
    print('Error: ${error.message}');
    return handler.next(error);
  },
));

await dio.get('https://jsonplaceholder.typicode.com/posts/1');
Enter fullscreen mode Exit fullscreen mode

Built-in Support for File Uploads

Uploading files with dio is straightforward using its FormData API, which simplifies multipart form uploads:

FormData formData = FormData.fromMap({
  'file': await MultipartFile.fromFile(file.path),
});
await dio.post('/upload', data: formData);
Enter fullscreen mode Exit fullscreen mode

In contrast, using http for file uploads requires managing MultipartRequest, which is more verbose and less intuitive, especially when handling multiple files or fields.

Downloading Files with Progress Reporting

In applications where you need to download files and display download progress to users, dio offers built-in support for tracking progress during downloads. You can easily update progress indicators in your UI using the progress callback provided by dio.

Using http for downloading files requires handling streams manually, which can be complex and error-prone for beginners and increases the amount of code you need to maintain.

await dio.download(
  'https://speed.hetzner.de/100MB.bin',
  '/local/path/100MB.bin',
  onReceiveProgress: (received, total) {
    if (total != -1) {
      print('${(received / total * 100).toStringAsFixed(0)}% downloaded');
    }
  },
);
Enter fullscreen mode Exit fullscreen mode

Request Cancellation

dio allows you to cancel an ongoing request using a CancelToken. This is particularly useful in Flutter apps where a user may navigate away from a screen before a request completes, allowing you to save resources and prevent unnecessary UI updates.

In contrast, http does not support canceling requests once they have been initiated, which can lead to wasted network calls and memory usage in your app.

CancelToken cancelToken = CancelToken();

dio.get(
  'https://jsonplaceholder.typicode.com/posts',
  cancelToken: cancelToken,
);

// To cancel:
cancelToken.cancel('Request cancelled by user.');
Enter fullscreen mode Exit fullscreen mode

Error Handling with Structured Information

dio provides a structured DioError object that gives you detailed information about what went wrong in your request, including the error type, the request that caused the error, and any available response data. This enables more effective error handling and user feedback in your app.

http provides basic exception handling, and you often need to parse error responses manually to extract meaningful error information.

try {
  await dio.get('https://api.example.com/data');
} on DioError catch (e) {
  if (e.type == DioExceptionType.connectionTimeout) {
    print('Connection timed out');
  } else if (e.response?.statusCode == 404) {
    print('Data not found');
  } else {
    print('Other error: ${e.message}');
  }
}
Enter fullscreen mode Exit fullscreen mode

Performance Considerations

The http package is slightly faster than dio in raw network calls because it has fewer abstractions and less overhead. However, in real-world applications, this difference is negligible, as the network latency itself dominates the total request time.

The trade-off of slightly lower raw performance with dio is often acceptable considering the significant gains in developer productivity, code maintainability, and scalability for larger applications.

When Should You Use http?

  • When you are building a small app with minimal API requests.
  • When you want a lightweight dependency with the smallest possible app size.
  • When you do not need advanced features like file uploads, downloads, interceptors, or cancellation.

When Should You Use dio?

  • When your app requires user authentication and token management across many requests.
  • When your app involves file uploads or downloads with progress reporting.
  • When you need structured, centralized error handling.
  • When you want scalable, clean network architecture for a project that will grow over time.
  • When you prefer automatic JSON decoding and global configuration to reduce repetitive code.

Final Thoughts

Choosing between http and dio in Flutter depends on your application's current and future needs. While http is an excellent choice for simple applications that only require a few API requests, dio provides a powerful, scalable, and developer-friendly solution for apps that require advanced networking capabilities and clean architecture.

If you are building a production-ready Flutter application that needs to handle complex API interactions, token management, file uploads, downloads with progress, or centralized error handling, using dio will make your code cleaner, easier to maintain, and more scalable as your app grows.

If you would like, I can also prepare a ready-to-use dio_service.dart structure to help you set up a clean, scalable networking layer in your Flutter project.

Top comments (0)