#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;