Using Dio to fetch data in Flutter

Kirtan Dudhat
14 min readFeb 19, 2024

--

Dio Flutter is a powerful HTTP client library for Dart and Flutter. It is developed by Flutter’s JianyingLi and is essential for making HTTP requests in applications. Beyond the basic functionalities of Dart’s http package, Dio offers added features like interceptors, multipart requests, and request cancellation, crucial for handling network requests and edge cases. This article delves into using Dio in Flutter for robust network interactions, covering its intuitive API, ease in handling REST API requests, file downloads, and advanced network tasks like progress notifications and timeout handling.

Installation

To use the Dio flutter package in your Flutter application, you’ll need to add it to your pubspec. yaml file:

dependencies:
dio: ^(latest_version)

You can also use the command to add dio as a dependency with the latest stable version:

$ dart pub add dio

Once you’ve added the package, you can import it in your Dart code:

import 'package:dio/dio.dart';

That’s it! And you’re good to go!

Here’s an example of a simple get request using the Dio package:

import 'package:dio/dio.dart';

final dio = Dio();

void getHttp() async {
final response = await dio.get('https://dart.dev');
print(response);
}

Dio Apis

Creating an instance of Dio

To use the Dio flutter package in your Dart or Flutter application, you first need to create an instance of the Dio class. This instance will be used to make HTTP requests to remote APIs.

import 'package:dio/dio.dart';

final dio = Dio();

This is how we can create an instance of the Dio class. Here’s an example of creating an instance of Dio and setting some default configurations:

import 'package:dio/dio.dart';

final dio = Dio(
BaseOptions(
baseUrl: 'https://api.example.com',
connectTimeout: 5000,
receiveTimeout: 5000,
headers: {
'Accept': 'application/json',
},
),
);

In this example, we’re creating an instance of Dio with the BaseOptions class. The BaseOptions class allows us to set default options for our requests, including the base URL, connection timeout, receive timeout, and headers.

The baseUrl option specifies the base URL for all requests made with this Dio instance. Any relative URLs used in the get, post, put, patch, or delete methods will be appended to this base URL.

The connectTimeout and receiveTimeout options specify the maximum amount of time to wait for a connection and for data to be received, respectively. If a timeout occurs, the request will fail with an error.

The headers option allows you to set default headers for all requests made with this Dio instance. In this example, we’re setting the Accept header to application/json, indicating that we expect to receive JSON data in the response.

By setting these default configurations, we can simplify our code and reduce duplication. We can then make HTTP requests with this Dio instance by calling the appropriate method, such as get or post.

Request Options

In Dio Flutter, you can also set request-specific options using the Options class. When making an HTTP request, you can pass an instance of Options to the options parameter of the request method, such as get, post, put, patch, or delete. Here are some of the options you can set:

  • method: Specifies the HTTP method to use for the request. If not specified, Dio Flutter will default to GET.
  • headers: Specifies any additional headers to include in the request. These headers will be merged with any default headers set on the Dio instance.
  • queryParameters: Specifies any query parameters to include in the request URL.
  • data: Specifies the data to include in the request body. This can be a String, Map, List, FormData, or a file (MultipartFile).
  • contentType: Specifies the content type of the request body. If not specified, Dio Flutter will automatically set the content type based on the type of data.
  • responseType: Specifies the type of the response data. This can be ResponseType.json or ResponseType.stream, ResponseType.plain, or ResponseType.bytes.

Here’s an example of using Options to set request-specific options:

import 'package:dio/dio.dart';

final dio = Dio();

void makeRequest() async {
try {
final response = await dio.get(
'/users',
options: Options(
headers: {
'Authorization': 'Bearer $accessToken',
},
queryParameters: {
'sortBy': 'createdAt',
'limit': 10,
},
),
);
print(response.data);
} catch (e) {
print(e);
}
}

In this example, we’re making a GET request to the /users endpoint with a few request-specific options. We’re including an Authorization header with an access token, and we’re passing some query parameters to filter the results.

By using Options, you can customize each request as needed, without having to modify the default settings on your Dio instance.

Response

In Dio Flutter, the response from an HTTP request is represented by an instance of the Response class. This class contains information about the response, including the response headers, status codes, and data.

Here’s an example of making a GET request with Dio and handling the response:

import 'package:dio/dio.dart';

final dio = Dio();

