Working with adaptive programmable APIs in Flutter

The translation of the article was prepared in advance of the start Flutter Mobile Developer course

We also invite you to sign up for the open Webinar on “Writing a Flutter Application Using Redux”… At the webinar, participants, together with an expert, will analyze the main features of Redux and write a small application using it, as well as discuss how well Redux scales in the future. Join us!


My previous article Parsing Complex JSON (JavaScript Object Notation) in Flutter got a lot of good feedback from people who start working at Flutter. And one of the most popular Frequently Asked Questions (FAQs) from beginners was: “How to do the same with API requests?”

Your wish has come true, my friend.

Let’s work with simple APIwhich is easily accessible without mandatory authentication.

HTTP methods supported by JSONPlaceholder

… … …

GET: / POSTS / 1

Let’s check what our response will look like when we hit this API endpoint. You can use Postman program or just paste this link into your browser. Remember, if you are working with APIs that require authentication and the transfer of authorization tokens using HTTP headers, the Postman program is recommended.

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

Here’s a quick tip. You can either use all the knowledge gained from my article on parsing a JSON complex (Parsing Complex JSON), or save your time by using this little converter utility… I just recently discovered it and fell in love.

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

This tool will create model classes, factory settings and conversion methods for you. Should look like So

Let’s focus on our postFromJson method.

Post postFromJson(String str) {    
   final jsonData = json.decode(str);    
   return Post.fromJson(jsonData);
}

str Is just our JSON string. After decoding str, jsonData looks like that.

No formatting, just a string (Map).
No formatting, just a string (Map).

It loads into Post.fromJsonso that this standard way can create a new object for you Postthat you can use in your application.

Hey why are we sending the line (Map) in Post.fromJson?

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

Yes, because Post.fromJson requires a type argument Map… And then.

Now let’s call our API

Basically, the API calling tools are in another file, let’s say the file 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 availability of this file for the required import.

Woman, explain the code!

So, so far we’ve talked about a JSON string, but we don’t have one yet. Because we have never accessed the API. So let’s get this job done first.

Way getPost() will call the API endpoint that is defined in url. And we get the JSON string in response.bodywhich we must send to the address postFromJsonso that he can make this transformation magical.

But why the return type Future, but not Post?

Oh well.

We make a network request. Obviously, we will not get an instant response immediately when we call the API. It will take some time. By definition, A Future used to display a potential value, or error, that will be available at some point in the future. Since our answer will also be available some time in the future, we use Futures

Since we have a network request, we obviously need an asynchronous way to call the API. This is where we need async and await… In simple words, async Is the keyword that makes your method asynchronous. IN async functions when we stumble upon await (wait), the next expression is evaluated, and the currently executing function is suspended until we get the result. In this case, until we reach a favorable outcome or an error occurs.

What about creating a user interface (UI) for the response you receive?

Yes, I got to this point. Obviously, if we get our answer in the future, then the response-dependent UI should also be in the future.

Why?

Because your user interface (UI) will be created as soon as the application starts, but you will not receive an API response as soon as your application starts. Thus, if your user interface depends on API response values, it will lead to a lot of erroneous results.

And to solve this problem, we have …

The Future of Futures: FutureBuilder

Simply put, use FutureBuilder to build a widget when it comes to Futures. Let’s add the following lines of code to the build function of your user interface (UI).

FutureBuilder<Post>(
    future: getPost(),
    builder: (context, snapshot) {
        return Text('${snapshot.data.title}');
    }
)

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

FutureBuilder has two main properties – future and builderfuture need an object future and getPost()which returns future

Thus, future will use method getPost()which will make its network call with a wave of a magic wand and return the result to a snapshot from builder. Now just create any widget you like with the desired result. Viewing a list? Storage with text? Anything!

Attention: Here FutureBuilder has a form that is also the return type of the function getPost()… So whatever type your future function returns, it must be the type of your FutureBuilder

Now, what if I want this behavior. While I’m waiting for the results, I want to show users CircularProgressIndicator and, as soon as the result is ready, show the widget Text.тFutureBuilder also makes this task easier.

if(snapshot.connectionState == ConnectionState.done)
  return Text('Title from Post JSON : ${snapshot.data.title}');
else
  return CircularProgressIndicator();

And suppose I want to show a specific user interface 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}');
}

There are other methods such as snapshot.hasData other ConnectionStates, eg ConnectionState.waiting, ConnectionState.active… I would encourage you to try them all out to create better apps.

… … …

POST / posts

Hey, that was a lot of detailed information about the GET request. Can you just quickly tell me how to make a POST request?

Of course, in a POST request, the body of the network access method would look a little different, but otherwise everything is almost the same.

You would create a generic POST response class in the same way. Yours will be built in the same way. FutureBuilder… Let’s see what differences exist in the method of network access.

Future<Post> createPost(Post post) async{
  final response = await http.post('$url',
      headers: {
        HttpHeaders.contentTypeHeader: 'application/json'
      },
      body: postToJson(post)
  );
  return postFromJson(response.body);
}

Now your http.post will take 3 parameters → url (URL endpoint API) headers (HTTP Headers; if applicable) and body (body) (required).

Post object to send with http POST request
Post object to send with http POST request

So you can have such a post object.PostToJson(post) converts your post object to a JSON string ready to be sent to the server. Now use the method createPost on your FutureBuilder and create your Widget!

But I don’t want to create a user interface (UI) for this network request.

There is a possibility of such a scenario. For example, using a login or just a network call that will send some values ​​to the server and return 200 or 400 statusCode, which is the only thing that worries me.

So don’t you want a FutureBuilder anymore?

Then just use .then() method further.

createPost(post).then(
(response){
   
}
)

Use this when you want to access the API. For example, this snippet could be in onPressed function of your button.

Here response is the result we get when createPost receives any response. We can now use this answer to do whatever we want. Maybe go to another screen.

createPost(post).then((response){
    Navigate.of(context).pushNamed('dashboard');
})

But in this situation, he can go to another screen, even if the statusCode is 400, because in any case, a response will be received. (The error is also a valid answer.)

Dot. Let’s say we want to control our logic based on a favorable outcome or error code. Then we have to modify the method createPost

Future<http.Response> createPost(Post post) async{
  //same as previous body
  return response;
}

Now createPost returns the future of type http.Response… This way we can control a lot of our user interface (UI).

createPost(post).then((response){
    if(response.statusCode == 200)
      Navigate.of(context).pushNamed('dashboard');
    else
      print(response.statusCode);
})

Check out the GitHub project to check the examples discussed above: PoojaB26 / ParsingJSON-Flutter

This is all for you beginners! But the ocean is vast, keep exploring!


Learn more about the course Flutter Mobile Developer.

Watch the webinar on the topic “Writing a Flutter Application Using Redux”.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *