A Beginner's Guide to the HTTP Package in Flutter
In today’s interconnected world, many apps require internet access to provide dynamic and up-to-date content. A commonly used method for this is HTTP requests, a form of communication between client and server. In Flutter, we can use the http
package to handle these HTTP requests. This article will take you through the basics and advance you to more complex concepts involved in using the http
package.
What’s Covered in This Guide
- Introduction to HTTP
- Setting Up the HTTP Package
- Making a Basic GET Request
- Handling Responses
- POST Requests and Sending Data
- Error Handling
- Downloading Raw Bytes
- Interceptors
- Pros and cons
- Alternatives
- Conclusion
Introduction to HTTP
HTTP, or Hypertext Transfer Protocol, is the foundation of any data exchange on the Web. It’s a protocol used for transmitting hypertext over the internet. Hypertext is structured text that uses logical links (hyperlinks) between nodes containing text.
In the context of Flutter and the http
package, we’ll primarily be dealing with two types of HTTP requests:
- GET: Retrieves information from the specified source.
- POST: Sends new information to the specified source.
However, you can use this knowledge to perform any kind of HTTP request, including PUT, DELETE, and PATCH.
Setting Up the HTTP Package
Before we can make HTTP requests, we need to add the http
package to our project. You can add it by running the following command:
flutter pub add http
You can also manually add it. For that, add this line to your pubspec.yaml
file under dependencies:
dependencies:
flutter:
sdk: flutter
http: ^0.13.6
Then, run flutter packages get
in your terminal to fetch the package.
Making a Basic GET Request
A GET request retrieves, or “gets”, data from a server. In the http
package, we use the get
function for this. Here’s a basic example:
import 'package:http/http.dart' as http;
void fetchData() async {
var response = await http.get('https://jsonplaceholder.typicode.com/posts');
print('Response status: ${response.statusCode}');
print('Response body: ${response.body}');
}
This function fetches data from the URL provided and prints the status code and the response body.
Handling Responses
In the previous section, we printed the status code and body of our HTTP response. The status code tells us whether our request was successful, and the body contains the data we requested. Typically, this data is in JSON format.
import 'dart:convert';
import 'package:http/http.dart' as http;
void fetchData() async {
var response = await http.get('https://jsonplaceholder.typicode.com/posts');
if (response.statusCode == 200) {
var jsonResponse = jsonDecode(response.body);
print('Number of posts: ${jsonResponse.length}');
} else {
print('Request failed with status: ${response.statusCode}.');
}
}
POST Requests and Sending Data
A POST request is used to send data to a server. Here’s a basic example:
import 'package:http/http.dart' as http;
void postData() async {
var response = await http.post(
'https://jsonplaceholder.typicode.com/posts',
body: {'title': 'Flutter http guide', 'body': 'A guide to using the http package in Flutter', 'userId': '1'},
);
print('Response status: ${response.statusCode}');
print('Response body: ${response.body}');
}
Apart from get
and post
requests, you might also want to look into other functions provided by the http
package, such as put
for updating data, delete
for deleting data, and read
for making a GET request and reading the body of the response as a string.
Error Handling
It’s important to handle errors in our HTTP requests when they occur. This is especially important in HTTP requests, where many things can go wrong. Here’s an example of how you might handle errors in a POST request:
import 'dart:convert';
import 'package:http/http.dart' as http;
void postData() async {
try {
var response = await http.post(
'https://jsonplaceholder.typicode.com/posts',
body: {'title': 'Flutter http guide', 'body': 'A guide to using the http package in Flutter', 'userId': '1'},
);
if (response.statusCode == 200) {
print('POST request successful. Response: ${response.body}');
} else {
print('POST request failed. Status Code: ${response.statusCode}');
}
} catch (e) {
print('An error occurred: $e');
}
}
In this example, we use a try-catch
block to handle any errors that might occur during the POST request. If an error occurs, it is printed to the console.
Apart from try-catch
blocks, you can also use onError
to handle errors. Here’s an example:
import 'dart:convert';
import 'package:http/http.dart' as http;
void postData() async {
var response = await http.post(
'https://jsonplaceholder.typicode.com/posts',
body: {'title': 'Flutter http guide', 'body': 'A guide to using the http package in Flutter', 'userId': '1'},
).onError((error, stackTrace) {
print('An error occurred: $error');
});
if (response.statusCode == 200) {
print('POST request successful. Response: ${response.body}');
} else {
print('POST request failed. Status Code: ${response.statusCode}');
}
}
Downloading Raw Bytes
You can also use readBytes
to get the body of the response as a list of bytes, which can be useful for downloading files or working with binary data.
For example, here’s how you might download an image from a URL:
import 'dart:io';
import 'package:http/http.dart' as http;
void downloadImage() async {
var url = 'https://example.com/image.jpg';
var response = await http.readBytes(url);
var filePath = '/path/to/image.jpg';
await File(filePath).writeAsBytes(response);
print('Image downloaded to $filePath');
}
In this example, we use the readBytes
function to download an image as a list of bytes, then write those bytes to a file.
Interceptors
Interceptors are a powerful feature that allow you to process requests and responses before they are sent or received. You can add an interceptor to the client that will be called for every request and response.
For example, you might use an interceptor to add an authorization header to every request:
import 'package:http/http.dart' as http;
class AuthInterceptor implements http.BaseRequest {
http.StreamedResponse send(http.BaseRequest request) async {
request.headers['Authorization'] = 'Bearer your_token';
return request.send();
}
}
void main() {
var client = http.Client();
client.send = AuthInterceptor().send;
}
In this example, we create a class AuthInterceptor
that implements http.BaseRequest
. In the send
method, we add an authorization header to the request before sending it.
Pros and Cons
Pros
Easy to Use: One of the main advantages of the
http
package is its simplicity. It provides an intuitive and easy-to-use API for making HTTP requests.Flexibility: It provides a wide range of functions for making requests, handling responses, and dealing with errors. This makes it suitable for a variety of use cases.
Asynchronous Support: Dart’s
http
package works seamlessly with async and await keywords, which simplifies asynchronous programming.Interceptors: The
http
package supports the use of interceptors, allowing developers to process requests and responses before they are sent or received.Compatible with JSON and Other Data Formats: It’s highly compatible with various data formats, which makes it easy to send and receive data in formats like JSON, XML, and more.
Cons
Lack of Built-In Advanced Error Handling: While the
http
package does allow for error handling, it lacks advanced error handling capabilities out of the box. Developers often need to implement their own solutions for complex error handling.Absence of Automatic Request Retrying: The
http
package does not support automatic retrying of failed requests, which can be a crucial feature for unstable network conditions.No Built-In Caching: It doesn’t come with built-in caching mechanism. Therefore, for caching responses, another solution needs to be implemented.
Alternatives
While the http
package is quite robust, there are other libraries available that offer additional features or different approaches to handling HTTP requests.
Dio: Dio is a powerful HTTP client for Dart, which supports Interceptors, Global configuration, FormData, Request Cancellation, File downloading, Timeout etc.
Chopper: Chopper is an http client generator using source_gen and inspired by Retrofit. It might be a good choice if you’re looking for a more automated solution.
http_client: The
http_client
package is a multi-platform, pluggable HTTP client library for Dart that can be used on different platforms (Browser, VM, Flutter) with the same code.
Each of these libraries has its own set of features and benefits, and the best one for your project will depend on your specific needs and preferences. Be sure to evaluate each one carefully to find the best fit for your Flutter application.
Conclusion
The http
package provides a powerful and flexible way to work with network requests in Dart and Flutter. It offers a wide range of functions for making requests, handling responses, and dealing with errors. Remember that when you’re working with HTTP, things can and will go wrong, so always make sure to handle errors properly.
With a good understanding of the http
package, you’ll be well-equipped to build robust and reliable Flutter apps that can interact with APIs and handle data from the web. Happy coding!
Enjoyed? Tell your friends.