Порхание и сообщение о получении: плохое состояние: поток уже прослушан

#flutter #dart

#трепетание #дротик

Вопрос:

У меня есть класс с этим кодом в нем:

 class GeofenceManager {
  ReceivePort geofencingMessagePort = ReceivePort();

  Stream<String> get events {
    return geofencingMessagePort.map((event) => event.toString());
  }
  ...
 

И в моем пользовательском интерфейсе у меня есть StreamBuilder такое:

   @override
  Widget build(BuildContext context) {
    super.build(context);
    var geofenceManager = Provider.of<GeofenceManager>(context);
    return StreamBuilder(
      initialData: "Waiting ...",
      stream: geofenceManager.events,
      ...
 

Но это не работает. Когда я запускаю приложение, я получаю эту ошибку:

 Bad state: Stream has already been listened to.
 

Первая сборка пользовательского интерфейса работает, но если запускается вторая сборка, я получаю сообщение об ошибке. Я пробовал использовать asBroadcastStream() , но это не решило проблему.

Так что я немного застрял, есть идеи, как это исправить?

Ответ №1:

Хорошо, заставил его работать, добавив a StreamController и прослушивая порт, а не раскрывая его. Итак, мой код теперь выглядит так:

 class GeofenceManager {
  ReceivePort geofencingMessagePort = ReceivePort();
  StreamController<String> _eventController = StreamController<String>.broadcast();
  
  Stream<String> get events {
    return _eventController.stream;
  }

  GeofenceManager._() {
    IsolateNameServer.registerPortWithName(_geofencingMessagePort.sendPort, 'geofencing_send_port');
    _geofencingMessagePort.listen((message) {
      _eventController.add(message);
    });
  }

  ...
 

Это работает, но я не уверен, что это лучшее решение. Есть мысли?

Ответ №2:

Stream.asBroadcastStream(..) должно сработать. Убедитесь, что не вызываете geofencingMessagePort .asBroadcastStream(..) несколько раз, потому что это приводит к повторной подписке на одну подписку ReceivePort под капотом. Смотрите документацию

Возвращенный поток будет подписан на этот поток при добавлении его первого подписчика

К сожалению, вы не можете использовать геттер, как в вашем примере

 Stream<String> get events {
  return geofencingMessagePort.asBroadcastStream();
}
 

потому что каждый доступ к events результатам вызывает asBroadcastStream() , который, в свою очередь, пытается повторно подписаться только на одну подписку geofencingMessagePort .

Вместо этого вы можете назначить широковещательный поток в отдельное поле:

 ReceivePort _geofencingMessagePort = ReceivePort();
Stream<String> _eventController = _geofencingMessagePort.asBroadcastStream().map((event) => event.toString());
Stream<String> get eventController => _eventController;