void makeRequest() async {
try {
final response = await dio.get('/users');
print(response.statusCode); // print the status code
print(response.headers); // print the headers
print(response.data); // print the response data
} catch (e) {
print(e);
}
}

In this example, we’re making a GET request to the /users endpoint and printing some information about the response. We’re using the statusCode property to get the HTTP status code (e.g. 200 for a successful response), the headers property to get the response headers, and the data property to get the response data.

By default, Dio will parse the response data based on the responseType option. If responseType is set to ResponseType.json, for example, Dio will automatically parse the response data as JSON and return a Map or List object. You can also manually parse the response data using libraries like dart: convert.

If an error occurs during the request, such as a network error or a server error, Dio will throw an instance of DioError. You can catch this error and handle it appropriately in your code.

Interceptors

Interceptors are a powerful feature of Dio Flutter that allow you to intercept and modify HTTP requests and responses. You can use interceptors to add headers, modify data, log requests and responses, and more.

In Dio Flutter, interceptors are represented by the Interceptor class. You can create an instance of Interceptor and add it to the interceptors list on your Dio instance. The interceptors list is a list of request interceptors, which are executed in the order they are added. You can also add response interceptors, which are executed in the opposite order of the request interceptors.

Here’s an example of creating and adding an interceptor to a Dio instance:

import 'package:dio/dio.dart';

final dio = Dio();

class AuthInterceptor extends Interceptor {
final String accessToken;

AuthInterceptor(this.accessToken);

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

void main() {
dio.interceptors.add(AuthInterceptor('myAccessToken'));
}

In this example, we’re creating an AuthInterceptor class that adds an Authorization header to every outgoing request. We’re then adding an instance of AuthInterceptor to our Dio instance using the interceptors list.

Interceptors can also modify the response before it is returned to the caller. For example, you can use a response interceptor to log the response or modify the response data.

Here’s an example of creating and adding a response interceptor to a Dio instance:

import 'package:dio/dio.dart';

final dio = Dio();

class LoggingInterceptor extends Interceptor {
@override
void onResponse(Response response, ResponseInterceptorHandler handler) {
print(response.statusCode);
print(response.headers);
print(response.data);
super.onResponse(response, handler);
}
}

void main() {
dio.interceptors.add(LoggingInterceptor());
}

In this example, we’re creating a LoggingInterceptor class that logs the response status code, headers, and data. We’re then adding an instance of LoggingInterceptor to our Dio instance using the interceptors list.

By using interceptors, you can add powerful functionality to your HTTP requests and responses in Dio Flutter.

Handling Errors

In Dio Flutter, error handling is done through the use of the DioError class. This class is thrown whenever there is an error during a request and contains information about the error, such as the response code and message.

Here’s an example of how you can handle errors in Dio:

final dio = Dio();

try {
final response = await dio.get('https://example.com');
// Handle successful response
} on DioError catch (e) {
if (e.response != null) {
// Handle error response
print('Error ${e.response.statusCode}: ${e.response.statusMessage}');
} else {
// Handle no response
print('Error: ${e.message}');
}
}

In this example, we’re using a try-catch block to handle errors that may occur during a request. If a DioError is thrown, we’re checking whether the error contains a response. If there is a response, we’re printing the status code and message. If there is no response, we’re printing the error message.

Additionally, Dio provides several types of error that can be accessed through the DioErrorType enum. These error types include CANCEL, CONNECT_TIMEOUT, RECEIVE_TIMEOUT, SEND_TIMEOUT, DEFAULT, and RESPONSE. You can use these error types to more easily handle different types of errors that may occur during a request.

try {
final response = await dio.get('https://example.com');
// Handle successful response
} on DioError catch (e) {
if (e.type == DioErrorType.RESPONSE) {
// Handle response error
if (e.response != null) {
print('Error ${e.response.statusCode}: ${e.response.statusMessage}');
} else {
print('Error: ${e.message}');
}
} else if (e.type == DioErrorType.CONNECT_TIMEOUT) {
// Handle connection timeout error
print('Connection timeout error: ${e.message}');
} else {
// Handle other error types
print('Error: ${e.message}');
}
}

In this example, we’re using the DioErrorType enum to handle different types of errors that may occur during a request. We’re checking whether the error type is RESPONSE, CONNECT_TIMEOUT, or some other type, and handling the error accordingly.

By using the DioError class and the DioErrorType enum, you can handle errors in Dio more easily and provide better error messages to your users.

Use of “application/x-www-form-urlencoded” Format

application/x-www-form-urlencoded is a format used for submitting data from an HTML form to a web server. This format is also commonly used in API requests to send data in the body of the request.

Here’s an example code of how we can use application/x-www-form-urlencoded:

// Instance level
dio. options.contentType = Headers.formUrlEncodedContentType;
// or only works once
dio.post(
'/info',
data: {'id': 10},
options: Options(contentType: Headers.formUrlEncodedContentType),
);

Sending FormData

FormData is a class in Dio flutter that provides a convenient way to create and send multipart/form-data requests. multipart/form-data is a format used for sending binary data, such as images or files, as well as text data, in the body of an HTTP request.

Here’s an example of how you can use FormData in Dio flutter:

try {
final response = await dio.get('https://example.com');
// Handle successful response
} on DioError catch (e) {
if (e.type == DioErrorType.RESPONSE) {
// Handle response error
if (e.response != null) {
print('Error ${e.response.statusCode}: ${e.response.statusMessage}');
} else {
print('Error: ${e.message}');
}
} else if (e.type == DioErrorType.CONNECT_TIMEOUT) {
// Handle connection timeout error
print('Connection timeout error: ${e.message}');
} else {
// Handle other error types
print('Error: ${e.message}');
}
}

In this example, we’re creating a FormData object and adding some data to it, including a file (avatar.jpg). We’re then sending a POST request to an API endpoint with the data parameter set to the FormData object.

When the request is sent, Dio will automatically convert the data in the FormData object to multipart/form-data format and include it in the body of the request. The file will be sent as binary data, and the other fields will be sent as text data.

Note that when adding a file to a FormData object, you’ll need to use the MultipartFile.fromFile method to create a MultipartFile object from the file, and provide a filename for the file. You can also provide other options, such as the content type of the file.

Using FormData in Dio flutter makes it easy to send multipart/form-data requests with text and binary data, without having to manually encode the data in the correct format.

Transformer

In Dio flutter, a Transformer is a class that can be used to transform the data received in the response. Dio Flutter includes several built-in transformers that can be used out of the box, including:

