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.
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 .
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.
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.
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(),
);
}
}
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.
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();
},
)
],
),
//
)
}
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.
#flutter #api