Ad

StreamProvider And TCP Socket

- 1 answer

I wrote an one-page desktop app to communicate with TCP Server.

In my code, I use Socket.listen() method to receive data and it is OK.

  • I used single subscription and it was enough for me.

I tried to convert it to StreamProvider[Riverpod] and I failed.

  • I used StreamController() then I get bad state.
  • I used StreamController.broadcast() and I couldn't get data from socket

Could you suggest me correct way?
For a side note: I'm not an experienced flutter developer, just try to learn :)

I added code blocks to below and also full code.

For the full code: https://gist.github.com/sphinxlikee/3cbfa47817a5187c7b67905028674041

UI:

enter image description here

Working code;

  Future<void> createConnection() async {
    try {
      _socket = await Socket.connect(serverAddress, serverPort);
      _changeConnectionState();
    } catch (e) {
      print('connection has an error and socket is null.');
      print(e);
      return;
    }

    listenSocket();
  }

  void listenSocket() {
    _socket.listen(
      (event) {
        _getData(String.fromCharCodes(event));
        print('received: $receivedData');

        if (!_dataReceived) {
          _changeDataReceivedState();
        }
      },
    )
      ..onDone(
        () {
          _changeConnectionState();
          _streamDone();
          print('socket is closed');
        },
      )
      ..onError(
        (error, stackTrace) {
          print('$error');
        },
      );
  }

Working code - UI side

class ReceivedData extends ConsumerWidget {
  @override
  Widget build(BuildContext context, ScopedReader watch) {
    final receivedData = watch(tcpClientProvider).receivedData;
    return Text('Received data: $receivedData');
  }
}

For the StreamProvider I tried,

 Future<void> createConnection() async {
    try {
      _socket = await Socket.connect(serverAddress, serverPort);
      streamController.sink.add(_socket.listen((event) => String.fromCharCodes(event)));
      _changeConnectionState();
    } catch (e) {
      print('connection has an error and socket is null.');
      print(e);
      return;
    }

  }

StreamProvider - UI side

final streamProvider = StreamProvider.autoDispose(
  (ref) async* {
    await for (final value in ref.watch(tcpClientProvider).streamController.stream) {
      yield value;
    }
  },
);

class ReceivedDataWithProvider extends ConsumerWidget {
  @override
  Widget build(BuildContext context, ScopedReader watch) {
    AsyncValue receivedData = watch(streamProvider);

    return receivedData.when(
      data: (data) => Text('Received data: $data'),
      loading: () => const CircularProgressIndicator(),
      error: (err, stack) => Text('error'),
    );
  }
}
Ad

Answer

Socket implements Stream, so you could just write:

final streamProvider = StreamProvider.autoDispose<Uint8List>((ref) {
  return ref.watch(tcpClientProvider)._socket;
});

If you still want to add a listener, there's no harm in having one if you need:

final streamProvider = StreamProvider.autoDispose<Uint8List>((ref) {
  final client = ref.watch(tcpClientProvider);

  return client._socket
    ..listen(
      (event) {},
    ).onDone(
      () {
        client
          .._changeConnectionState()
          .._streamDone();
        print('socket is closed');
      },
    );
});
Ad
source: stackoverflow.com
Ad