  • DefaultTransformer: This is the default transformer used by Dio Flutter. It doesn’t do any transformation of the response data, and simply returns it as a String.
  • JsonTransformer: This transformer deserializes JSON data into a Dart object using the dart: convert library.
  • TypedResponseTransformer: This transformer deserializes the response data into a Response object, which includes both the response data and metadata such as the status code.

You can also define your custom Transformer by creating a class that implements the Transformer interface and then passing an instance of that class to the transformer property of the Dio instance.

Using Transformer in Dio Flutter allows you to easily deserialize response data into Dart objects or modify the response data in other ways, without having to manually handle the response data yourself.

HttpClientAdapter

In Dio flutter, a HttpClientAdapter is an abstract class that defines the interface for an HTTP client adapter. An HTTP client adapter is responsible for handling the low-level details of making HTTP requests, such as establishing connections, sending requests, and receiving responses.

Dio flutter provides several built-in HTTP client adapters that can be used out-of-the-box, including:

  • DefaultHttpClientAdapter: This is the default HTTP client adapter used by Dio Flutter. It uses the dart: io library to make HTTP requests.
  • BrowserHttpClientAdapter: This HTTP client adapter is designed for use in web applications, and uses the dart library to make HTTP requests.

You can also create your own custom HttpClientAdapter by extending the HttpClientAdapter class and implementing its methods.

Using HttpClientAdapter in Dio Flutter allows you to customize how HTTP requests are made, and provides an abstraction layer that makes it easy to switch between different HTTP client implementations.

Cancellation

Cancellation in Dio flutter refers to the ability to cancel an ongoing HTTP request that has been sent using the Dio instance. This can be useful in situations where the user navigates away from a screen or performs another action that makes the current request unnecessary.

Dio Flutter provides a CancelToken class that can be used to cancel requests. A CancelToken is created using the CancelToken() constructor, and can then be passed as an optional parameter to the Dio methods that send HTTP requests, such as get(), post(), put(), and delete(). When a request is canceled, a CancelTokenException is thrown.

Using cancellation in Dio Flutter allows you to provide a better user experience by allowing users to cancel unnecessary requests, and can also help to reduce unnecessary network traffic.

Example App

Here’s an example app that demonstrates how to use GET and POST APIs with the Dio package in Flutter:

Let’s build the UI first!

import 'dart: convert';

import 'package: flutter/material.dart';
// import 'package:dio/dio.dart';

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
const MyApp({super.key});

@override
Widget build(BuildContext context) {
return const MaterialApp(
title: 'Dio Example',
home: MyHomePage(),
);
}
}

