Beginner-Friendly Implementation of the HTTP Methods (Get, Post, Put, and Delete) from APIs in Flutter.

Chikezirim Orioha
11 min readFeb 24, 2024

--

Introduction

HTTP which stands for Hyper-Text Transfer Protocol is a protocol that helps us to communicate over the internet, communication in the sense that we can send data from a source to a destination, retrieve or get data from a source(server), update an existing data and delete it when we wish to. For us to successfully carry out these tasks, we have to rely on some of the HTTP paradigms, which include but not limited to the get method, the post, put, patch, read and delete methods. API which stands for Application Programming Interface can be referred to as data source or location, where structured data are kept which can be accessed from different platforms for software development, given that you have the right credentials to access the data.

In this article we shall set our focus on the simple ways on how one can implement some of the basic HTTP methods, which includes the get, post, put and delete methods, using Flutter which is a cross-platform framework built with Dart.

Key takeaways

  • Create and Run Flutter project.
  • Implement http in a project.
  • Communicate with data from an API.

Prerequisites:

To follow up this article you need to have these:

  • A computer with Flutter setup on it.
  • Basic programming knowledge.
  • Understand the flutter file structure.
  • Working internet.
  • Code editor or an IDE(Visual Studio Code or Android Studio)

With these we are set to dive into the application of these methods.

Table of Contents:

  • Introduction
  • Key Takeaways
  • Prerequisites
  • Getting Started
  • The Get Method
  • The Post Method
  • The Put Method
  • The Delete Method
  • Final Output
  • Conclusion

Getting Started

