Developing A Chat App With Express, Socket.IO, and Flutter Web

Socket.IO is a widely used JavaScript library that enables bi-directional communication between a client and a server. In this article, we will use it to build a chat app with Express and Flutter.

Setting Up The Project

Make sure that Flutter and Node.js are installed. First we will create our Express project. We can use Express application generator (npx expresss-generator) to create our project. You can delete the generated routes since we will only we serving our Flutter project. I prefer to write TypeScript instead of JavaScript, but it is optional. Then, we will create a folder named client and put our newly created Flutter project inside client. Our project structure should look like similar to the image in the left. Make sure to be on Flutter’s Beta Channel to develop web apps. You can use FVM to use both the stable channel and beta channel and not have to download it every time you switch a channel. Then we need to add Socket.io as a dependency for both Express and Flutter. For Express, we will use npm and write npm i socket.io in the terminal in our Express project directory. For Flutter, we will use the socket_io_client plugin.

Socket.io Server

We need to create an Express server and connect it to Socket.IO. This can be easily done with these 3 lines:

import express from "express";
import socketio from "socket.io";
import http from "http";
const app = express();
const server = http.createServer(app);
const io = socketio(server);

Now we an Express server with Socket.IO connected. We need to send our Flutter Web project as a response when a request is made to our server. For that we will send the built web project as a response. I created a directory named public in the Flutter project and copied the built web project into that directory. The name of the directory is not important as long as that directory is sent as a response to the request made to Express. This can also be easily done with:

//Serve the static files created in the Flutter build
app.use(express.static(path.join(__dirname, "client", "public")));
//When a get request is made to the server, handle this request
app.get("/", (req, res) => {
//Send the index.html file in our Flutter project's build
res.sendFile(path.join(__dirname, "client",
"public", "index.html"));
});

Our server can send our Flutter Web project as a response and is ready to make Socket.IO connections. We will create multiple chat rooms and every user can either join a room or create a new room. Users will be able to send messages to everyone in that room. This can be easily accomplished with:

//Handle when a socket connection is made
io.on("connection", (socket) => {
//Handle when a socket makes a request to join a room
//Request a roomId and a username for who joined
socket.on("joinRoom", (roomId: string, username: string) => {
//Join the user to the roomId
socket.join(roomId);
//Send a message to everyone in that roomId that
//username has joined the room
io.to(roomId).emit("sendMessage", username +
" joined room " + roomId);
});
//Handle when a socket makes a request to send a message
//Request a message, a roomId, and a username
socket.on(
"sendMessage",
(message: string, roomId: string, username: string) => {
io.to(roomId).emit("sendMessage", message, username);
}
);
});

This is a simple way to join and create rooms and send messages in rooms. This is also not secure, since anyone with a socket connection can change roomId of the message they send. One simple solution would be to request passwords when sending a message from the socket to the room and checking that password. If the password is correct, emit that message. Since this is a simple application and every room is public with no passwords, security is not that important for this case.

Socket.IO Client

In our Flutter project’s Homepage, we will let the user enter a roomId and a username. Then we will join that user to the roomId. We will also listen for sendMessage events in case a user sends a message.

void initSocket() {
try {
//Connect the client to the socket
socket = IO.io('https://ibkchatapp.herokuapp.com/#/',
<String, dynamic>{
'transports': ['websocket'],
});
//Join the user with username to the room with roomId
socket.emit("joinRoom", [widget.roomId, widget.username]);
//Listen to the sendMessage events emitted from the socket
socket.on("sendMessage", (res) {
//If a message comes, add it to a List<Message> to
//display as a ListView in the UI
Message msg = Message(message: res[0], username: res[1]);
setState(() {
messages.add(msg);
});
});
} catch (e) {
print(e);
}
}

For sending a message we will emit a sendMessage event.

//Emit a new message. The message is stored in a
//TextEditingController
socket.emit("sendMessage",
[messageController.text, widget.roomId, widget.username]);

After styling our UI, the application is ready to be deployed. We will run flutter build web, and copy the output to our public directory.

Result

The Homepage
The Chat Screen

The application is deploy to Heroku and is available at ibkchatapp.herokuapp.com. The users can join or create a room and send a message that will be emitted to everyone in that room. The code of the application is available at github.com/iberatkaya/chat_app. One major benefit of Flutter is that this project can also be ran as an iOS or Android application. Fell free to clone the repo and try it out!

Make sure to checkout flutter.dev to learn more about Flutter.

Useful links:

I study computer engineering at Istanbul Technical University and I am in my senior year. I’m interested in Node.js, Flutter, React Native, and Rust.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store