flutterdartflutter-streambuilder

Flutter StreamSubscription works with listen, but not in a StreamBuilder widget


Sure! Here's the fixed message to ask for help on Stack Overflow:


I am working with flutter_blue_plus and flutter_foreground_task packages.

I have a Bluetooth handler class called BluetoothTaskHandler, which extends TaskHandler. Here's the code:

class BluetoothTaskHandler extends TaskHandler {
  SendPort? _sendPort;

  @override
  Future<void> onStart(DateTime timestamp, SendPort? sendPort) async {
    _sendPort = sendPort;
    // Here, I perform the setup for the Bluetooth device, services, characteristics, and listeners.
  }

  // This is the listener specific to a characteristic.
  void onBreathEcgValueReceived(List<int> event) {
    // Send data to the main isolate.
    EcgBrtValueDto? result = bluetoothBreathEcgInterpreter.getEcgBrt(Uint8List.fromList(event));
    if (result != null) {
      var brt = result.brt;
      var ecg = result.ecg;

      logger.info("@onBreathEcgValueReceived ecg ${ecg.toString()}");
      logger.info("@onBreathEcgValueReceived brt ${brt.toString()}");

      if (_sendPort != null) {
        FlutterForegroundTask.isAppOnForeground.then((isAppOnForeground) {
          if (isAppOnForeground) {
            _sendPort?.send(json.encode(result.toJson()));
          }
        });
      }
    }
  }
}

In my widget, I have the following code (only the State part is relevant). There is an important comment:

@override
void initState() {
  super.initState();
  WidgetsBinding.instance.addPostFrameCallback((_) async {
    if (await FlutterForegroundTask.isRunningService) {
      setUpPortStream();
    }
  });

  WidgetsBinding.instance.addObserver(this);
}

setUpPortStream() {
  // Type: ReceivePort?
  final newReceivePort = FlutterForegroundTask.receivePort;
  if (newReceivePort == null) {
    logger.warn("@setUpPortStream no receive port");
    return;
  }

  /*** IMPORTANT: if I uncomment this, I see the data coming ***/
  /*newReceivePort.listen((message) {
    logger.info("Received $message");
  });*/

  return;

  // [..] Build stuff [..]

  StreamBuilder<dynamic>(
    // Already tried: stream: newReceivePort.asBroadcastStream(),
    stream: newReceivePort.asBroadcastStream(),
    builder: (context, snapshot) {
      List<Widget> children;
      if (!snapshot.hasData) {
        logger.debug("@showBatteryStatus snapshot has no data in it.");
        children = <Widget>[const Text('no data.')];
      }
      // [...]
    }
  );
}

The issue I'm facing is that it always displays "No data" in the StreamBuilder. I also tried converting the subscription to a stream using the convertSubscriptionToStream method, but without success.

Additionally, after initializing the foreground task, this is the top-level callback:

// The callback function should always be a top-level function.
@pragma('vm:entry-point')
void startCallback() {
  // The setTaskHandler function must be called to handle the task in the background.
  FlutterForegroundTask.setTaskHandler(BluetoothTaskHandler());
}

I would appreciate any help or suggestions on why I'm not receiving data in the StreamBuilder. Thank you!


Solution

  • Stop creating the stream as the stream: parameter of the StreamBuilder. Watch this for why and how to fix: https://youtu.be/sqE-J8YJnpg