class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});

@override
_MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Dio Example'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// Button for getting request
ElevatedButton(
// method will be implemented later
onPressed: (){},
child: const Text('Get Data'),
),
const SizedBox(height: 16.0),
// To display the response of Get the request of screen
Text("Get Response"),
const SizedBox(height: 32.0),
// Button for Post request
ElevatedButton(
// method will be implemented later
onPressed: (){},
child: const Text('Post Data'),
),
const SizedBox(height: 16.0),
// To display the response to the Post request on the screen
Text("Post Response"),
],
),
),
);
}
}

So here is the simple UI with two elevated buttons on the screen labeled Get Data and Post Data. Here’s how it looks.

Let’s implement the logic.

In the _MyHomePageState class, we will declare two String variables as follows:

String _getResponse = '';
String _postResponse = '';

_getResponse and _postResponse will be used to store the response of Get Request and Post Request.

In the same class, we will define two methods named _fetchGetData and _fetchPostData:

void _fetchGetData() async {
try {
final response = await Dio().get('https://example.com');
setState(() {
_getResponse = json.encode(response.data);
});
} catch (e) {
print(e.toString());
}
}

void _fetchPostData() async {
try {
final response = await Dio().post(
'https://example.com',
data: {'title': 'Dio Example', 'body': 'This is a post request example'},
);
setState(() {
_postResponse = json.encode(response.data);
});
} catch (e) {
print(e.toString());
}
}

The _fetchGetData method sends a GET request to the API and sets the _getResponse string to the response data using json. encode()* Similarly, the _fetchPostData method sends a POST request to the API with a JSON payload containing a title and body, and sets the _postResponse string to the response data using json. encode().

Here’s the final code of MyHomePage with the methods implemented:

class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});


@override
_MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
String _getResponse = '';
String _postResponse = '';

void _fetchGetData() async {
try {
final response = await Dio().get('https://example.com');
setState(() {
_getResponse = json.encode(response.data);
});
} catch (e) {
print(e.toString());
}
}

void _fetchPostData() async {
try {
final response = await Dio().post(
'https://example.com',
data: {'title': 'Dio Example', 'body': 'This is a post request example'},
);
setState(() {
_postResponse = json.encode(response.data);
});
} catch (e) {
print(e.toString());
}
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Dio Example'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// Button for getting request
ElevatedButton(
onPressed: _fetchGetData,
child: const Text('Get Data'),
),
const SizedBox(height: 16.0),
// To display the response of Get the request of screen
Text(_getResponse),
const SizedBox(height: 32.0),
// Button for Post request
ElevatedButton(
onPressed: _fetchPostData,
child: const Text('Post Data'),
),
const SizedBox(height: 16.0),
// To display the response to the Post request on the screen
Text(_postResponse),
],
),
),
);
}
}

Conclusion

In conclusion, Dio Flutter is a versatile and powerful package for making HTTP requests in Flutter. Here are some of the key takeaways:

  • Dio is a powerful and versatile package for making HTTP requests in Flutter.
  • Dio provides a simple and intuitive API for working with HTTP requests and responses in Flutter.
  • Dio supports a variety of data formats, including JSON, form data, and URL-encoded data.
  • Dio includes interceptors that allow you to modify requests and responses, as well as handle errors and authentication.
  • Dio includes error-handling features that enable you to gracefully handle network errors and exceptions.
  • Dio provides the ability to cancel ongoing HTTP requests using the CancelToken class, which can be useful for managing network traffic and providing a better user experience.
  • Dio allows you to customize the default configuration of your HTTP client, including timeouts, headers, and proxy settings.
  • Dio includes a Transformer class that can be used to transform data into the desired format, such as converting JSON data into Dart objects.

Overall, Dio Flutter is a great package for working with API data in Flutter. Its flexibility, simplicity, and wide range of features make it a powerful tool for building high-quality applications that interact with web services. By leveraging the capabilities of Dio, you can easily manage HTTP requests and responses in your Flutter application, and create a seamless and efficient user experience.

Codeklips

Thank you for reading until the end. Before you go:

--

--

Kirtan Dudhat

Senior Flutter Developer | Mobile App Development | Android App Development | iOS App Development | Website Development | Digital Marketing | Expert App Develop