79.1 Introducing Dio
As mentioned in the previous section, directly using HttpClient
to make network requests can be cumbersome, as many aspects require manual handling. When it comes to file uploads/downloads and cookie management, it can become quite complex. Fortunately, the Dart community offers several third-party HTTP request libraries that simplify the process significantly. In this section, we will introduce the popular Dio library.
Dio is a powerful Dart HTTP request library maintained by the author, supporting RESTful APIs, FormData, interceptors, request cancellation, cookie management, file uploads/downloads, timeouts, and more. The usage of Dio may change with version upgrades, so if the content of this section differs from the latest Dio functionality, please refer to the latest Dio documentation.
79.2 Using Dio to Make Requests
Adding Dio:
dependencies: dio: ^x.x.x # Please use the latest version from pub
Importing and Creating a Dio Instance:
import 'package:dio/dio.dart'; Dio dio = Dio();
Now you can use the dio
instance to make network requests. Note that a single dio
instance can initiate multiple HTTP requests. Generally, when an app has only one HTTP data source, Dio should be used as a singleton.
Making GET Requests:
Response response; response = await dio.get("/test?id=12&name=wendu"); print(response.data.toString());
For GET requests, you can also pass query parameters as an object. The above code is equivalent to:
response = await dio.get("/test", queryParameters: {"id": 12, "name": "wendu"}); print(response);
Making a POST Request:
response = await dio.post("/test", data: {"id": 12, "name": "wendu"});
Making Multiple Concurrent Requests:
response = await Future.wait([dio.post("/info"), dio.get("/token")]);
Downloading a File:
response = await dio.download("https://www.google.com/", _savePath);
Sending FormData:
FormData formData = FormData.from({ "name": "wendux", "age": 25, }); response = await dio.post("/info", data: formData);
If the data being sent is FormData
, Dio will automatically set the request header contentType
to multipart/form-data
.
Uploading Multiple Files via FormData:
FormData formData = FormData.from({ "name": "wendux", "age": 25, "file1": UploadFileInfo(File("./upload.txt"), "upload1.txt"), "file2": UploadFileInfo(File("./upload.txt"), "upload2.txt"), // Supports uploading an array of files "files": [ UploadFileInfo(File("./example/upload.txt"), "upload.txt"), UploadFileInfo(File("./example/upload.txt"), "upload.txt") ] }); response = await dio.post("/info", data: formData);
It’s worth mentioning that Dio internally uses HttpClient
to initiate requests, so proxy settings, request authentication, certificate verification, etc., work the same way as with HttpClient
. We can configure these in the onHttpClientCreate
callback, for example:
(dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate = (client) { // Set up proxy client.findProxy = (uri) { return "PROXY 192.168.1.2:8888"; }; // Certificate verification httpClient.badCertificateCallback = (X509Certificate cert, String host, int port) { if (cert.pem == PEM) { return true; // If the certificate matches, allow data to be sent } return false; }; };
Note that onHttpClientCreate
will be called whenever the current Dio instance needs to create an HttpClient
, so configuring the HttpClient
through this callback will apply to the entire Dio instance. If the application requires multiple proxies or certificate verification strategies, you can create different Dio instances for each.
Isn't it simple? Besides these basic usages, Dio also supports request configuration, interceptors, etc. The official documentation is quite detailed, so this book will not delve further into those topics. For more information, you can refer to the Dio homepage: Dio GitHub. In the next section, we will implement a chunked downloader using Dio.
79.3 Example
We will use GitHub's open API to request all public open-source projects under the FlutterChina organization, implementing:
A loading indicator during the request phase.
If the request fails, display an error message; if successful, display the project name list.
Here’s the code:
class _FutureBuilderRouteState extends State<FutureBuilderRoute> { Dio _dio = Dio(); @override Widget build(BuildContext context) { return Container( alignment: Alignment.center, child: FutureBuilder( future: _dio.get("https://api.github.com/orgs/flutterchina/repos"), builder: (BuildContext context, AsyncSnapshot snapshot) { // Request completed if (snapshot.connectionState == ConnectionState.done) { Response response = snapshot.data; // Error occurred if (snapshot.hasError) { return Text(snapshot.error.toString()); } // Request successful; construct a ListView to display project names return ListView( children: response.data.map<Widget>((e) => ListTile(title: Text(e["full_name"])) ).toList(), ); } // Show loading indicator while request is in progress return CircularProgressIndicator(); } ), ); } }