[WIP] Dart Websockets Introduction
By Michael
I’ve been researching using websockets in Dart lately as a way to remotely initiate actions on the connected clients without having them poll every X seconds (not great to be chatty with the serversession85!).
I referenced the The Boaring Show Full-stack Dart YouTube episodes quite heavily. These videos include Simon Lightfoot, from the Flutter Community, and Craig Labenz from the Google Flutter team.
Pt1: https://www.youtube.com/watch?v=AaQzV1LTmo0
Pt2: https://www.youtube.com/watch?v=K85PUBjFhn8
Full websocket server https://gist.github.com/mmaitlen/19d9d0c91db29a55bc8cf883da03a511
Full websocket client https://gist.github.com/mmaitlen/9c51792cff715deb58f167ff8e9791c2
The following code shows the main functionality for the server. Hopefully the code speaks for itself, very light commenting. See the videos from The Boring Show to get a much better explaination then I could do!
/// Define the websocket route using /ws
final _router = router.Router()
..get('/ws', webSocketHandler(_handleWebSocket));
// A place to store the websocket so it can be used to call client
WebSocketChannel? wsc;
Future<void> _startServer() async {
final ip = InternetAddress.anyIPv4;
final handler =
const Pipeline().addMiddleware(logRequests()).addHandler(_router);
final port = int.parse(Platform.environment['PORT'] ?? '8081');
final server = await serve(handler, ip, port);
debugPrint('Server listening on port ${server.port}');
}
void _handleWebSocket(WebSocketChannel webSocket) {
debugPrint("client connected");
webSocket.stream.listen((message) {
debugPrint("echo to client $message");
webSocket.sink.add("echo $message");
}, onDone: () {
debugPrint('client connection closed');
});
// save the websocket so it can be used to ping client later
wsc = webSocket;
}
A selection of the client code follows
class _MainAppState extends State<MainApp> {
late WebSocketChannel channel;
@override
void initState() {
super.initState();
final wsUrl = Uri.parse('ws://localhost:8081/ws');
channel = WebSocketChannel.connect(wsUrl);
channel.stream.listen((event) {
debugPrint('got stuff from server $event');
});
}
@override
void dispose() {
channel.sink.close();
super.dispose();
}
void pingServer() {
debugPrint("ping server");
channel.sink.add('bobdog');
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(
child: Column(
children: [
const Text('CLIENT!'),
ElevatedButton(
onPressed: pingServer,
child: const Text("Ping Server"),
)
],
),
),
),
);
}
}