HTTP Requests with Flutter

Paul Halliday

It’s hard to do any sort of development without having to make some form of HTTP request, therefore, we’ll be looking at how to use the http plugin within Flutter.

Creating a New Flutter Project

In order to follow along with the setup we’ll be creating an example Flutter app. Assuming you already have the Flutter and Dart SDKs installed, run the following in your terminal:

# New Flutter application
$ flutter create flutter_http

# Open this up inside of VS Code
$ cd flutter_http && code .

Adding the HTTP plugin

Head over to your pubspec.yaml and add the following plugin:

dependencies:
  flutter:
    sdk: flutter

  http: ^0.12.0+2

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

HTTP Requests

Our first task will be to create a class which we can use to interact with our API. We’ll create a new class named HttpService at lib/http_service.dart and add a getPosts function:

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 "Can't get posts.";
    }
  }
}

As seen from the getPosts function, we’re firstly calling get from the http package on the postsURL.

Next, if that request was successful, we’re parsing the response and returning a List<Post> using Post.fromJson. Let’s go ahead and create the Post class at lib/posts_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, we’re returning a new Post with the fromJson method based on a JSON Map. In a production application, I’d recommend using something like json_serializable to handle the serialization automatically.

Displaying Posts

With this in mind, let’s create a new page named PostsPage at lib/posts.dart:

import 'package:flut_http/http_service.dart';
import 'package:flut_http/post_detail.dart';
import 'package:flut_http/post_model.dart';
import 'package:flutter/material.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());
          }
        },
      ),
    );
  }
}

We’re using a FutureBuilder widget to interact with the getPosts() function. This allows us to determine when the List<Post> is ready and act accordingly.

If the snapshot.hasData is false, then we’re displaying the CircularProgressIndicator, otherwise we’re displaying the ListTile with various post information.

Let’s make sure we update main.dart with the home of PostsPage:

import 'package:flut_http/posts.dart';
import 'package:flutter/material.dart';

void main() => runApp(MyApp());

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

Post List screenshot

Post Detail

If the user taps on the post, we’re looking to navigate the user away to a PostDetail page. Let’s build that:

import 'package:flut_http/post_model.dart';
import 'package:flutter/material.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}"),
                      ),
                    ],
                  ),
                ),
              ],
            ),
          ),
        ));
  }
}

While this page doesn’t have any HTTP data, I’ve elected to add this to display the complete Post data.

Post Detail screenshot

DELETE

Another example of such HTTP request is the use of the delete method. For example, inside of our HttpService we can create a deletePost(int id) method:

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

  if (res.statusCode == 200) {
    print("DELETED");
  } else {
    throw "Can't delete post.";
  }
}

We can add an IconButton to the actions array within the AppBar so that whenever this is tapped a Post is deleted:

final HttpService httpService = HttpService();

@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();
            },
          )
        ],
      ),
    //
  )
}

Wrapping Up

In this article, we looked at how to interact with the Flutter http package. This allowed us to get a list of posts, as well as delete an individual post.

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

  Tweet It

🕵 Search Results

🔎 Searching...

Sponsored by #native_company# — Learn More
#native_title# #native_desc#
#native_cta#