Using the Dio Package for More Advanced Networking Features Like Interceptors and File Downloads in Flutter

Flutter developers often rely on networking packages to communicate with web services and APIs. While the built-in http package suffices for simple tasks, the dio package provides more advanced features that are crucial for robust and scalable applications. These features include interceptors for request and response handling and powerful file download capabilities. This blog post explores using the dio package to enhance your Flutter apps.

Introduction to the Dio Package

Dio is a powerful HTTP client for Dart, which supports interceptors, global configuration, FormData, request cancellation, timeout, and file downloading, making it ideal for building networked applications in Flutter. Its versatility and ease of use make it a favorite among Flutter developers.

Why Use Dio Over the http Package?

Compared to the http package, dio offers several advantages:

  • Interceptors: Ability to intercept and modify HTTP requests and responses globally.
  • File Downloads: Streamlined and efficient file downloading.
  • Request Cancellation: Ability to cancel pending requests.
  • Global Configuration: Centralized configuration for base URLs, headers, and timeouts.
  • FormData Support: Easy handling of form data.

Setting Up Dio in Your Flutter Project

First, add the dio dependency to your pubspec.yaml file:

dependencies:
  flutter:
    sdk: flutter
  dio: ^5.4.0 # Use the latest version

Then, run flutter pub get to install the package.

Basic Usage of Dio

Here’s a simple example to illustrate how to make a GET request with dio:

import 'package:dio/dio.dart';

void main() async {
  final dio = Dio();

  try {
    final response = await dio.get('https://jsonplaceholder.typicode.com/todos/1');
    print('Response data: ${response.data}');
  } catch (e) {
    print('Error: $e');
  }
}

This code creates a new Dio instance, makes a GET request to a sample API, and prints the response data or any error that occurs.

Implementing Interceptors

Interceptors in dio are classes that can intercept requests and responses. This is particularly useful for adding headers, logging requests, or handling errors globally.

Creating a Custom Interceptor

Here’s how to create a custom interceptor that logs each request and response:

import 'package:dio/dio.dart';

class LoggingInterceptor extends Interceptor {
  @override
  void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
    print('REQUEST[${options.method}] => PATH: ${options.path}');
    return super.onRequest(options, handler);
  }

  @override
  void onResponse(Response response, ResponseInterceptorHandler handler) {
    print(
        'RESPONSE[${response.statusCode}] => PATH: ${response.requestOptions.path}');
    return super.onResponse(response, handler);
  }

  @override
  void onError(DioException err, ErrorInterceptorHandler handler) {
    print(
        'ERROR[${err.response?.statusCode}] => PATH: ${err.requestOptions.path}');
    return super.onError(err, handler);
  }
}

Adding the Interceptor to Dio

To add the interceptor to your Dio instance, use the interceptors.add method:

void main() async {
  final dio = Dio();
  dio.interceptors.add(LoggingInterceptor());

  try {
    final response = await dio.get('https://jsonplaceholder.typicode.com/todos/1');
    print('Response data: ${response.data}');
  } catch (e) {
    print('Error: $e');
  }
}

Now, every request and response will be logged to the console, which aids in debugging and monitoring.

Handling Authentication with Interceptors

Interceptors can also manage authentication by adding an authorization header to each request. For example, let’s assume you need to add a token to the header:

class AuthInterceptor extends Interceptor {
  final String token;

  AuthInterceptor(this.token);

  @override
  void onRequest(RequestOptions options, RequestInterceptorHandler handler) {
    options.headers['Authorization'] = 'Bearer $token';
    return super.onRequest(options, handler);
  }
}

void main() async {
  final dio = Dio();
  final token = 'your_auth_token';
  dio.interceptors.add(AuthInterceptor(token));

  try {
    final response = await dio.get('https://api.example.com/data');
    print('Response data: ${response.data}');
  } catch (e) {
    print('Error: $e');
  }
}

This interceptor adds the Authorization header to each request, ensuring that the application authenticates properly.

File Downloading with Dio

The dio package simplifies file downloading, allowing you to save files directly to the device.

Basic File Download

Here’s a simple example of how to download a file using dio:

import 'dart:io';
import 'package:dio/dio.dart';
import 'package:path_provider/path_provider.dart';

void main() async {
  final dio = Dio();
  try {
    final dir = await getApplicationDocumentsDirectory();
    final path = '${dir.path}/example.pdf';
    await dio.download(
      'http://www.africau.edu/images/default/sample.pdf',
      path,
      onReceiveProgress: (received, total) {
        if (total != -1) {
          print('${(received / total * 100).toStringAsFixed(0)}%');
        }
      },
    );
    print('File downloaded to: $path');
  } catch (e) {
    print('Error: $e');
  }
}

Explanation:

  • First, it retrieves the application documents directory using path_provider.
  • It constructs the full file path.
  • The dio.download method is used to download the file, taking the URL and the destination path as arguments.
  • The onReceiveProgress callback provides progress updates during the download.

Advanced File Download

You might want to handle exceptions and implement more advanced features, like resuming downloads or verifying file integrity. Here’s an enhanced example:

import 'dart:io';
import 'package:dio/dio.dart';
import 'package:path_provider/path_provider.dart';

void downloadFile(String url, String fileName) async {
  final dio = Dio();
  try {
    final dir = await getApplicationDocumentsDirectory();
    final filePath = '${dir.path}/$fileName';

    final response = await dio.get(
      url,
      onReceiveProgress: (received, total) {
        if (total != -1) {
          print('Downloading: ${(received / total * 100).toStringAsFixed(0)}%');
        }
      },
      options: Options(
        responseType: ResponseType.bytes,
        followRedirects: false,
        validateStatus: (status) {
          return status != null && status < 500;
        },
      ),
    );

    final file = File(filePath);
    final raf = file.openSync(mode: FileMode.write);
    raf.writeFromSync(response.data);
    await raf.close();

    print('File downloaded to $filePath');
  } catch (e) {
    print('Error downloading file: $e');
  }
}

void main() {
  downloadFile(
      'http://www.africau.edu/images/default/sample.pdf', 'sample.pdf');
}

This improved version includes exception handling, custom validation, and direct writing to a file using File and RandomAccessFile, which offers better performance.

FormData with Dio

FormData is useful for sending multipart/form-data requests, such as when uploading files to a server.

import 'dart:io';
import 'package:dio/dio.dart';

void main() async {
  final dio = Dio();
  final formData = FormData.fromMap({
    'name': 'John Doe',
    'age': 30,
    'file': await MultipartFile.fromFile(
      'path/to/your/file.jpg',
      filename: 'avatar.jpg',
    ),
  });

  try {
    final response = await dio.post(
      'https://api.example.com/upload',
      data: formData,
    );
    print('Response data: ${response.data}');
  } catch (e) {
    print('Error: $e');
  }
}

In this example:

  • It creates a FormData object and adds text fields (name, age) and a file field (file).
  • It then posts the FormData to an upload endpoint.

Conclusion

The dio package is an excellent choice for Flutter developers who need more advanced networking features than the basic http package provides. With features like interceptors and file downloading, dio offers the flexibility and power needed to build robust and scalable Flutter applications. Whether you need to add authentication headers, log requests, or efficiently download files, dio has you covered.