Building a WebSocket Client in Flutter: From Zero to Hero
Teqani Blogs
Writer at Teqani
WebSockets are essential for real-time communication in applications like chat apps, live dashboards, games, or collaborative tools. Flutter, a powerful UI toolkit, simplifies creating such reactive applications. This guide offers a step-by-step approach to building a resilient WebSocket client in Flutter, covering everything from basic connectivity to advanced features.
Step 1: Set Up Your Flutter Project
Begin by setting up your Flutter project and adding the necessary dependencies. Add the following to your pubspec.yaml
file:
dependencies:
flutter:
sdk: flutter
web_socket_channel: ^2.3.0
rxdart: ^0.27.4
Step 2: Basic WebSocket Connection
Let's start with a minimal WebSocket client to connect and receive messages. Here's a simple example:
import 'package:web_socket_channel/web_socket_channel.dart';
final channel = WebSocketChannel.connect(
Uri.parse('wss://echo.websocket.events'), // Public echo server
);
channel.sink.add('Hello WebSocket!');
channel.stream.listen((message) {
print('Received: $message');
});
Step 3: Create a Robust WebSocket Client
Now, let's create a reusable service with auto-reconnect, stream handling, and safe disconnection. Below is an example websocket_client.dart
file:
import 'dart:async';
import 'package:web_socket_channel/web_socket_channel.dart';
import 'package:web_socket_channel/status.dart' as status;
import 'package:rxdart/rxdart.dart';
class WebSocketClient {
final String url;
WebSocketChannel? _channel;
final BehaviorSubject<String> _messages = BehaviorSubject<String>();
Stream<String> get messages => _messages.stream;
Timer? _reconnectTimer;
int _retrySeconds = 1;
bool _manuallyDisconnected = false;
WebSocketClient(this.url);
void connect() {
_manuallyDisconnected = false;
_channel = WebSocketChannel.connect(Uri.parse(url));
_channel!.stream.listen(
(data) {
_messages.add(data);
},
onDone: _handleDisconnect,
onError: (error) => _handleDisconnect(),
);
print('Connected to $url');
}
void _handleDisconnect() {
if (_manuallyDisconnected) return;
_channel = null;
_scheduleReconnect();
}
void _scheduleReconnect() {
_reconnectTimer?.cancel();
_reconnectTimer = Timer(Duration(seconds: _retrySeconds), () {
connect();
_retrySeconds = (_retrySeconds * 2).clamp(1, 64); // exponential backoff
});
print('Reconnecting in $_retrySeconds seconds...');
}
void send(String message) {
if (_channel != null) {
_channel!.sink.add(message);
}
}
void disconnect() {
_manuallyDisconnected = true;
_reconnectTimer?.cancel();
_channel?.sink.close(status.goingAway);
_messages.close();
}
}
Step 4: Flutter UI to Test WebSocket
Let’s build a simple UI to send and receive messages via our client. Here’s an example main.dart
file:
import 'package:flutter/material.dart';
import 'websocket_client.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
final WebSocketClient client = WebSocketClient('wss://echo.websocket.events');
@override
Widget build(BuildContext context) {
client.connect();
return MaterialApp(
home: WebSocketScreen(client: client),
);
}
}
class WebSocketScreen extends StatefulWidget {
final WebSocketClient client;
WebSocketScreen({required this.client});
@override
_WebSocketScreenState createState() => _WebSocketScreenState();
}
class _WebSocketScreenState extends State<WebSocketScreen> {
final TextEditingController _controller = TextEditingController();
@override
void dispose() {
widget.client.disconnect();
super.dispose();
}
void _sendMessage() {
final message = _controller.text;
if (message.isNotEmpty) {
widget.client.send(message);
_controller.clear();
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('WebSocket Client')),
body: Column(
children: [
Expanded(
child: StreamBuilder<String>(
stream: widget.client.messages,
builder: (context, snapshot) {
if (snapshot.hasData) {
return Center(child: Text('Message: ${snapshot.data}'));
} else {
return Center(child: Text('Waiting for messages...'));
}
},
),
),
Padding(
padding: EdgeInsets.all(8.0),
child: Row(
children: [
Expanded(child: TextField(controller: _controller)),
IconButton(
icon: Icon(Icons.send),
onPressed: _sendMessage,
)
],
),
)
],
),
);
}
}
Step 5: Run the App
Run the app and:
- Send a message
- Receive the echoed message
- Disable internet to simulate reconnection
- Re-enable internet and observe automatic reconnect
Bonus Tips for Production
- Secure your WebSocket endpoint with authentication (Firebase tokens, JWT, etc.)
- Use SSL/TLS (wss://) in production
- Add ping/pong or heartbeat messages for liveness detection
- Centralize reconnection logic for better state management
Conclusion
With this robust WebSocket client setup in Flutter, you’ve gone from zero to hero. You’ve built:
- A full connection lifecycle manager
- Automatic reconnect with exponential backoff
- Clean UI integration with real-time updates
Whether you’re building a real-time dashboard, live chat, or trading platform, this approach is scalable and production-ready.
All blogs are certified by our company and reviewed by our specialists
Issue Number: #72ab3384-24f6-4114-91d2-4adb9b99315f