Flutter REST API Example With Dart's Async/Await and Future API
In this tutorial, you'll learn how to get started with async programming in Dart and Flutter with a REST API call example.
We'll learn about asynchronous programming and how Dart deals with async operations either using the async/await
keywords or the Future API.
Finally, we implemented a simple example of a REST API call to an Open API with Flutter and ListView.builder
.
We'll be using an Open API which returns the number of people currently in space, their names and respective craft.
Async Programming in Flutter Using Aync/Await and Future
Asynchronous programming is a form of parallel programming that allows a unit of work to run on a different thread than the main application. The thread runs independently from the main app thread and notifies it when it reaches completion or ends in a failure.
This approach allows us to do time heavy jobs that if executed on the main thread would bog down the application performance.
Flutter REST API Example
We are using REST API calls in this tutorial as an example for async programming for two reasons:
- REST API calls are a very common use case and have a variety of applications.
- REST API calls can take up time for various reasons like network issues on the client or server ends which makes it ideal to deal with asynchronous programming and highlight its benefits.
Dart’s Support for Async Programming with Futures
Dart code runs in a single thread of execution. Hence tasks that take time to complete (typically more than 10ms) are generally running on a different thread.
Dart deals with it by objects of the Future<T>
class where T
is the type of the final result of the asynchronous operation.
You can think of Futures as return type of async functions. Whenever the code reaches an async operation it suspends its execution and carries on with the rest of the sync code while the async operation runs on a different thread.
Upon completion a Future is returned with the value of the completed operation.
Note: if the value of the returned value is unusable, use
Future<void>
as the return type.
Implementing Future<T>
in Dart
Dart provides two ways to implement Futures in your code:
- Using the
async
andawait
keywords, - Using the Future API.
For this tutorial, we will be using the async and await approach.
Dart's Async
The async
keyword is used before the body of a function to define it as an asynchronous operation with a return type of Future<T>
.
Dart's Await
The await
keyword can only be used inside of an async function. Inside an async function, the code runs synchronously until it encounters an await upon which it suspends execution until the statement’s completion.
We will understand this better through our API call example.
The REST API Call Example
REST API calls are generally made by making GET and POST requests to a server that typically responds with a JSON file.
To keep this article focused on Flutter, we will take a simple open API to which we will make a GET request.
This is an open API which returns the number of people currently in space, their names and respective craft.
So, without further ado, let’s begin.
Importing the Dependencies
You need to import the following dependencies.
import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
Modelling the JSON Response to a Dart Object
Here, we make an object to deal with the JSON response. For now, we will just display the number of people in the spaceship. You can see the bonus section to see how we implement ListView.builder
to show each person’s name and craft.
class Post {
final int number;
Post({this.number,this.people});
factory Post.fromJson(Map<String, dynamic> json) {
return Post(
number: json['number'],
);
}
}
Most of the code is fairly simple. We just declared the variables we want to use and add a constructor. The interesting part is the used fromJson()
factory method.
This factory method is used to initialize the class with the parameter values of a JSON file:
factory Post.fromJson(Map<String, dynamic> json) {
return Post(
number: json['number'],
);
The constructor takes a Map
object as input from the JSON file and assigns the class variables their respective values.
Making the GET Request
The GET request is a time intense procedure, so we will make it inside an async function:
Future<Post> fetchPost() async {
final response = await http.get('http://api.open-notify.org/astros');
if (response.statusCode == 200) {
// If the call to the server was successful, parse the JSON.
return Post.fromJson(json.decode(response.body));
} else {
// If that call was not successful, throw an error.
throw Exception('Failed to load post');
}
}
Here as we see, fetchPost()
is first declared async
with a Future<Post>
return value.
For now, it will be run synchronously until the code reaches an await
statement:
final response = await http.get('http://api.open-notify.org/astros');
Here is where the magic happens. The http.get()
method makes a GET call to the server and the execution of the function is suspended till the call is completed.
Whenever you make a request, the response contains a status code with each code representing a different meaning. 200 is code for successful request.
We check for success and throw an exception if not successful.
On success, we initialize a Post
variable from the JSON response in response.body
and then we return this variable.
Calling the Function
void main() => runApp(MyApp(post: fetchPost()));
The MyApp
widget has a variable of class Future<Post>
which is initialized when it is called by main()
.
In its build()
function, we return a MaterialApp
with a home Scaffold
that has a FutureBuilder
as its body.
FutureBuilder
is a special builder provided by Flutter to deal with futures and their response cycle.
FutureBuilder
has two essential properties, a future that takes a Future object and a builder that takes a function with context and snapshot. It also returns a widget.
- context: contains the current context and snapshot contains the response from the Future.
- snapshot: contains the response from the futures response cycle.
body: Center(
child: FutureBuilder<Post>(
future: post,
builder: (context, snapshot) {
if (snapshot.hasData) {
return Text("Number of people in space: ${snapshot.data.number}");
});
} else if (snapshot.hasError) {
return Text("${snapshot.error}");
}
// By default, show a loading spinner.
return CircularProgressIndicator();
},
),
),
),
Inside the builder, we check if the snapshot has any data. If it does then we return a center aligned text widget with the number of people it has.
Otherwise we return a CircularProgressIndicator
which is a library widget provided by flutter that shows a Circular loading bar to the user.
That’s all folks! This is a small implementation of the async features in Flutter. In the bonus section you can find a more formatted implementation using ListView.builder
.
Example with ListView.builder
The ListView.builder
widget is used to display dynamic data in a list-form. This is an equivalent of ListView
in android.
To implement this, we just need to remodel our Post
class and change the widget returned by FutureBuilder
.
Remodeling the Post Class
For this we will add a List
called people that will store Maps with the person’s name and craft:
class Post {
final int number;
final List<dynamic> people;
Post({this.number,this.people});
factory Post.fromJson(Map<String, dynamic> json) {
return Post(
number: json['number'],
people: json['people'],
);
}
}
Next, we will edit the factory constructor as follows:
factory Post.fromJson(Map<String, dynamic> json) {
return Post(
number: json['number'],
people: json['people'],
);
}
}
Implementing ListView.builder
Here, we will replace the Text
widget with the ListView.builder
widget:
return ListView.builder(
itemCount: snapshot.data.number,
itemBuilder: (context,index){
return ListTile(
title: Text(snapshot.data.people[index]['name']),
subtitle: Text(snapshot.data.people[index]['craft'],
);
});
ListView.builder
is a fairly simple widget to understand. It takes an ItemCount
that we provide through snapshot.data.number
(The number of people in space).
This ItemCount
is the number of Items that will be in the List.
The next part is ItemBuilder
. This tells Flutter how each item in the list will be built. It takes two arguments:
- context: the current context.
- index: the current index of the item being built.
In the ItemBuilder
, we return a ListTile
with the title having the name and the subtitle having the craft of every person currently in space:
body: Center(
child: FutureBuilder<Post>(
future: post,
builder: (context, snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemCount: snapshot.data.number,
itemBuilder: (context,index){
return ListTile(
title: Text(snapshot.data.people[index]['name']),
subtitle: Text(snapshot.data.people[index]['craft'],
);
});
});
} else if (snapshot.hasError) {
return Text("${snapshot.error}");
}
// By default, show a loading spinner.
return CircularProgressIndicator();
},
),
),
),
This is a screenshot of the example:
Hope that helped and you enjoyed this tutorial. Flutter is an amazing upcoming framework that I truly love and I hope you have fun developing in it, like I did.
Conclusion
In this tutorial, we've learned about asynchronous programming and how Dart deals with async operations either using the async/await keywords or the Future API. Finally, we implemented a simple example of a RESR API call to an Open API with Flutter and ListView.builder
.
-
Date: