Tutorial

How To Use HTTP Requests in Flutter

Flutter

Introduction

Applications frequently need to perform POST and GET and other HTTP requests.

Flutter provides an http package that supports making HTTP requests.

In this article, you will create an example Flutter app that uses the http package to perform HTTP requests to display placeholder information.

Prerequisites

To complete this tutorial, you will need:

This tutorial was verified with Flutter v1.22.2, Android SDK v30.0.2, and Android Studio v4.1.

Step 1 — Setting Up the Project

In order to follow along with the setup, you will be creating an example Flutter app.

Once you have your environment set up for Flutter, you can run the following to create a new application:

  • flutter create flutter_http_example

Navigate to the new project directory:

  • cd flutter_http_example

Using flutter create will produce a demo application that will display the number of times a button is clicked.

Open pubspec.yaml in your code editor and add the following plugin:

pubspec.yaml
dependencies:
  flutter:
    sdk: flutter

  http: ^0.12.0+2

This is an official Flutter plugin published by dart.dev and it has a 100% health score, therefore, you can trust the reliability of this plugin.

Step 2 — Handling GET Requests

Your first task will be to create a class that you can use to interact with the API.

Open your code editor and create a http_service.dart file in the lib directory. Here, you will develop a new HttpService class and add a getPosts function:

lib/http_service.dart
import 'dart:convert';
import 'package:http/http.dart';
import 'post_model.dart';

class HttpService {
  final String postsURL = "https://jsonplaceholder.typicode.com/posts";

  Future<List<Post>> getPosts() async {
    Response res = await get(postsURL);

    if (res.statusCode == 200) {
      List<dynamic> body = jsonDecode(res.body);

      List<Post> posts = body
        .map(
          (dynamic item) => Post.fromJson(item),
        )
        .toList();

      return posts;
    } else {
      throw "Unable to retrieve posts.";
    }
  }
}

In this example, you will be connecting to JSON Placeholder. This code uses the http package’s get on the postsURL string.

If that request was successful, this code will return a List<Post> using Post.fromJson. Otherwise, an error message is thrown.

Note: HTTP status code are used to determine if a request was successful or unsuccessful. A status code of 200 represents a successful HTTP request.

Then, use your code editor to create a post_model.dart file in the lib directory. Here, you will develop a new Post class:

lib/post_model.dart
import 'package:flutter/foundation.dart';

class Post {
  final int userId;
  final int id;
  final String title;
  final String body;

  Post({
    @required this.userId,
    @required this.id,
    @required this.title,
    @required this.body,
  });

  factory Post.fromJson(Map<String, dynamic> json) {
    return Post(
      userId: json['userId'] as int,
      id: json['id'] as int,
      title: json['title'] as String,
      body: json['body'] as String,
    );
  }
}

In order to serialize the response from JSON Placeholder, this code will return a new Post with the fromJson method based on a JSON Map.

Note: In a production application, a package like json_serializable could be used to handle the serialization automatically.

A Post returned by JSON Placeholder will consist of a userId, id, title, and body.

Step 3 — Displaying Posts

Next, use your code editor to create a posts.dart file in the lib directory. Here, you will create a PostsPage class which will display the Posts that are returned from the HTTP request to JSON Placeholder:

lib/posts.dart
import 'package:flutter/material.dart';
import 'http_service.dart';
import 'post_model.dart';

class PostsPage extends StatelessWidget {
  final HttpService httpService = HttpService();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Posts"),
      ),
      body: FutureBuilder(
        future: httpService.getPosts(),
        builder: (BuildContext context, AsyncSnapshot<List<Post>> snapshot) {
          if (snapshot.hasData) {
            List<Post> posts = snapshot.data;
            return ListView(
              children: posts
                  .map(
                    (Post post) => ListTile(
                      title: Text(post.title),
                      subtitle: Text("${post.userId}"),
                    ),
                  )
                  .toList(),
            );
          } else {
            return Center(child: CircularProgressIndicator());
          }
        },
      ),
    );
  }
}

This code uses the FutureBuilder widget to interact with the getPosts() function. This allows the code to determine when the List<Post> is ready and act accordingly.

If the snapshot.hasData is false, then the CircularProgressIndicator is displayed. Otherwise, the ListTile with post information is displayed.

In order to observe what you have so far, you will need to replace the code in main.dart.

Open lib/main.dart in your code editor and modify it to use PostsPage:

lib/main.dart
import 'package:flutter/material.dart';
import 'posts.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'HTTP',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: PostsPage(),
    );
  }
}

Compile your code and have it run in an emulator:

