Using WebSockets in Dart and Flutter (convenient way)

Dmitriy Matyunin
3 min readFeb 12, 2023

--

Work with WebSockets in Dart and Flutter.

Part one, base usage in Dart.

In some applications, there is a need for non-stop communication between server and clients: let’s say you have a transportation application, and you need to notify a driver that he has a new order or notify a passenger that a car has arrived. One way to do it is periodically ping the server with HTTP requests: but this approach creates a lot of useless HTTP requests that not only load the server, but also make debugging and user experience not so smooth as it could be.

And using webSockets is not that hard:

1. Connect to a WebSocket server.

2. Listen for messages from the server.

3. Send data to the server.

4. Close the WebSocket connection when it’s no longer needed.

In this article, we will be using “websocket_universal” package for Dart and Flutter, because it has:

1. Easy-to-use interface.

2. Supports both web and IO platforms (valuable for flutter applications).

3. Exposed socket status stream (if you need to show “reconnecting…” message, for example).

4. Built-in ping measurement.

5. Auto-reconnection feature for “IWebSocketHandler” handler.

You can review in pub.dev: https://pub.dev/packages/websocket_universal . Complete code example: https://github.com/dvmatyun/dart_web_socket_example/blob/master/bin/main.dart

Let’s jump to a simple example in Dart:

1. Import package in pubspec.yaml (0.3.3 version of package is used):

dependencies:
websocket_universal: ^0.3.3

And use import in your code:

import 'package:websocket_universal/websocket_universal.dart';

void main() async {
///
/// code will be here
///
}

2. Prepare connection options (you may skip them, they are optional) and webSocket server url, that we will be connecting to:

  const websocketConnectionUri = 'wss://ws.postman-echo.com/raw';
const textMessageToServer = 'Hello server!';
const connectionOptions = SocketConnectionOptions(
pingIntervalMs: 3000, // send Ping message every 3000 ms
timeoutConnectionMs: 4000, // connection fail timeout after 4000 ms
skipPingMessages: false, /// see ping/pong messages in [logEventStream]
);

In this example, we will be using “Postman echo ws server” that responses with same text messages that it received.

3. Create WebSocket message processor that is responsible for customized message handling. In this example, we will be using a simple processor that just passes String messages in and out:

  /// Example with simple text messages exchanges with server
/// (not recommended for applications)
/// [<String, String>] generic types mean that we receive [String] messages
/// after deserialization and send [String] messages to server.
final IMessageProcessor<String, String> textSocketProcessor =
SocketSimpleTextProcessor();

4. Now we can finally create our webSocket handler client and, before connecting, start listening to its status changes and incoming messages:

 /// Creating webSocket handler object:
final textSocketHandler = IWebSocketHandler<String, String>.createClient(
websocketConnectionUri, // Postman echo ws server
textSocketProcessor,
connectionOptions: connectionOptions,
);
// Listening to webSocket status changes
final socketSub = textSocketHandler.socketStateStream.listen((stateEvent) {
// ignore: avoid_print
print('> status changed to ${stateEvent.status}');
});
// Listening to server responses:
final incomingSub = textSocketHandler.incomingMessagesStream.listen((inMsg) {
// ignore: avoid_print
print('> webSocket got text message from server: "$inMsg"');
});

5. Now we can connect to a server and then send our text message:

  // Connecting to server:
final isTextSocketConnected = await textSocketHandler.connect();
if (!isTextSocketConnected) {
// ignore: avoid_print
print('Connection to [$websocketConnectionUri] failed for some reason!');
return;
}
// Send message to server:
textSocketHandler.sendMessage(textMessageToServer);

6. And, after a short delay (or when we don’t need to use active connection with the server) we can disconnect from server:

  await Future<void>.delayed(const Duration(seconds: 2));
// Disconnecting from server:
await textSocketHandler.disconnect('manual disconnect');

7. When we are done working with this webSocket object, we should dispose it (you must not use this webSocket object after disposal — create new one if you need). And remember to cancel all stream subscriptions in dart/flutter:

  // Cancelling subscriptions after a small delay:
await Future<void>.delayed(const Duration(milliseconds: 100));
socketSub.cancel();
incomingSub.cancel();
// Disposing webSocket:
textSocketHandler.close();

When we run this program, we will see the following messages in console:

> status changed to SocketStatus.connecting
> status changed to SocketStatus.connected
> webSocket got text message from server: "Hello server!"
> status changed to SocketStatus.disconnected
Exited

In this article you learned how to create webSocket client in dart and flutter, connect to server and disconnect from it, send messages and get responses from server and listen to webSocket inner status changes. In the next article we will discuss one of the appropriate ways to route messages that are received from server (as well as route messages, for server, that are received from client). A complete code example link can be found in the beginning of this article.

Written by Dmitriy Matyunin

LinkedIn: https://www.linkedin.com/in/dvmatyun/

--

--