CRUD Operations with Flutter and Node.js (POST REST API)

Saransh Singhal
7 min readSep 3, 2023

--

This article will help you learn about :

  1. How to Post data from Flutter to node back-end.
  2. How to setup emulator to post data to local network.

This is 2nd part of the Flutter and Node.js series, make sure you have read the first article for better understandings :

Let’s start of by giving functionality to the POST button :

  1. In the flutter create a new file post.dart and add 3 text-fields in it naming product-name, product-price, product-desc and an elevated POST button.
  2. Here’s the code for the following :
import 'package:flutter/material.dart';

class POST extends StatefulWidget {
const POST({super.key});

@override
State<POST> createState() => _POSTState();
}

TextEditingController name = TextEditingController();
TextEditingController price = TextEditingController();
TextEditingController desc = TextEditingController();

class _POSTState extends State<POST> {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
padding: const EdgeInsets.all(10),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextField(
controller: name,
decoration: const InputDecoration(label: Text("Product-name")),
),
const SizedBox(
height: 50,
),
TextField(
controller: price,
decoration: const InputDecoration(label: Text("Product-price")),
),
const SizedBox(
height: 50,
),
TextField(
controller: desc,
decoration: const InputDecoration(label: Text("Product-desc")),
),
const SizedBox(height: 50,),
ElevatedButton(onPressed: (){}, child: const Text("POST"))
],
),
),
);
}
}

(here name, price, desc are the 3 controllers which are used to fetch data from the text-fields)

3. In the main.dart, add functionality to the POST button so that when pressed on it you can navigate to post.dart.

Here’s how your main.dart should look like :

import 'package:crud_frontend/post.dart';
import 'package:flutter/material.dart';

void main() {
runApp(const MaterialApp(
title: "APP",
home: MainApp(),
));
}

class MainApp extends StatelessWidget {
const MainApp({super.key});

@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (BuildContext context) {
return const POST();
},
),
);
},
child: const Text("POST")),
ElevatedButton(onPressed: () {}, child: const Text("GET")),
ElevatedButton(onPressed: () {}, child: const Text("UPDATE")),
ElevatedButton(onPressed: () {}, child: const Text("DELETE"))
],
),
),
);
}
}

NOTE : In the main.dart make sure that you add MaterialApp in the void main section of your code so that a Navigator.push can work because we are going from one Parent widget to another Parent widget so they both must be same for reference you can read more at Stack-Overflow.

4. Creating a Data Class, add folder, model > product_model, inside it :

class Product{
final String name;
final String price;
final String desc;

Product(this.name, this.price, this.desc);

}

(It will be used later)

Lets jump back to our backend for now, we’ll come back later to it…

Now follow along the steps for your index.js:

  1. Add the following code to your index.js :
const express = require("express");

const app = express();

app.use(express.json());

app.use(express.urlencoded({extended : true}));

app.listen(2000, ()=>{
console.log("listening to port 2000");
});

const productData = [];

//post api
app.post("/api/add_product", (req,res)=>{
console.log("Result", req.body);

const pdata = {
"id": productData.length + 1,
"name": req.body.name,
"price": req.body.price,
"desc" : req.body.desc

}

productData.push(pdata);
console.log("Final", productData);

res.status(200).send({
"status-code": 200,
"message": "Product added Successfully",
"Product": pdata
});
});

2. An empty list productData is created which will store out data when pushed.

3. Here we are creating a URL api/add_productwhere our data will be pushed.

4. It will be stored in pdata and then this stored value will be pushed to productData

5. And the the end if everything works fine and out status code is 200 then res will send the following data.

Our back-end is now finished to receive data from the font-end.

Moving on to the front-end :

  1. Now create a folder service and inside it create a file http.
  2. This is our service class and is used to make connection between the client and the server.
  3. Inside the file we need to have the url of our server and a function to post the data from front-end. So we create this method like this :
import 'package:http/http.dart' as http;

class Http{
String url = "";

postProduct(Map pdata) async{
try{
final res = await http.post(Uri.parse("${url}add_product"),body: pdata);

if (res.statusCode == 200){

}
else{

}
}
catch(e){
print(e.toString());
}
}
}

Explanation :

  1. Here we take a String to store our URL which will be stored in the next step.
  2. A function postProduct is created which takes Map pdata as a parameter for working.
  3. This method is async because we don’t know how much time can this function take to execute.
  4. Then we have our try and catch block which catches error if any.
  5. As we need to go to the /api/add_product URL that we created in our node file our URL will look like this ${url}add_product

As we are in test mode and we are working on our local machine we need to make our URL from the IP address of our local machine to add it to the above step. To do so :

  1. Open Command Prompt in you pc.
  2. Type ipconfig.

3. From the list of available addresses scroll down and find IP v4 address and copy it. Lets say it to be IPV4

(NOTE : Your IPv4 address should be of format 123.123.123.123)

4. Now your URL will be http://IPV4/api.

Now back to our http class :

import 'package:http/http.dart' as http;

class Http{
String url = "http://<your IPv4 address>/api/";

postProduct(Map pdata) async{
try{
final res = await http.post("${url}add_product",body: pdata);

if (res.statusCode == 200){

}
else{

}
}
catch(e){
print(e.toString());
}
}
}

Now to get the status that our app actually worked we will take the res and print it into out console, and by chance if any error occurs then we will print the else statement.

import 'dart:convert';

import 'package:http/http.dart' as http;

class Http{
static String url = "http://192.168.180.1/api/";

static postProduct(Map pdata) async{
try{
final res = await http.post(Uri.parse("${url}add_product"),body: pdata);

if (res.statusCode == 200){
var data = jsonDecode(res.body.toString());
print(data);
}
else{
print("Failed to load data");
}
}
catch(e){
print(e.toString());
}
}
}

Now our goal is to on the click of our POST button in post.dart, we should send the data :

In the post.dart adding functionality to the button :

  1. When clicked it should take data from the text-fields and convert it into a map.
  2. This map is given to the function postProduct as a parameter
import 'package:crud_frontend/service/http.dart';
import 'package:flutter/material.dart';

class POST extends StatefulWidget {
const POST({super.key});

@override
State<POST> createState() => _POSTState();
}

TextEditingController name = TextEditingController();
TextEditingController price = TextEditingController();
TextEditingController desc = TextEditingController();

class _POSTState extends State<POST> {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
padding: const EdgeInsets.all(10),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
TextField(
controller: name,
decoration: const InputDecoration(label: Text("Product-name")),
),
const SizedBox(
height: 50,
),
TextField(
controller: price,
decoration: const InputDecoration(label: Text("Product-price")),
),
const SizedBox(
height: 50,
),
TextField(
controller: desc,
decoration: const InputDecoration(label: Text("Product-desc")),
),
const SizedBox(height: 50,),
ElevatedButton(onPressed: (){
var data = {
"name": name.text,
"price": price.text,
"desc": desc.text
};

Http.postProduct(data);
}, child: const Text("POST"))
],
),
),
);
}
}

Final step is to setup your emulator or physical device so that they can communicate with the local server, To do so follow these steps:

  1. Go to settings > proxy

2. Click on Apply, your proxy status should display SUCCESS.

Good work !! Our setup is complete and now we need to test our system that if it works or not !?

TESTING :

So for sample testing let’s an example with product name as “bottle”, product price as “20” and product desc as “plastic bottle”

On Clicking the POST button :

  1. In our Flutter project we get :

2. In our Node.js Project we get :

Hence we have SUCCESSFULLY Posted data from frontend to our backend !!

LinkedIn : https://www.linkedin.com/in/saransh-singhal-359082238/

--

--

Saransh Singhal

Full Stack App developer trying to make learning easy and fun