$ dart pub add http
To use package:http in your code,
import it and optionally specify a library prefix:
import 'package:http/http.dart' as http;
To learn more specifics about package:http,
see its page on the pub.dev site
and its API documentation.
Build a URL
As previously mentioned,
to make an HTTP request,
you first need a URL that identifies
the resource being requested
or endpoint being accessed.
In Dart, URLs are represented through Uri objects.
There are many ways to build an Uri,
but due to its flexibility,
parsing a string with Uri.parse to
create one is a common solution.
The following snippet shows two ways
to create a Uri object
pointing to mock JSON-formatted information
about package:http hosted on this site:
// Parse the entire URI, including the scheme
Uri.parse('https://dart.dev/f/packages/http.json');
// Specifically create a URI with the https scheme
Uri.https('dart.dev', '/f/packages/http.json');
To learn about other ways of building and interacting with URIs,
see the library tour’s discussion about URIs.
Make a network request
If you just need to quickly fetch a string representation
of a requested resource,
you can use the top-level read
function found in package:http
that returns a Future<String> or throws
a ClientException if the request wasn’t successful.
The following example uses read to
retrieve the mock JSON-formatted information
about package:http as a string,
then prints it out:
info 备注:
Many functions in package:http, including read,
access the network and perform potentially time-consuming operations,
therefore they do so asynchronously and return a Future.
If you haven’t encountered futures yet,
you can learn about them—as well as the async and await keywords—in the
asynchronous programming codelab.
void main() async {
final httpPackageUrl = Uri.https('dart.dev', '/f/packages/http.json');
final httpPackageInfo = await http.read(httpPackageUrl);
print(httpPackageInfo);
This results in the following JSON-formatted output,
which can also be seen in your browser at
https://dart.cn/f/packages/http.json.
"name": "http",
"latestVersion": "0.13.5",
"description": "A composable, multi-platform, Future-based API for HTTP requests.",
"publisher": "dart.dev",
"repository": "https://github.com/dart-lang/http"
Note the structure of the data
(in this case a map),
as you will need it when decoding the JSON later on.
If you need other information from the response,
such as the status code or the headers,
you can instead use the top-level get function
that returns a Future with a Response.
The following snippet uses get to get the whole response
in order to exit early if the request was not successful,
which is indicated with a status code of 200:
void main() async {
final httpPackageUrl = Uri.https('dart.dev', '/f/packages/http.json');
final httpPackageResponse = await http.get(httpPackageUrl);
if (httpPackageResponse.statusCode != 200) {
print('Failed to retrieve the http package!');
return;
print(httpPackageResponse.body);
There are many other status codes besides 200
and your app might want to handle them differently.
To learn more about what different status codes mean,
see HTTP response status codes on the mdn web docs.
Some server requests require more information,
such as authentication or user-agent information;
in this case you might need to include HTTP headers.
You can specify headers by passing in a Map<String, String>
of the key-value pairs as the headers optional named parameter:
await http.get(Uri.https('dart.dev', '/f/packages/http.json'),
headers: {'User-Agent': '<product name>/<product-version>'});
Make multiple requests
If you’re making multiple requests to the same server,
you can instead keep a persistent connection
through a Client,
which has similar methods to the top-level ones.
Just make sure to clean up with
the close method when done:
void main() async {
final httpPackageUrl = Uri.https('dart.dev', '/f/packages/http.json');
final client = http.Client();
try {
final httpPackageInfo = await client.read(httpPackageUrl);
print(httpPackageInfo);
} finally {
client.close();
To enable the client to retry failed requests,
import package:http/retry.dart and
wrap your created Client in a RetryClient:
import 'package:http/http.dart' as http;
import 'package:http/retry.dart';
void main() async {
final httpPackageUrl = Uri.https('dart.dev', '/f/packages/http.json');
final client = RetryClient(http.Client());
try {
final httpPackageInfo = await client.read(httpPackageUrl);
print(httpPackageInfo);
} finally {
client.close();
The RetryClient has a default behavior for how many
times to retry and how long to wait between each request,
but its behavior can be modified through parameters
to the RetryClient()
or RetryClient.withDelays() constructors.
package:http has much more functionality and customization,
so make sure to check out its page on the pub.dev site
and its API documentation.
Decode the retrieved data
While you now have made a network request
and retrieved the returned data as string,
accessing specific portions of information
from a string can be a challenge.
Since the data is already in a JSON format,
you can use Dart’s built-in json.decode function
in the dart:convert library
to convert the raw string into
a JSON representation using Dart objects.
In this case, the JSON data is represented in a map structure
and, in JSON, map keys are always strings,
so you can cast the result of json.decode to a Map<String, dynamic>:
import 'dart:convert';
import 'package:http/http.dart' as http;
void main() async {
final httpPackageUrl = Uri.https('dart.dev', '/f/packages/http.json');
final httpPackageInfo = await http.read(httpPackageUrl);
final httpPackageJson = json.decode(httpPackageInfo) as Map<String, dynamic>;
print(httpPackageJson);
Create a structured class to store the data
To provide the decoded JSON with more structure,
making it easier to work with,
create a class that can store the
retrieved data using specific types depending
on the schema of your data.
The following snippet shows a class-based representation
that can store the package information returned
from the mock JSON file you requested.
This structure assumes all fields except the repository
are required and provided every time.
class PackageInfo {
final String name;
final String latestVersion;
final String description;
final String publisher;
final Uri? repository;
PackageInfo({
required this.name,
required this.latestVersion,
required this.description,
required this.publisher,
this.repository,
Encode the data into your class
Now that you have a class to store your data in,
you need to add a mechanism to convert
the decoded JSON into a PackageInfo object.
Convert the decoded JSON
by manually writing a fromJson method
matching the earlier JSON format,
casting types as necessary
and handling the optional repository field:
class PackageInfo {
// ···
factory PackageInfo.fromJson(Map<String, dynamic> json) {
final repository = json['repository'] as String?;
return PackageInfo(
name: json['name'] as String,
latestVersion: json['latestVersion'] as String,
description: json['description'] as String,
publisher: json['publisher'] as String,
repository: repository != null ? Uri.tryParse(repository) : null,
A handwritten method, such as in the previous example,
is often sufficient for relatively simple JSON structures,
but there are more flexible options as well.
To learn more about JSON serialization and deserialization,
including automatic generation of the conversion logic,
see the Using JSON guide.
Convert the response to an object of your structured class
Now you have a class to store your data
and a way to convert the decoded JSON object
into an object of that type.
Next, you can write a function that
pulls everything together:
Create your URI based off a passed-in package name.
Use http.get to retrieve the data for that package.
If the request didn’t succeed, throw an Exception
or preferably your own custom Exception subclass.
If the request succeeded, use json.decode to
decode the response body into a JSON string.
Converted the decoded JSON string into a PackageInfo object
using the PackageInfo.fromJson factory constructor you created.
Future<PackageInfo> getPackage(String packageName) async {
final packageUrl = Uri.https('dart.dev', '/f/packages/$packageName.json');
final packageResponse = await http.get(packageUrl);
// If the request didn't succeed, throw an exception
if (packageResponse.statusCode != 200) {
throw PackageRetrievalException(
packageName: packageName,
statusCode: packageResponse.statusCode,
final packageJson = json.decode(packageResponse.body) as Map<String, dynamic>;
return PackageInfo.fromJson(packageJson);
class PackageRetrievalException implements Exception {
final String packageName;
final int? statusCode;
PackageRetrievalException({required this.packageName, this.statusCode});
Utilize the converted data
Now that you’ve retrieved data and
converted it to a more easily accessible format,
you can use it however you’d like.
Some possibilities include
outputting information to a CLI, or
displaying it in a web or Flutter app.
Here is complete, runnable example
that requests, decodes, then displays
the mock information about the http and path packages:
import 'dart:convert';
import 'package:http/http.dart' as http;
void main() async {
await printPackageInformation('http');
print('');
await printPackageInformation('path');
Future<void> printPackageInformation(String packageName) async {
final PackageInfo packageInfo;
try {
packageInfo = await getPackage(packageName);
} on PackageRetrievalException catch (e) {
print(e);
return;
print('Information about the $packageName package:');
print('Latest version: ${packageInfo.latestVersion}');
print('Description: ${packageInfo.description}');
print('Publisher: ${packageInfo.publisher}');
final repository = packageInfo.repository;
if (repository != null) {
print('Repository: $repository');
Future<PackageInfo> getPackage(String packageName) async {
final packageUrl = Uri.https('dart.dev', '/f/packages/$packageName.json');
final packageResponse = await http.get(packageUrl);
// If the request didn't succeed, throw an exception
if (packageResponse.statusCode != 200) {
throw PackageRetrievalException(
packageName: packageName,
statusCode: packageResponse.statusCode,
final packageJson = json.decode(packageResponse.body) as Map<String, dynamic>;
return PackageInfo.fromJson(packageJson);
class PackageInfo {
final String name;
final String latestVersion;
final String description;
final String publisher;
final Uri? repository;
PackageInfo({
required this.name,
required this.latestVersion,
required this.description,
required this.publisher,
this.repository,
factory PackageInfo.fromJson(Map<String, dynamic> json) {
final repository = json['repository'] as String?;
return PackageInfo(
name: json['name'] as String,
latestVersion: json['latestVersion'] as String,
description: json['description'] as String,
publisher: json['publisher'] as String,
repository: repository != null ? Uri.tryParse(repository) : null,
class PackageRetrievalException implements Exception {
final String packageName;
final int? statusCode;
PackageRetrievalException({required this.packageName, this.statusCode});
@override
String toString() {
final buf = StringBuffer();
buf.write('Failed to retrieve package:$packageName information');
if (statusCode != null) {
buf.write(' with a status code of $statusCode');
buf.write('!');
return buf.toString();
与 Flutter 相关的备注:
For another example that covers fetching then displaying data in Flutter,
see the Fetching data from the internet Flutter recipe.
What next?
Now that you have retrieved, parsed, and used
data from the internet,
consider learning more about Concurrency in Dart.
If your data is large and complex,
you can move retrieval and decoding
to another isolate as a background worker
to prevent your interface from becoming unresponsive.