Home > database >  Combining bloc and websockets
Combining bloc and websockets

Time:05-31

I'm trying to use bloc with websockets in both directions (i.e. when a message is received on the websocket, an event is fired as well as when a state is emitted, a message is sent over the web socket). I'm still fairly new to flutter but have written similar style code (message queues and websockets) in other languages but and I'm really struggling to get my head around how to structure everything so it works in flutter.

I have a basic class that opens a websocket and waits for events

class WebsocketManager {
  final BuildContext context;
  late IOWebSocketChannel channel;

  WebsocketManager(this.context);

  void connect(){
    channel = IOWebSocketChannel.connect(Uri.parse(wsBaseUrl));
    channel.stream.listen(
      (msg) {
        //process msg 
        BlocProvider.of<SomeBloc>(context).add(MessageReceived(msg));
      }
    );
  }
}

This works perfectly fine (although having to pass the BuildContext in feels a bit wrong). The issue is with listening for new states. I assumed I would be able to do something like this

BlocListener<SomeBloc, SomeState>(
  listener: (context, state) {
    if(!sendMessage(SomeMessage()))
});

However this listener never fires. If I place that same code as a child of a Widget then it works fine so I assume a BlockListener has to be a child of a widget.

My question is, is there a way of using BlocListener (or some alternative) without being a child of a widget?

Alternatively, is there a better way I could structure my code so it can have access to both the websocket and bloc?

Thanks

CodePudding user response:

First, the BlockListener must be a child of the widget being provided with the bloc.

Yeah, I wouldn't pass the BuildContext into the WebsocketManager. I'd flip it around a bit, to make it a bit cleaner.

How you do it will of course depend on how your UI should behave based on events and states. But for the sake of keeping my example simple, I have a suggestion below where a Widget (perhaps an entire route), is listening and updating based on the websocket messages. You can of course do it so that entire app is affected by the websocket, but that is "the next step".

The bloc should be the glue between your WebsocketManager and the UI. So I'd suggest to let your Bloc be created (provided) to a Widget where appropriate. The bloc holds an instance of WebsocketManager (perhaps a Singleton?). Have a method and corresponding state in the bloc that sets up the connection using WebsocketManager.connect(). But instead of doing the ....add(MessageReceived(msg)) stuff in the listen callback, have a method (perhaps it is your connect() method) that returns a Stream<type of msg> and let the listen callback yield msg. You can then in your bloc set up a StreamSubscription for the WebsocketManager's stream, and then emit states and act based on what is received from the websocket.

I'd suggest that you also convert the msg from the listen-callback to your own domain object and yield that in the stream, so that your bloc isn't strictly dependent on the msg type from the WebsocketManager.

This way your WebsocketManager only do 'websocket-stuff' in the data layer, and you let your bloc do the logic in the application layer, which in terms will let your UI update based on what you bloc emits.

  • Related