To start, we have to create a new Flutter project. By following this article, it is assumed that you have Flutter set up on your computer; if you have not, you can follow this link to get Flutter on your computer. Once you have this ready, go into your preferred IDE and open your terminal (ctrl+` opens the terminal on VSCode). Type the following in the terminal.

flutter create http_example

This will create a new Flutter project with the required files and folders.

Head into the lib folder and open the main.dart file. You can see a demo counter app code; clear it so we can create what we need.

In your project home directory, open the pubspec.yaml file and paste the latest version of the http package for Flutter, under dependencies under Flutter.

dependencies:
flutter:
sdk: flutter
http: ^1.2.0

In this article, I’m using the above version, but make sure to go to pub.dev and copy the latest version of the package. After that, click on the “Get Packages” arrow-down icon at the top right-hand corner of vscode to get the package into your project.

The package can also be gotten into our project when we use,

flutter pub get

in the terminal, after we have pasted the package in the pubspec.ymal file.

After that, update your lib/main.dart file with the code below, importing the http package.

import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});

Widget build(BuildContext context) {
return MaterialApp(
);
}
}

The Get Method:

The get method retrieves resources from a source to the destination (application). This method is very important, as it helps to populate our application with data that would have been bulky to keep in our codebase. It can be used to load many forms of data, ranging from texts, images, and files.

For us to appreciate what we want to achieve, we will change the state of the application from a StatelessWidget to a StatefulWidget, whereby the state of the application can be rebuilt given some specified actions carried out in the application. Our code will now look like the below code after we have changed it to a StatefulWidget.

void main() {
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({super.key});
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
Widget build(BuildContext context) {
return MaterialApp(

);
}
}

The data we will get for this article is a list of dog images from the dog ceo API.

We will create a class and name it Dogmodel, where we will accept a list of messages from the API, which is in JSON format. Our Dogmodel class will look like the code below.

class Dogmodel {
final List message;
var dogRange = List;
Dogmodel({required this.message});
factory Dogmodel.fromJson(dynamic json) {
return Dogmodel(message: json['message'] as List);
}
}

The variable dogRange is used to control the number of dog images that will be retrieved, as we are looking at retrieving only a few images from the API.

In your class _MyAppState, create an asynchronous function that, when the response from the Uri has a status code of 200, will return the Dogmodel we created and decode the body of the response from json with the jsonDecode() function. The code below will get images of the dog breed beagle, but if something goes wrong, an exception will be thrown.

import 'dart:convert';
Future<Dogmodel> getDog() async {
final response =
await http.get(Uri.parse('https://dog.ceo/api/breed/beagle/images'));
if (response.statusCode == 200) {
return Dogmodel.fromJson(jsonDecode(response.body));
} else {
throw Exception("could not fetch data");
}
}

We then proceed to declare a variable called ‘get’ with a Future type of the Dogmodel, and assign the ‘getDog()’ function to the ‘get’ variable in an ‘initState()’ function.

@override
void initState() {
super.initState();
get = getDog();
}

After that, we now build our UI and retrieve a list of 10 dog images in a card using a ‘FutureBuilder<Dogmodel>()’ widget and a ‘ListView.builder()’.

@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(
child: FutureBuilder<Dogmodel>(
future: get,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const CircularProgressIndicator();
} else if (snapshot.hasData) {
List dogRange = snapshot.data!.message.sublist(0,10);
int dogRangelen = dogRange.length;
return ListView.builder(
itemCount: dogRangelen,
itemBuilder: (context, index) {

return Column(
children: [
Card(
margin: const EdgeInsets.all(8.0),
elevation: 4,
shape: const RoundedRectangleBorder(
borderRadius:
BorderRadius.all(Radius.circular(6))),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
Image.network(
snapshot.data!.message[index]),
],
),
),
),
],
);
});
} else {
print("Error: ${snapshot.error}");
throw Exception("Could not fetch data");
}
}),
),
),
);
}

You can go ahead and test the app by running:

flutter run -d yourdevice

The ‘yourdevice’ can be any platform you want to test your application on; it may be Chrome, Linux, Windows, iPhone, Android emulator, or your attached physical Android device.

Or you can simply type:

flutter run

in the terminal, then choose a device from the list of devices in the terminal.

Output

Output of http get method from DogCeo

The Post Method:

The post method is similar to the create keyword, which is used in database management and represents the C in CRUD (Create, Read, Update, and Delete). The post method helps send resources from a user end to the server, and in turn, if this request is successful, there will be a 201 response; if not, an error or exception will be thrown. We will use a free API from Jsonplaceholder and show a success message in a snackBar if the request was successful and show an an error message if something went wrong. This will help us understand this concept better.

To start, we will create a button in the appBar, which will lead us to the page where we will implement the post, put, and delete methods with the code below.

appBar: AppBar(
actions: [
Padding(
padding: const EdgeInsets.all(8.0),
child: ElevatedButton(
style: ElevatedButton.styleFrom(backgroundColor: Colors.purpleAccent,shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(6))),
onPressed: () {
Navigator.push(context, MaterialPageRoute(builder: (BuildContext context)=> const OtherMethods()));
}, child: const Text("Other Methods",style: TextStyle(color: Colors.white),)),
)
],),

Then we create an asynchronous function to post data.

Future<void> postData() async {
var response = await http.post(
Uri.parse('https://jsonplaceholder.typicode.com/posts'),
headers: {"content-type": "application/json"},
body: jsonEncode({
"userId": '2',
"title": 'Introduction to API',
"body": 'This is just a demo of what you can do'
}));
if (response.statusCode == 201) {
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
behavior: SnackBarBehavior.floating,
backgroundColor: Color.fromARGB(255, 122, 245, 126),
duration: Duration(seconds: 7),
content: Text("Post Created Successfuly"),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(36))),
showCloseIcon: true,
dismissDirection: DismissDirection.horizontal,
closeIconColor: Colors.white,
));
} else {
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
behavior: SnackBarBehavior.floating,
content: Row(
children: [
Icon(
Icons.warning_amber,
color: Colors.yellow,
),
SizedBox(
width: 18,
),
Text("Something went wrong!")
],
),
backgroundColor: Colors.red,
));
throw Exception("Could not Post Data");
}
}

This function will be passed into a setState() function in a button.

Padding(
padding: const EdgeInsets.all(12.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: SizedBox(
width: double.maxFinite,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Colors.purpleAccent,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(6))),
onPressed: () {
setState(() {
postData();
});
},
child: const Text(
"POST",
style: TextStyle(color: Colors.white),
)),
),
),

The Put Method:

The put method is used to update data on the server, and the code to implement it is seen below. A putData() asynchronous function was created and also passed into a button, so that when clicked it will update the data under the given userId.

Future<void> putData() async {
var response = await http.put(
Uri.parse('https://jsonplaceholder.typicode.com/posts/2'),
headers: {"content-type": "application/json"},
body: jsonEncode({
"userId": '2',
"title": 'Introduction to API',
"body":
'You have learnt some of these concepts now is the time to apply them.'
}));
if (response.statusCode == 200) {
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
behavior: SnackBarBehavior.floating,
backgroundColor: Color.fromARGB(255, 122, 245, 126),
duration: Duration(seconds: 7),
content: Text("Post Updated Successfuly"),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(36))),
showCloseIcon: true,
dismissDirection: DismissDirection.horizontal,
closeIconColor: Colors.white,
));
} else {
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
behavior: SnackBarBehavior.floating,
content: Row(
children: [
Icon(
Icons.warning_amber,
color: Colors.yellow,
),
SizedBox(
width: 18,
),
Text("Something went wrong!")
],
),
backgroundColor: Colors.red,
));
throw Exception("Could not update Data");
}
}

This function is then passed into the button below.

Padding(
padding: const EdgeInsets.all(8.0),
child: SizedBox(
width: double.maxFinite,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Colors.purpleAccent,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(6))),
onPressed: () {
setState(() {
putData();
});
},
child: const Text(
"PUT",
style: TextStyle(color: Colors.white),
)),
),
),

The Delete Method

The delete method, as the name goes, helps to remove resources from the server. This is achieved by creating an asynchronous function and using the http.delete() method to implement the delete. The implementation can be seen below.

Future<void> deleteData() async {
var response = await http.delete(Uri.parse('https://jsonplaceholder.typicode.com/posts/${3}'));
}

If the asynchronous function returns a response of a 200 status code, which means the delete operation was successful, then a SnackBar() which contains the success message will be shown, which is implemented with the code below.

if (response.statusCode == 200) {
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
behavior: SnackBarBehavior.floating,
backgroundColor: Colors.green,
duration: Duration(seconds: 7),
content: Text("Post delete Success"),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(36))),
showCloseIcon: true,
dismissDirection: DismissDirection.horizontal,
closeIconColor: Colors.white,
));
}

If there was an error when you wanted to carry out the delete operation, then a SnackBar() which contains the error message will be shown, and an Exception() message will be printed in the terminal, which is implemented with the code below.

else {
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
behavior: SnackBarBehavior.floating,
content: Row(
children: [
Icon(
Icons.warning_amber,
color: Colors.yellow,
),
SizedBox(
width: 18,
),
Text("Something went wrong!")
],
),
backgroundColor: Colors.red,
));
throw Exception("Could not delete Data");
}

This deleteData() function is also passed into a button, so we can test it from our app’s interface. This is achieved with the code below.

Padding(
padding: const EdgeInsets.all(8.0),
child: SizedBox(
width: double.maxFinite,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Colors.purpleAccent,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(6))),
onPressed: () {
setState(() {
deleteData();
});
},
child: const Text(
"DELETE",
style: TextStyle(color: Colors.white),
)),
),
),

The full codes can be seen below.

import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'package:http_example/other_methods.dart';
void main() {
runApp(const MaterialApp(
debugShowCheckedModeBanner: false,

home: MyApp()));
}
class MyApp extends StatefulWidget {
const MyApp({super.key});
@override
State<MyApp> createState() => _MyAppState();
}
class Dogmodel {
final List message;
var dogRange = List;
Dogmodel({required this.message});
factory Dogmodel.fromJson(dynamic json) {
return Dogmodel(message: json['message'] as List);
}
}
class _MyAppState extends State<MyApp> {
Future<Dogmodel> getDog() async {
final response =
await http.get(Uri.parse('https://dog.ceo/api/breed/beagle/images'));
if (response.statusCode == 200) {
return Dogmodel.fromJson(jsonDecode(response.body));
} else {
throw Exception("could not fetch data");
}
}
late Future<Dogmodel> get;
@override
void initState() {
super.initState();
get = getDog();
}
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,

home: Scaffold(
appBar: AppBar(
actions: [
Padding(
padding: const EdgeInsets.all(8.0),
child: ElevatedButton(
style: ElevatedButton.styleFrom(backgroundColor: Colors.purpleAccent,shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(6))),
onPressed: () {
Navigator.push(context, MaterialPageRoute(builder: (BuildContext context)=> const OtherMethods()));
}, child: const Text("Other Methods",style: TextStyle(color: Colors.white),)),
)
],),
body: Center(
child: FutureBuilder<Dogmodel>(
future: get,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const CircularProgressIndicator();
} else if (snapshot.hasData) {

List dogRange = snapshot.data!.message.sublist(0, 10);
int dogRangelen = dogRange.length;

return ListView.builder(
itemCount: dogRangelen,
itemBuilder: (context, index) {
return Column(
children: [
Card(
margin: const EdgeInsets.all(8.0),
elevation: 4,
shape: const RoundedRectangleBorder(
borderRadius:
BorderRadius.all(Radius.circular(6))),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
children: [
Image.network(
snapshot.data!.message[index]),
],
),
),
),

],
);
});
} else {
print("Error: ${snapshot.error}");
throw Exception("Could not fetch data");
}
}),
),
),
);
}
}
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
class OtherMethods extends StatefulWidget {
const OtherMethods({super.key});
@override
State<OtherMethods> createState() => _OtherMethodsState();
}
class Datamodel {}
class _OtherMethodsState extends State<OtherMethods> {
@override
Widget build(BuildContext context) {


return SafeArea(
child: Scaffold(
body: Center(
child: Padding(
padding: const EdgeInsets.all(12.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: SizedBox(
width: double.maxFinite,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Colors.purpleAccent,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(6))),
onPressed: () {
setState(() {
postData();
});
},
child: const Text(
"POST",
style: TextStyle(color: Colors.white),
)),
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: SizedBox(
width: double.maxFinite,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Colors.purpleAccent,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(6))),
onPressed: () {
setState(() {
deleteData();
});
},
child: const Text(
"DELETE",
style: TextStyle(color: Colors.white),
)),
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: SizedBox(
width: double.maxFinite,
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Colors.purpleAccent,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(6))),
onPressed: () {
setState(() {
putData();
});
},
child: const Text(
"PUT",
style: TextStyle(color: Colors.white),
)),
),
),
],
),
),
),
),
);
}
Future<void> postData() async {
var response = await http.post(
Uri.parse('https://jsonplaceholder.typicode.com/posts'),
headers: {"content-type": "application/json"},
body: jsonEncode({
"userId": '2',
"title": 'Introduction to API',
"body": 'This is just a demo of what you can do'
}));
if (response.statusCode == 201) {
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
behavior: SnackBarBehavior.floating,
backgroundColor: Color.fromARGB(255, 122, 245, 126),
duration: Duration(seconds: 7),
content: Text("Post Created Successfuly"),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(36))),
showCloseIcon: true,
dismissDirection: DismissDirection.horizontal,
closeIconColor: Colors.white,
));
} else {
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
behavior: SnackBarBehavior.floating,
content: Row(
children: [
Icon(
Icons.warning_amber,
color: Colors.yellow,
),
SizedBox(
width: 18,
),
Text("Something went wrong!")
],
),
backgroundColor: Colors.red,
));
throw Exception("Could not Post Data");
}
}
Future<void> deleteData() async {
var response = await http
.delete(Uri.parse('https://jsonplaceholder.typicode.com/posts/${3}'));
if (response.statusCode == 200) {
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
behavior: SnackBarBehavior.floating,
backgroundColor: Colors.green,
duration: Duration(seconds: 7),
content: Text("Post delete Success"),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(36))),
showCloseIcon: true,
dismissDirection: DismissDirection.horizontal,
closeIconColor: Colors.white,
));
} else {
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
behavior: SnackBarBehavior.floating,
content: Row(
children: [
Icon(
Icons.warning_amber,
color: Colors.yellow,
),
SizedBox(
width: 18,
),
Text("Something went wrong!")
],
),
backgroundColor: Colors.red,
));
throw Exception("Could not delete Data");
}
}

Future<void> putData() async {
var response = await http.put(
Uri.parse('https://jsonplaceholder.typicode.com/posts/2'),
headers: {"content-type": "application/json"},
body: jsonEncode({
"userId": '2',
"title": 'Introduction to API',
"body":
'You have learnt some of these concepts now is the time to apply them.'
}));
if (response.statusCode == 200) {
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
behavior: SnackBarBehavior.floating,
backgroundColor: Color.fromARGB(255, 122, 245, 126),
duration: Duration(seconds: 7),
content: Text("Post Updated Successfuly"),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(36))),
showCloseIcon: true,
dismissDirection: DismissDirection.horizontal,
closeIconColor: Colors.white,
));
} else {
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
behavior: SnackBarBehavior.floating,
content: Row(
children: [
Icon(
Icons.warning_amber,
color: Colors.yellow,
),
SizedBox(
width: 18,
),
Text("Something went wrong!")
],
),
backgroundColor: Colors.red,
));
throw Exception("Could not update Data");
}
}
}

You can then run the app by using the below command in your terminal.

flutter run

Final Output

Conclusion

You have successfully learned how to use the HTTP methods to implement API calls from two different endpoints, where we implemented the get, post, put, and delete methods in Flutter, and also learned how to build an asynchronous function with an approach that is suitable for a beginner. Thanks for reading.

You can check out the full code on GitHub.

--

--