WebSockets with Flutter

What is a websocket

A WebSocket is a persistent connection between a client and server. WebSockets provide a bidirectional, full-duplex communications channel that operates over HTTP through a single TCP/IP socket connection. At its core, the WebSocket protocol facilitates message passing between a client and server.

The idea of WebSockets was borne out of the limitations of HTTP-based technology. With HTTP, a client requests a resource, and the server responds with the requested data. HTTP is a strictly unidirectional protocol — any data sent from the server to the client must be first requested by the client. Long-polling has traditionally acted as a workaround for this limitation. With long-polling, a client makes an HTTP request with a long timeout period, and the server uses that long timeout to push data to the client. Long-polling works, but comes with a drawback — resources on the server are tied up throughout the length of the long-poll, even when no data is available to send.

Establishing a WebSocket connection

WebSockets do not use the http:// or https:// scheme (because they do not follow the HTTP protocol). Rather, WebSocket URIs use a new scheme ws: (or wss: for a secure WebSocket). The remainder of the URI is the same as an HTTP URI: a host, port, path and any query parameters.

"ws:" "//" host [ ":" port ] path [ "?" query ]
"wss:" "//" host [ ":" port ] path [ "?" query ]

doing it with flutter

For initial testing of our web socket applications, we use this website https://www.websocket.org/echo.html

More precisely, we use an actual web socket at this location wss://echo.websocket.org to make sure our app is working as expected.

Flutter SDK comes with a lot of third party packages. The one we will use is called web_socket_channel. It provides us with Stream channels which wrap around our web socket connections. Stream channels are basically streams with a Sink attached to them. The Sink is the part which sends the data, and Stream is the part that receives the data.

The basic idea of the following program is to have an input box and a button. We will be able to type in strings to input box and when we click the button, the string will get sent to the web socket. After it gets sent to the socket, then we will receive the incoming information from the web socket and put it inside of body of our application.

If we create an app with stateful widget, inside of State extending class I will define a WebSocketChannel and TextEditingController

class _MyHomePageState extends State<MyHomePage> {
     WebSocketChannel channel;
     TextEditingController controller;

     @override
     void initState() {
       super.initState();
       channel = IOWebSocketChannel.
connect('ws://echo.websocket.org');
       controller = TextEditingController();
     }

     @override
     Widget build(BuildContext context) {
       return Scaffold();
    }
}

Now in the same class, I will define a method for sending a data to the web socket. Latter I will set this method to be called on a click of a Send button

  void sendData() {
    if (controller.text.isNotEmpty) {
      channel.sink.add(controller.text);
      controller.text = "";
    }
  }

Now I will create a button for calling a sendData():

@override Widget build(BuildContext context) {
     return Scaffold(
     floatingActionButton: FloatingActionButton(
        child: Icon(Icons.send),
        onPressed: () {
          sendData();
        },
      ),
); 
}

 

Now I will define a list for storing the data that is being passed to socket:

class _MyHomePageState extends State<MyHomePage> {
  WebSocketChannel channel;
  TextEditingController controller;
  final List<String> list = [];
  
    @override
  void initState() {
    super.initState();
    channel = IOWebSocketChannel.
connect('ws://echo.websocket.org');
    controller = TextEditingController();
    channel.stream.listen(
(data) => setState(() => list.add(data))
);
  }
}

So, in our channel.stream we attach a listener and the callback from that listener will take data that comes out of our stream and pass it into setState() where we are taking the list and adding the data to it.

Finally, in the build() function I will create a Column with map function which iterates through our data list and creates a Text widget for every list item (following complete build function):

@override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('WebSocket Example'),
      ),
      body: Container(
        padding: EdgeInsets.all(20.0),
        child: Column(
          children: <Widget>[
            Form(
              child: TextFormField(
                controller: controller,
                decoration: InputDecoration(
                  labelText: "Send to WebSocket",
                ),
              ),
            ),
            Column(
              children: list.map((data) =>
 Text(data)).toList(),
            )
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        child: Icon(Icons.send),
        onPressed: () {
          sendData();
        },
      ),
    );
  }

The last thing we should do is to close our stream:

  @override
  void dispose() {
    channel.sink.close();
    super.dispose();
  }

 

That is all. This is how you make a web socket in Flutter.

Complete source code: https://github.com/azemZejnil?tab=repositories

Share with your friends
Tagged with: