In this blog, we will see how to make a realtime chat app using Node.js as the backend and Flutter as the frontend. We will use sockets to communicate between devices.
Prerequisites
The following should be installed and running in your PC.
- Node.js : https://nodejs.org/en/download/
- Flutter : https://flutter.dev/docs/get-started/install
- Heroku CLI : https://devcenter.heroku.com/articles/heroku-cli
- git : https://git-scm.com/downloads
Nodejs (Server-Side)
Create a folder named real_chat_node and open it in the terminal. Then run the following command:
npm init
Accept the defaults by pressing Enter. Next install the required packages through npm, which is available by default when you install node.
npm install express nodemon http socket.io
Open the folder with your favourite IDE. Then go to package.json and in scripts add a dev key.
Next create a index.js file in your root directory. Write the following code in it.
const app = require('express')()
const http = require('http').createServer(app)
app.get('/', (req, res) => {
res.send("Node Server is running. Yay!!")
})
http.listen(8080)
On your terminal type the following command:
npm run dev
Leave it running and go to http://localhost:8080 and you will get the message.
Now lets add socket in our nodejs app.
const app = require('express')()
const http = require('http').createServer(app)
app.get('/', (req, res) => {
res.send("Node Server is running. Yay!!")
})
//Socket Logic
const socketio = require('socket.io')(http)
socketio.on("connection", (userSocket) => {
userSocket.on("send_message", (data) => {
userSocket.broadcast.emit("receive_message", data)
})
})
http.listen(process.env.PORT)
The connection event is triggered whenever a socket is connected to our app. We then add a listener to the send_message event which forwards any data that is sent to it to receive_message event.
Voila!! Our backend is ready. Letβs deploy it to heroku and then we can start our Flutter app.
Quick Note: The following are the ways to send and listen on events.
Heroku (Deployment)
Heroku is a cloud platform which will deploy our app so that we can access it from anywhere though a url. Letβs get started.
Before we deploy our app, we just need to make few changes.
In index.js file replace port 8080 with process.env.PORT.
http.listen(process.env.PORT)
Create a file named Procfile in the root directory and write the following in it.
web: node index.js
Also create a .gitignore file and add the folowing
/node_modules
Next open the root directory in your terminal and setup heroku cli
heroku login
You will be asked to login in. Enter your credentials and you are good to go.Now create your heroku app.
heroku create <your-app-name-here>
Now initialize git and commit everything to heroku master.
git init
git add .
git commit -m "Initial Commit"
git push heroku master
Wait for it to finish and done. You can go to the url generated to see the same message as earlier.
Note: Use your own url that will be generated.
Flutter (Client-Side)
So our backend part is complete and now its time to start making our chat app in Flutter.
Open your terminal and type in the following command to create our flutter app.
flutter create --androidx real_chat_flutter
After the project is created open the folder in your IDE.
In your pubspec.yaml file add the following dependency
dependencies:
flutter:
sdk: flutter
cupertino_icons: ^0.1.2
flutter_socket_io: ^0.6.0 //Add this dependency
Open the main.dart in the lib folder and delete all the code and add the following code:
import 'package:flutter/material.dart';
import './ChatPage.dart';
void main() => runApp(MyMaterial());
class MyMaterial extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: ChatPage(),
);
}
}
Now we have to create the ChatPage. Create ChatPage.dart file inside lib folder. Lets write the code for our Chat Page.
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter_socket_io/flutter_socket_io.dart';
import 'package:flutter_socket_io/socket_io_manager.dart';
class ChatPage extends StatefulWidget {
@override
_ChatPageState createState() => _ChatPageState();
}
class _ChatPageState extends State<ChatPage> {
SocketIO socketIO;
List<String> messages;
double height, width;
TextEditingController textController;
ScrollController scrollController;
@override
void initState() {
//Initializing the message list
messages = List<String>();
//Initializing the TextEditingController and ScrollController
textController = TextEditingController();
scrollController = ScrollController();
//Creating the socket
socketIO = SocketIOManager().createSocketIO(
'<ENTER THE URL OF YOUR DEPLOYED APP>',
'/',
);
//Call init before doing anything with socket
socketIO.init();
//Subscribe to an event to listen to
socketIO.subscribe('receive_message', (jsonData) {
//Convert the JSON data received into a Map
Map<String, dynamic> data = json.decode(jsonData);
this.setState(() => messages.add(data['message']));
scrollController.animateTo(
scrollController.position.maxScrollExtent,
duration: Duration(milliseconds: 600),
curve: Curves.ease,
);
});
//Connect to the socket
socketIO.connect();
super.initState();
}
Widget buildSingleMessage(int index) {
return Container(
alignment: Alignment.centerLeft,
child: Container(
padding: const EdgeInsets.all(20.0),
margin: const EdgeInsets.only(bottom: 20.0, left: 20.0),
decoration: BoxDecoration(
color: Colors.deepPurple,
borderRadius: BorderRadius.circular(20.0),
),
child: Text(
messages[index],
style: TextStyle(color: Colors.white, fontSize: 15.0),
),
),
);
}
Widget buildMessageList() {
return Container(
height: height * 0.8,
width: width,
child: ListView.builder(
controller: scrollController,
itemCount: messages.length,
itemBuilder: (BuildContext context, int index) {
return buildSingleMessage(index);
},
),
);
}
Widget buildChatInput() {
return Container(
width: width * 0.7,
padding: const EdgeInsets.all(2.0),
margin: const EdgeInsets.only(left: 40.0),
child: TextField(
decoration: InputDecoration.collapsed(
hintText: 'Send a message...',
),
controller: textController,
),
);
}
Widget buildSendButton() {
return FloatingActionButton(
backgroundColor: Colors.deepPurple,
onPressed: () {
//Check if the textfield has text or not
if (textController.text.isNotEmpty) {
//Send the message as JSON data to send_message event
socketIO.sendMessage(
'send_message', json.encode({'message': textController.text}));
//Add the message to the list
this.setState(() => messages.add(textController.text));
textController.text = '';
//Scrolldown the list to show the latest message
scrollController.animateTo(
scrollController.position.maxScrollExtent,
duration: Duration(milliseconds: 600),
curve: Curves.ease,
);
}
},
child: Icon(
Icons.send,
size: 30,
),
);
}
Widget buildInputArea() {
return Container(
height: height * 0.1,
width: width,
child: Row(
children: <Widget>[
buildChatInput(),
buildSendButton(),
],
),
);
}
@override
Widget build(BuildContext context) {
height = MediaQuery.of(context).size.height;
width = MediaQuery.of(context).size.width;
return Scaffold(
body: SingleChildScrollView(
child: Column(
children: <Widget>[
SizedBox(height: height * 0.1),
buildMessageList(),
buildInputArea(),
],
),
),
);
}
}
Now run the app in two devices and chat back and forth π.
If you face any problem, you can check out my github repo :
Donβt forget to star β the repo and give claps π if you liked the article. If you have any queries you can ask in comments. Thank you π
Top comments (6)
It didnt work with my code ..It gave this error
[{"cause":{"cause":{"detailMessage":"Control frames must be final.","stackTrace":[],"suppressedExceptions":[]},"detailMessage":"websocket error","stackTrace":[],"suppressedExceptions":[]},"detailMessage":"Connection error","stackTrace":[],"suppressedExceptions":[]}]
Try to downgrade you're socket.io package using the below command
I have the same error too.
Have you gotten a solution a solution for this yet
Can you check heroku log if the request is coming to server or not?
WIth this code,It didnt work.I changed to socket_io client .I was also getting control frames issues.
I wanna a request from postman to send to flutter, can you provide some code for sending from Postman? I tried to do that but can't succeed because I'm new at NodeJS