Two-Way, Real-Time Communication with WebSockets in Flutter Apps (+ Node.js Backend)

Two-Way, Real-Time Communication with WebSockets in Flutter Apps (+ Node.js Backend)

TB

Teqani Blogs

Writer at Teqani

May 21, 20254 min read

Real-time data exchange is at the heart of many modern apps—chats, multiplayer games, trading dashboards, and collaborative tools. In this tutorial, you’ll learn how to build a two-way real-time communication system using Flutter on the frontend and a Node.js WebSocket server on the backend. This is crucial for creating engaging and interactive user experiences.

Setting up the Node.js WebSocket Server

Let's start by setting up a basic WebSocket server using the popular ws library. First, initialize your project and install the necessary dependencies:

  • Setup

Install dependencies:

npm init -y

npm install ws

Next, create your server.js file:

const WebSocket = require('ws');

const wss = new WebSocket.Server({ port: 8080 });

wss.on('connection', function connection(ws) {

console.log('Client connected');

ws.on('message', function incoming(message) {

console.log('received: %s', message);

// Echo the message back to all clients

wss.clients.forEach(function each(client) {

if (client.readyState === WebSocket.OPEN) {

client.send(`Echo: ${message}`);

}

});

});

ws.send('Welcome to the WebSocket server!');

});

console.log("WebSocket server running on ws://localhost:8080");

Run the server:

node server.js

Building the Flutter Client

Now, let's build the Flutter client. Add web_socket_channel as a dependency to your pubspec.yaml:

dependencies:

flutter:

sdk: flutter

web_socket_channel: ^2.3.0

Here’s the main.dart code for the Flutter client:

import 'package:flutter/material.dart';

import 'package:web_socket_channel/web_socket_channel.dart';

void main() {

runApp(MyApp());

}

class MyApp extends StatelessWidget {

final WebSocketChannel channel =

WebSocketChannel.connect(Uri.parse('ws://localhost:8080'));

@override

Widget build(BuildContext context) {

return MaterialApp(

home: ChatScreen(channel: channel),

);

}

}

class ChatScreen extends StatefulWidget {

final WebSocketChannel channel;

ChatScreen({required this.channel});

@override

_ChatScreenState createState() => _ChatScreenState();

}

class _ChatScreenState extends State {

final TextEditingController _controller = TextEditingController();

final List _messages = [];

@override

void initState() {

super.initState();

widget.channel.stream.listen((message) {

setState(() {

_messages.add(message);

});

});

}

void _sendMessage() {

if (_controller.text.isNotEmpty) {

widget.channel.sink.add(_controller.text);

_controller.clear();

}

}

@override

void dispose() {

widget.channel.sink.close();

_controller.dispose();

super.dispose();

}

@override

Widget build(BuildContext context) {

return Scaffold(

appBar: AppBar(title: Text("Flutter WebSocket Chat")),

body: Column(

children: [

Expanded(

child: ListView.builder(

itemCount: _messages.length,

itemBuilder: (_, index) => ListTile(

title: Text(_messages[index]),

),

),

),

Padding(

padding: const EdgeInsets.all(12),

child: Row(

children: [

Expanded(child: TextField(controller: _controller)),

IconButton(

icon: Icon(Icons.send),

onPressed: _sendMessage,

)

],

),

)

],

),

);

}

}

In this tutorial, we built a working prototype of a bi-directional real-time chat system using a Node.js WebSocket server and a Flutter client, making use of the web_socket_channel package.

TB

Teqani Blogs

Verified
Writer at Teqani

Senior Software Engineer with 10 years of experience

May 21, 2025
Teqani Certified

All blogs are certified by our company and reviewed by our specialists
Issue Number: #e4a97227-2563-483b-ae43-9d1c99876846