WebSockets represent a long-awaited evolution in client/server web technology. It defines a fully duplex bi-directional communication channel between the client and server.
web_socket_channel package
The web_socket_channel basically works with the StreamChannel class, which is an abstract class representing a two-way communication channel
Each StreamChannel exposes a Stream for receiving data. A Stream is like a pipe, you put a value on one end and if there’s a listener on the other end that listener will receive that value.
It also exposes StreamSink used to push messages to the server.
Let's see by the example
We'd have a textfield in our app where the user could type a message. The data that we input would be displayed on the screen after pressing a button.
The data displayed comes from the server response, which is an echo server in this case, meaning it sends back what it receives. These servers are used to determine whether or not a connection to a server is successful. Here we would be using them to maintain simplicity for this example
class MyHomePageState extends State<MyHomePage> {
TextEditingController _controller = TextEditingController();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("WebSocket Example"),
),
body: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Form(
child: TextFormField(
decoration: InputDecoration(labelText: "Send message to the server"),
controller: _controller,
),
),
],
),
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.send),
onPressed: sendData,
),
);
}
}
WebSocketChannel channel = IOWebSocketChannel.connect("wss://ws.ifelse.io/");
WebSocketChannel: A StreamChannel (class representing a two-way communication) that communicates over a WebSocket.
IOWebSocketChannel : A WebSocketChannel that communicates using a dart:io WebSocket.
IOWebSocketChannel.connect: Creates a new WebSocket connection and connects to url using WebSocket.connect and returns a channel that can be used to communicate over the resulting socket.
Here wss://ws.ifelse.io/ is an echo WebSocket server. Please note that the mostly used echo server ws://echo.websocket.org is no longer in service.
Now that we’ve established a connection, let us listen to messages from the server.
We will use a StreamBuilder widget to listen for new messages, and a Text widget to display them.
A StreamBuilder:
Can listen to exposed streams.
Return widgets
Catch snapshots of got stream information.
StreamBuilder(
stream: widget.channel.stream,
builder: (context, snapshot) {
return Padding(
padding: const EdgeInsets.all(20.0),
child: Text(snapshot.hasData ? '${snapshot.data}' : ''),
);
},
)
Send data to the server :
add a function sendData() responsible for sending data to the stream whenever the floating button is pressed.
void sendData() {
if (_controller.text.isNotEmpty) {
widget.channel.sink.add(_controller.text);
}
}
channel.sink.add(): for sending values to the other endpoint of the stream (i.e the server) using the sink property of the WebSocketChannel.
close the connection
void dispose() {
widget.channel.sink.close();
super.dispose();
}
Now you can see the full code :
class MyHomePage extends StatefulWidget {
WebSocketChannel channel = IOWebSocketChannel.connect("wss://ws.ifelse.io/");
@override
MyHomePageState createState() {
return MyHomePageState();
}
}
class MyHomePageState extends State<MyHomePage> {
TextEditingController _controller = TextEditingController();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Web Socket"),
),
body: Padding(
padding: const EdgeInsets.all(20.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Form(
child: TextFormField(
decoration: InputDecoration(labelText: "Send any message to the server"),
controller: _controller,
),
),
StreamBuilder(
stream: widget.channel.stream,
builder: (context, snapshot) {
return Padding(
padding: const EdgeInsets.all(20.0),
child: Text(snapshot.hasData ? '${snapshot.data}' : ''),
);
},
)
],
),
),
floatingActionButton: FloatingActionButton(
child: Icon(Icons.send),
onPressed: sendData,
),
);
}
void sendData() {
if (_controller.text.isNotEmpty) {
widget.channel.sink.add(_controller.text);
}
}
@override
void dispose() {
widget.channel.sink.close();
super.dispose();
}
}