This article (which was recently updated to keep up with the latest versions of Flutter) shows you a couple of different approaches to fetching data from APIs in Flutter. The first approach is using the HttpClient class, and the second one is using the http plugin from pub.dev.

For demonstration purposes, we’ll use an open API endpoint that contains information of 5000 photos (thanks to the Typicode team for this):
https://jsonplaceholder.typicode.com/photos
Information of a single photo looks like this:
{
"albumId": 1,
"id": 6
"title": "accusamus ea aliquid et amet sequi nemo",
"url": "https://via.placeholder.com/600/56a8c2",
"thumbnailUrl": "https://via.placeholder.com/150/56a8c2"
}
After sending a GET request to the API and taking a response, we’ll display photos by using a ListView.
Using HttpClient
If you don’t like to use so many plugins in your application, try this method.
Example Preview

The function that fetches data from the API:
Future<void> _fetchData() async {
const apiUrl = 'https://jsonplaceholder.typicode.com/photos';
HttpClient client = HttpClient();
client.autoUncompress = true;
final HttpClientRequest request = await client.getUrl(Uri.parse(apiUrl));
request.headers
.set(HttpHeaders.contentTypeHeader, "application/json; charset=UTF-8");
final HttpClientResponse response = await request.close();
final String content = await response.transform(utf8.decoder).join();
final List data = json.decode(content);
setState(() {
_loadedPhotos = data;
});
}
The full code
// main.dart
import 'package:flutter/material.dart';
import 'dart:io'; // for using HttpClient
import 'dart:convert'; // for using json.decode()
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const MaterialApp(
// Hide the debug banner
debugShowCheckedModeBanner: false,
title: 'Kindacode.com',
home: HomePage(),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
// The list that contains information about photos
List _loadedPhotos = [];
// The function that fetches data from the API
Future<void> _fetchData() async {
const apiUrl = 'https://jsonplaceholder.typicode.com/photos';
HttpClient client = HttpClient();
client.autoUncompress = true;
final HttpClientRequest request = await client.getUrl(Uri.parse(apiUrl));
request.headers
.set(HttpHeaders.contentTypeHeader, "application/json; charset=UTF-8");
final HttpClientResponse response = await request.close();
final String content = await response.transform(utf8.decoder).join();
final List data = json.decode(content);
setState(() {
_loadedPhotos = data;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Kindacode.com'),
),
body: SafeArea(
child: _loadedPhotos.isEmpty
? Center(
child: ElevatedButton(
onPressed: _fetchData,
child: const Text('Load Photos'),
),
)
// The ListView that displays photos
: ListView.builder(
itemCount: _loadedPhotos.length,
itemBuilder: (BuildContext ctx, index) {
return ListTile(
leading: Image.network(
_loadedPhotos[index]["thumbnailUrl"],
width: 150,
fit: BoxFit.cover,
),
title: Text(_loadedPhotos[index]['title']),
subtitle:
Text('Photo ID: ${_loadedPhotos[index]["id"]}'),
);
},
)));
}
}
Using a Plugin
If you want to write less code, try this approach.
Installation
Install the http plugin by running the following command:
flutter pub add http
Then execute this command:
flutter pub get
Import the plugin into the code:
import 'package:http/http.dart' as http;
The fetching data function
Future<void> _fetchData() async {
const apiUrl = 'https://jsonplaceholder.typicode.com/photos';
final response = await http.get(Uri.parse(apiUrl));
final data = json.decode(response.body);
setState(() {
_loadedPhotos = data;
});
}
It’s much shorter than the first approach, isn’t it?
The full code
// main.dart
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:convert'; // for using json.decode()
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
// Hide the debug banner
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.pink,
),
title: 'Kindacode.com',
home: const HomePage(),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
@override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
// The list that contains information about photos
List _loadedPhotos = [];
// The function that fetches data from the API
Future<void> _fetchData() async {
const apiUrl = 'https://jsonplaceholder.typicode.com/photos';
final response = await http.get(Uri.parse(apiUrl));
final data = json.decode(response.body);
setState(() {
_loadedPhotos = data;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Kindacode.com'),
),
body: SafeArea(
child: _loadedPhotos.isEmpty
? Center(
child: ElevatedButton(
onPressed: _fetchData,
child: const Text('Load Photos'),
),
)
// The ListView that displays photos
: ListView.builder(
itemCount: _loadedPhotos.length,
itemBuilder: (BuildContext ctx, index) {
return ListTile(
leading: Image.network(
_loadedPhotos[index]["thumbnailUrl"],
width: 150,
fit: BoxFit.cover,
),
title: Text(_loadedPhotos[index]['title']),
subtitle:
Text('Photo ID: ${_loadedPhotos[index]["id"]}'),
);
},
)));
}
}
The Result
The result you get is identical to the first example:

The example above uses the http plugin but there’re many other good options. See this article: Best Libraries for Making HTTP Requests in Flutter.
Conclusion
We’ve walked through 2 techniques to fetch data from remote APIs in Flutter. If you’d like to learn more new and interesting things in Flutter, take a look at the following articles:
- Flutter and Firestore Database: CRUD example (null safety)
- Flutter: ListView Pagination (Load More) example
- Ways to Store Data Offline in Flutter
- Flutter & SQLite: CRUD Example
- Using GetX (Get) for State Management in Flutter
- Flutter + Firebase Storage: Upload, Retrieve, and Delete files
You can also check out our Flutter category page, or Dart category page for the latest tutorials and examples.