How to Fetch an API in Flutter
Great applications should provide useful information to their users. Therefore, displaying data from other source (server) is necessary for most application. Fortunately, Flutter gives us convenient tools out of the box to do such thing.
In Dart language, we can use a package called http
. It’s a package that contains useful functions to send and retrieve data from HTTP server.
To install http
package, add the following package to your pubspec.yaml
file on your Flutter project.
# ...
dependencies:
http: <latest_version> # add this
In this tutorial, we will fetch some data from JSONPlaceholder. It’s an open-source REST API that contains useful fake resources like blog posts, comments, photos, users, etc.
First, we can create a function that returns the response of our HTTP request. This function is asynchronous, so it should return a Future. Then, we need to check wether the response is successful or not by checking the HTTP status code. Finally, we decode our response body to with jsonDecode
function and return it.
We also want to use the http
package which we installed earlier.
import 'package:http/http.dart' as http;
Future<List<dynamic>> fetchPost() async {
final response = await http.get('https://jsonplaceholder.typicode.com/posts/1');
if (response.statusCode == 200) {
List<dynamic> post = jsonDecode(response.body);
return post;
} else {
throw Exception('Failed to load post');
}
}
Now, look back to your Flutter widget where you want to make a request to the server. If your widget is now stateless, change it to stateful. Because, we want to store the response data to display it in our widget.
import 'package:flutter/material.dart';
class PostDetail extends StatefulWidget {
@override
_PostDetailState createState() => _PostDetailState();
}
class _PostDetailState extends State<PostDetail> {
@override
Widget build(BuildContext context) {
// Your widgets
}
}
Create an initState
method in your widget. This method will be invoked whenever your flutter widget is ready to be displayed on the screen. From there, we will fetch our data and save the response to the widget state.
// ...
class _PostDetailState extends State<PostDetail> {
Future<List<dynamic>> _post;
@override
void initState() {
super.initState();
_post = fetchPost();
}
@override
Widget build(BuildContext context) {
// Your widgets
}
}
From there, we invoke our fetchPost
function and store the response to the post
widget state.
Remember, this data is asynchronous. So we can’t just put the post
state inside our build method directly.
One way to display asynchronous data is by using a widget called FutureBuilder. It’s a widget provided by Flutter inside the Flutter Material Library. This widget makes us very easy to work with asynchronous data. It let us determine what to display when the data is loaded.
When using FutureBuilder widget, we need to define our Future, obviously, and a function that returns a widget.
// ...
class _PostDetailState extends State<PostDetail> {
Future<List<dynamic>> _post;
@override
void initState() {
super.initState();
_post = fetchPost();
}
@override
Widget build(BuildContext context) {
return FutureBuilder(
future: _post,
builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) {
if (snapshot.hasData) {
// Data fetched successfully, display your data here
return Column(
children: <Widget>[
Text(snapshot.data['title']),
Text(snapshot.data['content'])
],
);
} else if (snapshot.hasError) {
// If something went wrong
return Text('Something went wrong...');
}
// While fetching, show a loading spinner.
return CircularProgressIndicator();
}
)
}
}
The builder
function gets two parameters. The BuildContext, and an AsyncSnapshot. The AsyncSnaphot object, which we defined as snapshot
, has the hasData
and hasError
properties, which very useful to check if there is an error. Finally, while the request is still going on, we can display a loading spinner.