GET : /POSTS/1

Let’s check how our response would look like when we hit this API endpoint. You can use Postman or just paste this link in your browser. Remember, if you are working with APIs that require authentication and other HTTP headers, it’s recommended to use Postman.

Let’s make a model class for this JSON structure.

Here’s a little tip. You can either use all your knowledge you gained from my Parsing Complex JSON article, or you can save your time by using this little converter tool. I just discovered it recently, and I’m in love. 😍

Remember to change the name of your class and Source Type. Don't forget to change the language to Dart.

This tool will create your model classes, the factory methods, and the converter methods. Should look like this.

Let’s just focus on our postFromJson method.

Post postFromJson(String str) {

final jsonData = json.decode(str);

return Post.fromJson(jsonData);

}

The str is just our JSON string. After decoding str , jsonData looks like this

No formatting, just a Map of <String, dynamic>

This is fed into Post.fromJson so that this factory method can create a new Post object for you that you can use in your application.

Hey, why are we sending a Map to the Post.fromJson?

factory Post.fromJson(Map<String, dynamic> json){

...

}

Yes. Because Post.fromJson requires an argument of the type Map<String, dynamic> . Duh.

Now let’s call our API

I mostly have the API call methods in a different file, suppose a services.dart

String url = 'https://jsonplaceholder.typicode.com/posts';



Future<Post> getPost() async{

final response = await http.get('$url/1');

return postFromJson(response.body);

}

Note: Don't forget to import the required packages. Check this file for the imports required.

Woman, explain the code!

So till now, we were talking about a JSON string, but we don’t even have it yet. Because we never called the API. So let’s do that work first.

The getPost() method will call the API endpoint which is defined in url . And we will receive a JSON string in response.body , which we have to send to postFromJson so that it can do its conversion magic.

But why is the return type Future<Post> and not Post?

Well, that’s right.

We are doing a network call. So obviously we won’t have a response as soon as we call the API. It will take some time. By definition, A Future is used to represent a potential value, or error, that will be available at some time in the future. Since our response will also be available sometime in the future, we use Futures.

Since we have a network call, we would obviously want an asynchronous way of calling an API. That is where we need async and await . In simple words, async is a keyword that makes your method asynchronous. In an async function, when we stumble upon await, the following expression is evaluated and the currently running function is suspended until we get our result. In this case, until we get our success or error response.

What about building a UI for the response I got?

Yes, I was getting there. Obviously, if we have our response in the future, the UI depending on the response should also be in the future.

Why?

Because, your UI will be built as soon as the app runs, but you won’t get your API response as soon as your app runs. So if your UI depends on API response values, then it will throw you a lot of null errors.

And to solve this problem, we have…

The Future of Futures : FutureBuilder

Simply put, use a FutureBuilder to build a widget when there are Futures involved. Let’s add the following lines of code in your UI’s build function.

FutureBuilder<Post>(

future: getPost(),

builder: (context, snapshot) {

return Text('${snapshot.data.title}');

}

)

The FutureBuilder is also a widget, so you can either have it attached to your Scaffold directly, or attach it as a child to any widget you like.

FutureBuilder has two main properties — future and builder. future needs a future and getPost() returns a future .

So future will call the getPost() method, it will do its network call magic and return the result to snapshot of builder . Now simply create whatever widget you like with the result given. Listview? Containers with Text? Really whatever!

Note: Here FutureBuilder has the type <Post> which is also the return type of getPost() . So whatever type is returned by your future function, that should be the type of your FutureBuilder.

Now what if I want a behaviour like this. While I am waiting for the results, I want to show the users a CircularProgressIndicator and as soon as the result is available, show the Text widget.

FutureBuilder makes this easy for you too.

if(snapshot.connectionState == ConnectionState.done)

return Text('Title from Post JSON : ${snapshot.data.title}');

else

return CircularProgressIndicator();

And suppose I want to show a particular UI for error situations like No Internet Connection?

if(snapshot.connectionState == ConnectionState.done) {

if(snapshot.hasError){

return ErrorWidget();

}

return Text('Title from Post JSON : ${snapshot.data.title}');

}