In Flutter, real-time communication between the app and a server can be achieved using WebSockets. The web_socket_channel package provides an easy-to-use interface for establishing and managing WebSocket connections. This guide walks you through how to use the web_socket_channel package in Flutter to build a simple WebSocket-based application.
What is a WebSocket?
A WebSocket is a communication protocol that provides full-duplex communication channels over a single TCP connection. It is commonly used for real-time applications like chat, live feeds, and gaming, where immediate data transfer is crucial.
Why Use web_socket_channel in Flutter?
The web_socket_channel package offers a straightforward API for handling WebSocket connections, allowing you to:
- Establish persistent connections with a WebSocket server.
- Send and receive data asynchronously.
- Handle connection errors and disconnections gracefully.
Setting Up Your Flutter Project
Before diving into the code, ensure you have a Flutter project set up and have added the web_socket_channel package to your dependencies.
Step 1: Create a New Flutter Project
If you don’t have one already, create a new Flutter project by running:
flutter create websocket_example
cd websocket_example
Step 2: Add the web_socket_channel Dependency
Open your pubspec.yaml file and add web_socket_channel to your dependencies:
dependencies:
flutter:
sdk: flutter
web_socket_channel: ^2.3.0
Run flutter pub get to install the new dependency.
Implementing WebSocket Communication
Now, let’s implement WebSocket communication using the web_socket_channel package.
Step 1: Import Necessary Packages
In your Flutter widget, import the necessary packages:
import 'package:flutter/material.dart';
import 'package:web_socket_channel/web_socket_channel.dart';
import 'package:web_socket_channel/io.dart';
Step 2: Establish a WebSocket Connection
Create a WebSocketChannel instance, connecting to a WebSocket server URL. Here, we’ll use a public WebSocket echo server for testing purposes:
class WebSocketPage extends StatefulWidget {
final String title;
WebSocketPage({Key? key, required this.title}) : super(key: key);
@override
_WebSocketPageState createState() => _WebSocketPageState();
}
class _WebSocketPageState extends State {
final String webSocketUrl = 'wss://echo.websocket.events';
late WebSocketChannel channel;
final TextEditingController _controller = TextEditingController();
@override
void initState() {
super.initState();
channel = IOWebSocketChannel.connect(Uri.parse(webSocketUrl));
}
@override
void dispose() {
channel.sink.close();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Form(
child: TextFormField(
controller: _controller,
decoration: InputDecoration(labelText: 'Send a message'),
),
),
const SizedBox(height: 24),
StreamBuilder(
stream: channel.stream,
builder: (context, snapshot) {
return Text(snapshot.hasData ? '${snapshot.data}' : '');
},
)
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _sendMessage,
tooltip: 'Send message',
child: const Icon(Icons.send),
),
);
}
void _sendMessage() {
if (_controller.text.isNotEmpty) {
channel.sink.add(_controller.text);
}
}
}
Step 3: Sending Messages to the WebSocket Server
To send data to the WebSocket server, use the channel.sink.add() method:
void _sendMessage() {
if (_controller.text.isNotEmpty) {
channel.sink.add(_controller.text);
_controller.clear();
}
}
This function takes the text from the input field, sends it to the WebSocket server, and clears the input field.
Step 4: Listening for Incoming Messages
To listen for messages from the WebSocket server, use a StreamBuilder to listen to the channel.stream:
StreamBuilder(
stream: channel.stream,
builder: (context, snapshot) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 24.0),
child: Text(snapshot.hasData ? '${snapshot.data}' : ''),
);
},
)
The StreamBuilder widget listens to the stream and rebuilds whenever new data is available.
Step 5: Closing the WebSocket Connection
Ensure you close the WebSocket connection when it’s no longer needed, such as when the widget is disposed. This can prevent memory leaks and other issues:
@override
void dispose() {
channel.sink.close();
super.dispose();
}
Complete Example
Here’s the complete Flutter code for the WebSocket example:
import 'package:flutter/material.dart';
import 'package:web_socket_channel/web_socket_channel.dart';
import 'package:web_socket_channel/io.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter WebSocket Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: WebSocketPage(title: 'WebSocket Demo'),
);
}
}
class WebSocketPage extends StatefulWidget {
final String title;
WebSocketPage({Key? key, required this.title}) : super(key: key);
@override
_WebSocketPageState createState() => _WebSocketPageState();
}
class _WebSocketPageState extends State {
final String webSocketUrl = 'wss://echo.websocket.events';
late WebSocketChannel channel;
final TextEditingController _controller = TextEditingController();
@override
void initState() {
super.initState();
channel = IOWebSocketChannel.connect(Uri.parse(webSocketUrl));
}
@override
void dispose() {
channel.sink.close();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Form(
child: TextFormField(
controller: _controller,
decoration: InputDecoration(labelText: 'Send a message'),
),
),
const SizedBox(height: 24),
StreamBuilder(
stream: channel.stream,
builder: (context, snapshot) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 24.0),
child: Text(snapshot.hasData ? '${snapshot.data}' : ''),
);
},
)
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _sendMessage,
tooltip: 'Send message',
child: const Icon(Icons.send),
),
);
}
void _sendMessage() {
if (_controller.text.isNotEmpty) {
channel.sink.add(_controller.text);
_controller.clear();
}
}
}
Conclusion
Using the web_socket_channel package simplifies establishing and managing WebSocket connections in Flutter. This allows you to create real-time applications that can send and receive data efficiently. By following the steps outlined in this guide, you can easily integrate WebSocket communication into your Flutter apps, enabling features like live updates, chat functionality, and more. Ensure that you handle connections, messages, and disconnections properly to build robust and reliable real-time applications.