flutter dio
what is dio in flutter

Most of us are unaware of what Dio in Flutter is. I’m going to talk something about dio in this flutter article, that may be useful to many of us in the future in flutter app development. A networking library called flutter dio was created by Flutter China. Things that dio package supports may be accomplished using the standard http library provided by the flutter sdk, but dio may be preferable because it is easier to learn quickly and make internet calls.

So let’s begin with Dio.

What is dio in flutter

library dio Null safety Dio is a strong HTTP client for Dart that supports Interceptors, global configuration, FormData, file downloading, etc. Dio is also highly user-friendly. a free software undertaking approved by https://flutterchina.club .

Why to use Dio for Internet calls?

Dio makes networking in Flutter feel simple and gently manages several edge circumstances. Dio makes it simpler to manage several network requests running simultaneously while providing the security of a sophisticated error handling method. Additionally, it enables you to avoid the boilerplate code required to track any file upload progress using the http package.

So let’s begin with Dio. In order to begin using Dio, you must first add dependency :

dependencies:
dio: ^any

Then import it after installing the package using the terminal’s command line:

To set up the package: flutter pub get or hit pub get button on android studio

Now , import "package:dio/dio.dart" into your dart code;

How to Make Internet Calls in flutter using Dio Package

Making a GET request using Dio package:

Here, create & use a Dio instance, then wait for the desired response from the api’s.

Response? response;
Dio dio = Dio();
response = await dio.get("base_url/test?id=1&name=jhon");
print(“Response :: ${response.data.toString()}”);

A POST request using Dio Package:

Await the request you wish to post after receiving the request using a Dio instance.

response = await dio.post("base_url/test", data: {"id": 1, "name": "jhon"});

Executing many queries at once / multiple api call in flutter:

It frequently happens that you must access multiple APIs at once, which can be perplexing at times. In these situations, dio can be used quickly to obtain results from multiple APIs.

response = await Future.wait([dio.post("/info"), dio.get("/token")]);


In order to download a file using dio in flutter:

Dio can be the greatest option for downloading any type of content. Although the standard http library included in the flutter SDK can be used, it is not particularly intuitive, thus dio can be used instead.

response = await dio.download("https://www.google.com/", "./xx.html","./xx.pdf""./xx.jpeg",);

Receive a stream response:

Receiving a series of events is possible with the help of a stream. Therefore, you must set responseType to “stream” in order to get response stream with the flow of your code.

Response<ResponseBody> response  = await Dio().get<ResponseBody>(url,
 options: Options(responseType: ResponseType.stream), 
/// here, change responseType to bytes
);
print(“Response ::${response.data.stream}”);

Obtain a response in bytes:

Set responseType to “bytes” in order to receive a response in bytes.

Response<List<int>> response  = await Dio().get<List<int>>(url,
 options: Options(responseType: ResponseType.bytes), 
/// here, change responseType to bytes
);
print(“Response :: ${response .data}”); 

Sending FormData in flutter:

Use the instance FormData from the map to transmit the form data, then choose the destination and watch for the post.

FormData formData = FormData.fromMap({
    "name": "jhon",
    "age": 20,
  });
response = await dio.post("/info", data: formData);
print(“Response FormData :: ${formData}”);

Using FormData to upload several files to a server:

Using MultiPartFile and then waiting for it step by step or vice versa might save you important time when you need to upload many files to the server using your formData.

FormData.fromMap({
    "name": "jhon",
    "age": 20,
    "file": await MultipartFile.fromFile("./text.txt",filename: "dataFile.txt"),
    "files": [
      await MultipartFile.fromFile("./text1.txt", filename: "myFile.txt"),
      await MultipartFile.fromFile("./text2.txt", filename: "myFiles.txt"),
    ]
});
response = await dio.post("/info", data: formData);

It is important to note that the HttpClient-initiated request is still used by dio internally. As a result, the callback function onHttpClientCreate can set the proxy, request authentication, certificate verification, and other parameters to those of the HttpClient.

(dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate = (client) {
client.findProxy = (uri) {
      return "PROXY 192.168.1.2:8888";
    };
    ///Here , Verification certificate
httpClient.badCertificateCallback=(X509Certificate cert, String host, int port){
      if(cert.pem==PEM){
      return true; 
///here, If the certificate is consistent, data is allowed to be sent
     }
     return false;
    };   
  };

The current dio instance will call onHttpClientCreate when a new HttpClient needs to be created, so changing the configuration of the HttpClient through this callback will affect the entire dio instance. You can start a fresh dio instance if you want to ask for a different proxy or certificate verification policy for a certain application.

Following the uploading process with audio:

After using await in the post request, the function onSendProgress can be helpful for monitoring the uploading process of the response.

response = await dio.post(
  "http://www.dtworkroom.com/doris/1/2.0.0/test",
  data: {"aa": "bb" * 22},
  onSendProgress: (int sent, int total) {
    print("Value :: $sent");
    print("Value :: $total");
  },
);

Data post-binary by stream:

/// Binary data
List<int> postData = <int>[...];
await dio.post(
  url,
  data: Stream.fromIterable(postData.map((e) => [e])), 
///create a Stream<List<int>>
  options: Options(
    headers: {
      Headers.contentLengthHeader: postData.length, 
///Here, set content-length
    },
  ),
);

Many more functionalities offered by dio through the dio API include like :

baseUrl: It makes a basic url request, which typically includes the sub route. comparable to “https://www.google.com/api/.”

connectionTimeout: It contains the timeout for opening a url in milliseconds.

receiveTimeout: The Dio will issue a DioError with a DioErrorType if the duration between two events in the response stream exceeds the receiveTimeout. A RECEIVE TIMEOUT.

dio.options.baseUrl = "Your URL Here ";
dio.options.connectTimeout = 3000; 
dio.options.receiveTimeout = 2000;

BaseOptions options = BaseOptions(
    baseUrl: "Your URL Here ",
    connectTimeout: 3000,
    receiveTimeout: 2000,
);

Dio dio = Dio(options);

ResponseType: 

The following details are included in a response to a request.

{
  // Response body. may have been transformed, please refer to [ResponseType].
  T? data;
  // Response headers.
  Headers? headers;
  // The corresponding request info.
  Options? request;
  // Http status code.
  int?statusCode;
  // Whether redirect 
  bool? isRedirect;  
  // redirect info    
  List<RedirectInfo>? redirects ;
  // Returns the final real request uri (maybe redirect). 
  Uri ? realUri;    
  // Custom field that you can retrieve it later in `then`.
  Map<String, dynamic>? extra;
}

Verifying the Response :

We also have a unique key stored in our shared preferences that we must use to verify our responses, just as it was with the Request. The user is no longer active, according to the error message we issue if we cannot locate the requested header or if the key differs from the one that is saved.

We can develop a method for the response in the same way that we did for the request. The main distinction is that we are now working with a Response object, which includes both the statusCode and the original Request data in addition to some of the headers and data from the Request.

We also have a dynamic type for the function’s return, which can be any of the following:

If we wish to proceed with the request, we must pass the Response object.

If we throw an error after validating the return data, it will be a DioError.

This means that we may return the following DioError object if the boolean value for the header isUserActive is false:

dynamic responseInterceptor(Response options) async {
  if (options.headers.value("verify_Token") != null) {

    /// Compare it to the Shared Preferences key if the header is present.
    SharedPreferences prefs = await SharedPreferences.getInstance();
    var verifyToken = prefs.get("Verify_Token");
    
    // if the value is the same as the header, continue with the request
    if (options.headers.value("verify_Token") == verify_Token) {
      return options;
    }
  }

  return DioError(request: options.request, message: "Something Wrong");
}

Defining the PUT request :

Using a PUT request, you can change the data already on the API server.

Future<UserInfo?> updateUserValue({
  required UserInfoValue ? userInfoValue,
  required String ? idValue,
}) async {
  UserInfoValue ? updatedUserValue;

  try {
    Response response = await _dio.put(
      _baseUrl + '/users/$idValue',
      data: UserInfoValue.toJson(),
    );

     print(“updated Value : ${response.data}”);

    updatedUserValue = userInfoValue.fromJson(response.data);
  } catch (exe) {
    print(“Exception :: $exe”);
  }

  return updatedUserValue;
}

The DELETE request’s :

Using a DELETE request, you can remove some data from the server.

Future<void> deleteValue({required String ? idValue}) async {
  try {
    await _dio.delete(_baseUrl + '/users/$idValue');
    print(“User deleted successfully…..”);
  } catch (exe) {
    print(“Exception :: $exe”);
  }
}

Services communicate with the backend using an API class. A dedicated Dio client is used when the token has to be refreshed; otherwise, one of the two Dio clients in the class is used for all requests.

class Api {
  final dio = createDio();
  final tokenDio = Dio(BaseOptions(baseUrl: Globals().Url));

  Api._internal();

  static final _singleton = Api._internal();

  factory Api() => _singleton;

  static Dio createDio() {
    var dio = Dio(BaseOptions(
      baseUrl: Globals().Url,
      receiveTimeout: 5000, 
      connectTimeout: 5000,
      sendTimeout: 5000,
    ));

    dio.interceptors.addAll({
      AppInterceptors(dio),
    });
    return dio;
  }
}

class AppInterceptors extends Interceptor {
  final Dio ? dio;

  AppInterceptors(this.dio);

  @override
  void onRequest(
      RequestOptions options, RequestInterceptorHandler handler) async {
    var accessToken = await TokenRepository().getAccessToken();

    if (accessToken != null) {
      var expiration = await TokenRepository().getAccessTokenRemainingTime();

      if (expiration.inSeconds < 60) {
        dio.interceptors.requestLock.lock();

        // Call the refresh endpoint to get a new token
        await UserService()
            .refresh()
            .then((response) async {
          await TokenRepository().persistAccessToken(response.accessToken);
          accessToken = response.accessToken;
        }).catchError((error, stackTrace) {
          handler.reject(error, true);
        }).whenComplete(() => dio.interceptors.requestLock.unlock());
      }

      options.headers['Authorization'] = “Bearer $accessToken”;
    }

    return handler.next(options);
  }
}

Error handling :

It is simpler to detect and manage various mistakes when exceptions are used. Using the error interceptor shown below, we can handle various HTTP status codes and turn them into the relevant exceptions.

class ErrorHandlingClass extends Interceptor {
  final Dio ? dio;

  ErrorHandlingClass(this.dio);

  @override
  void onError(DioError ? err, ErrorInterceptorHandler ? handler) {
    switch (e.type) {
      case DioErrorType.connectTimeout:
      case DioErrorType.sendTimeout:
      case DioErrorType.receiveTimeout:
        throw DeadlineExceededException(e.requestOptions);
      case DioErrorType.response:
        switch (e.response?.statusCode) {
          case 400:
            throw BadRequestException(e.requestOptions);
          case 401:
            throw UnauthorizedException(e.requestOptions);
          case 404:
            throw NotFoundException(e.requestOptions);
          case 409:
            throw ConflictException(e.requestOptions);
          case 500:
            throw InternalServerErrorException(e.requestOptions);
        }
        break;
      case DioErrorType.cancel:
        break;
      case DioErrorType.other:
        throw NoInternetConnectionException(e.requestOptions);
    }

    return handler.next(e);
  }
}

class BadRequestException extends DioError {
  BadRequestException(RequestOptions  r) : super(requestOptions: r);

  @override
  String toString() {
    return 'Invalid request';
  }
}

class InternalServerErrorException extends DioError {
  InternalServerErrorException(RequestOptions r) : super(requestOptions: r);

  @override
  String toString() {
    return 'Unknown error, please try again …..';
  }
}

class ConflictException extends DioError {
  ConflictException(RequestOptions r) : super(requestOptions: r);

  @override
  String toString() {
    return 'Conflict occurred';
  }
}

class UnauthorizedException extends DioError {
  UnauthorizedException(RequestOptions r) : super(requestOptions: r);

  @override
  String toString() {
    return 'Access denied';
  }
}

class NotFoundException extends DioError {
  NotFoundException(RequestOptions r) : super(requestOptions: r);

  @override
  String toString() {
    return 'The requested info could not be found';
  }
}

class NoInternetConnectionException extends DioError {
  NoInternetConnectionException(RequestOptions r) : super(requestOptions: r);

  @override
  String toString() {
    return 'No internet connection found , please try again.';
  }
}

class DeadlineExceededException extends DioError {
  DeadlineExceededException(RequestOptions r) : super(requestOptions: r);

  @override
  String toString() {
    return 'The connection has timed out, please try again…..';
  }
}

We log the following while testing it with the JSON Placeholder website:

GET “https:// url”
Headers: headers
requiresToken: true
queryParameters:
—> END GET
<— 200 for success
Headers:
connection: [keep-alive]
set-cookie: [cookieKey=cookieValue; expires=Fri, 04-Sep-20 10:34:19 GMT; path=/; path ; HttpOnly]
cache-control: [public, max-age=1400]
transfer-encoding: [chunked]
date: [Thu, 09 Sep 2020 10:00:00 GMT]
content-encoding: [gzip]
vary: [Origin, Accept-Encoding]
age: [4045]
cf-cache-status: [HIT]
expect-ct: [max-age=604800, report-uri=“https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct”]
content-type: [application/json; charset=utf-8]
pragma: [no-cache]
server: [cloudflare]
x-powered-by: [Express]
access-control-allow-credentials: [true]
cf-ray: [51178cd29f9b724b-AMS]
etag: [W/“53-hfEnumeNh6YirfjyjaujcOPPT+s”]
via: [1.1 vegur]
x-content-type-options: [nosniff]
expires: [Thu, 05 Sep 2019 14:34:19 GMT]
Response: {userId: 2, id: 2, title: dioExample, completed: false}
<— END HTTP

To add to that, the logs are now displayed via the print method. Since these logs will also be shown in the production app, anyone who connects the phone while this app is open and launches flutter logs will be able to view the complete output, which may not be optimal. DebugPrint is a superior substitute that we may utilize.

Interceptors :

By using then or catchError, you can stop Dio requests, answers, and errors before they are processed. In a real-world setting, interceptors are helpful for JSON Web Tokens (JWT)-based authorisation, parsing JSON, managing problems, and quickly troubleshooting Dio network requests.

By replacing the callbacks at onRequest, onResponse, and onError, you may run the interceptor.

For the purposes of our example, we will construct a straightforward interceptor for logging various requests. a new class called Logging that extends Interceptor should be created:

void onResponse(Response ? response, ResponseInterceptorHandler ?handler) {
 
   print( “Response is :: [${response.statusCode}] => PATH:     ${response.requestOptions.path}”);
   
 return super.onResponse(response, handler);
  }

  void onError(DioError ? e, ErrorInterceptorHandler ? handler) {

    print(“Error is [${err.response?.statusCode}] => PATH: ${err.requestOptions.path}”);

    return super.onError(e, handler);
  }
}

Conclusion

Dio makes networking in Flutter feel simple and gently manages several edge circumstances. Dio makes it simpler to manage several network requests running simultaneously while providing the security of a sophisticated error handling method. Additionally, it enables you to avoid the boilerplate code required to track any file upload progress using the http package. Furthermore, the Dio package allows you to perform a number of further sophisticated changes that go beyond what we have discussed here.

ThankYou for reading the content!!!!!Have a Wonderful Day 🙂🙂……..