Screenshot of the current state of the application displaying a list of post titles and user ids returned by JSON Placeholder

You should observe a list of post titles and user ids returned by JSON Placeholder.

Note: The titles will be excerpts from Lorem Ipsum which is frequently used as placeholder text.

The next step is to create a detail page with more information about the post when a user clicks on a post title.

Step 4 — Displaying PostDetail

If the user taps on the post, your app should navigate the user away to a PostDetail page.

Use your code editor to create a post_detail.dart file in the lib directory. Here, you will create a PostDetail class which will display an individual Post:

lib/post_detail.dart
import 'package:flutter/material.dart';
import 'post_model.dart';

class PostDetail extends StatelessWidget {
  final Post post;

  PostDetail({@required this.post});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(post.title),
      ),
      body: SingleChildScrollView(
        child: Padding(
          padding: const EdgeInsets.all(12.0),
          child: Column(
            children: <Widget>[
              Card(
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.center,
                  children: <Widget>[
                    ListTile(
                      title: Text("Title"),
                      subtitle: Text(post.title),
                    ),
                    ListTile(
                      title: Text("ID"),
                      subtitle: Text("${post.id}"),
                    ),
                    ListTile(
                      title: Text("Body"),
                      subtitle: Text(post.body),
                    ),
                    ListTile(
                      title: Text("User ID"),
                      subtitle: Text("${post.userId}"),
                    ),
                  ],
                ),
              ),
            ],
          ),
        ),
      )
    );
  }
}

This code will display the title, id, body, and userId.

In order to observe what you have so far, you will need to modify posts.dart to support post_detail.dart:

lib/posts.dart
import 'package:flutter/material.dart';
import 'http_service.dart';
import 'post_detail.dart';
import 'post_model.dart';

class PostsPage extends StatelessWidget {
  final HttpService httpService = HttpService();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Posts"),
      ),
      body: FutureBuilder(
        future: httpService.getPosts(),
        builder: (BuildContext context, AsyncSnapshot<List<Post>> snapshot) {
          if (snapshot.hasData) {
            List<Post> posts = snapshot.data;
            return ListView(
              children: posts
                  .map(
                    (Post post) => ListTile(
                      title: Text(post.title),
                      subtitle: Text("${post.userId}"),
                      onTap: () => Navigator.of(context).push(
                        MaterialPageRoute(
                          builder: (context) => PostDetail(
                            post: post,
                          ),
                        ),
                      ),
                    ),
                  )
                  .toList(),
            );
          } else {
            return Center(child: CircularProgressIndicator());
          }
        },
      ),
    );
  }
}

Compile your code and have it run in an emulator:

Screenshot of the current state of the application displaying the detail of an individual post returned by JSON Placeholder

The next step is to add the ability to remove a post by deleting it.

Step 5 — Handling DELETE Requests

Another example of an HTTP request is the use of the DELETE method.

Revisit http_service.dart in your code editor and create a deletePost(int id) method:

lib/http_service.dart
import 'dart:convert';
import 'package:http/http.dart';
import 'post_model.dart';

class HttpService {
  final String postsURL = "https://jsonplaceholder.typicode.com/posts";

  // ...

  Future<void> deletePost(int id) async {
    Response res = await delete("$postsURL/$id");

    if (res.statusCode == 200) {
      print("DELETED");
    } else {
      throw "Unable to delete post.";
    }
  }
}

Revisit post_detail.dart in your code editor and add an IconButton to the actions array within the AppBar. When the icon is pressed, the associated post should be deleted:

lib/post_detail.dart
import 'package:flutter/material.dart';
import 'http_service.dart';
import 'post_model.dart';

class PostDetail extends StatelessWidget {
  final HttpService httpService = HttpService();
  final Post post;

  PostDetail({@required this.post});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(post.title),
        actions: <Widget>[
          IconButton(
            icon: Icon(Icons.delete),
            onPressed: () async {
              await httpService.deletePost(post.id);
              Navigator.of(context).pop();
            },
          )
        ],
      ),
      // ...
    );
  }
}

Compile your code and have it run in an emulator.

When you visit a post detail page, you will see a Delete icon button in the AppBar. Pressing on the button will print a message in the console:

Output
flutter: DELETED

This will represent a delete request. Due to the limitations of JSON Placeholder and this example application, the post will not actually be deleted.

Conclusion

In this article, you learned how to interact with the Flutter http package. This allowed you to GET a list of posts and DELETE an individual post.

Similar operations such as post, put, patch, and so on are also available. Consult the official documentation for more information.

Creative Commons License