Ответ 1
Проблема заключалась в том, что они не выбрали контроллеры в блоке.
void dispose() {
monthChangedController.close();
dayPressedController.close();
resultController.close();
}
class MyPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return DefaultTabController(
length: 2,
child: new Scaffold(
appBar: TabBar(
tabs: [
Tab(child: Text("MY INFORMATION",style: TextStyle(color: Colors.black54),)),
Tab(child: Text("WEB CALENDER",style: TextStyle(color: Colors.black54),)),
],
),
body:PersonalInformationBlocProvider(
movieBloc: PersonalInformationBloc(),
child: TabBarView(
children: [
MyInformation(),
new SmallCalendarExample(),
],
),
),
),
);
}
}
class MyInformation extends StatelessWidget{
// TODO: implement build
var deviceSize;
//Column1
Widget profileColumn(PersonalInformation snapshot) => Container(
height: deviceSize.height * 0.24,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
Container(
decoration: BoxDecoration(
borderRadius:
new BorderRadius.all(new Radius.circular(50.0)),
border: new Border.all(
color: Colors.black,
width: 4.0,
),
),
child: CircleAvatar(
backgroundImage: NetworkImage(
"http://www.binaythapa.com.np/img/me.jpg"),
foregroundColor: Colors.white,
backgroundColor: Colors.white,
radius: 40.0,
),
),
ProfileTile(
title: snapshot.firstName,
subtitle: "Developer",
),
SizedBox(
height: 10.0,
),
],
)
],
),
);
Widget bodyData(PersonalInformation snapshot) {
return SingleChildScrollView(
child: Column(
children: <Widget>[
profileColumn(snapshot)
],
),
);
}
@override
Widget build(BuildContext context) {
final personalInformationBloc = PersonalInformationBlocProvider.of(context);
deviceSize = MediaQuery.of(context).size;
return StreamBuilder(
stream: personalInformationBloc.results,
builder: (context,snapshot){
if (!snapshot.hasData)
return Center(
child: CircularProgressIndicator(),
);
return bodyData(snapshot.data);
}
);
}
}
Я использую Bloc Pattern для извлечения данных из Rest API (только что вызвал весь объект из json и только проанализировал имя пользователя). Страница состоит из двух вкладок MyIformation и SmallCalendar. Когда приложение запускается, данные выбираются правильно, и все хорошо. Когда я перехожу на вкладку 2 и возвращаюсь к вкладке 1, все экраны на вкладке 1 переходят в красный цвет, показывая ошибку: Bad state: Stream has already been listened to.
Проблема заключалась в том, что они не выбрали контроллеры в блоке.
void dispose() {
monthChangedController.close();
dayPressedController.close();
resultController.close();
}
Вы должны использовать следующее.
StreamController<...> _controller = StreamController<...>.broadcast();
Наиболее распространенную форму Stream
можно слушать только один раз за раз. Если вы попытаетесь добавить нескольких слушателей, это будет
Плохое состояние: поток уже прослушали
Чтобы предотвратить эту ошибку, выведите широковещательный Stream
. Вы можете конвертировать свой поток в эфир, используя myStream.asBroadcastStream
Это необходимо сделать внутри вашего класса, которые выставляют Stream
. Не как параметр StreamBuilder
. Поскольку asBroadcastStream
внутренне прослушивает исходный поток для генерации широковещательной передачи, это означает, что вы не можете дважды вызвать этот метод в одном потоке.
В моем случае я получал эту ошибку, потому что одна и та же строка кода myStream.listen()
вызывалась дважды в одном и том же виджете в одном и том же потоке. Видимо, это запрещено!
Для меня исправлено то, что я создал контроллер потока в качестве контроллера потока вещания:
var myStreamController = StreamController<bool>.broadcast();
И
использовать поток в качестве вещательного потока:
myStreamController.stream.asBroadcastStream().listen(onData);
У меня была такая же проблема, когда я использовал результат Observable.combineLatest2 для StreamBuilder в Drawer:
flutter: плохое состояние: поток уже прослушан.
Как по мне, лучшее решение - добавить результат этого объединения в новый BehaviorSubject и прослушать новый.
Не забудь послушать старую !!!
class VisitsBlocc extends Object {
Map<Visit, Location> visitAndLocation;
VisitsBloc() {
visitAndLocations.listen((data) {
visitAndLocation = data;
});
}
final _newOne = new BehaviorSubject<Map<Visit, Location>>();
Stream<Map<Visit, Location>> get visitAndLocations => Observable.combineLatest2(_visits.stream, _locations.stream, (List<vis.Visit> visits, Map<int, Location> locations) {
Map<vis.Visit, Location> result = {};
visits.forEach((visit) {
if (locations.containsKey(visit.skuLocationId)) {
result[visit] = locations[visit.skuLocationId];
}
});
if (result.isNotEmpty) {
_newOne.add(result);
}
});
}
Я не использовал .broadcast
потому что это замедлило мой интерфейс.
Просто подвести итог:
Основное отличие состоит в том, что broadcast()
создает a Stream
для прослушивания для нескольких источников, но его необходимо прослушивать, по крайней мере, для одного источника, чтобы начать излучать элементы.
Поток должен быть инертным до тех пор, пока подписчик не начнет слушать его (используя обратный вызов [onListen], чтобы начать генерировать события).
asBroadcastStream
превращает существующий Stream
в мульти-прослушиваемый, но его не нужно слушать, чтобы начать излучать, так как он вызывает onListen()
под капотом.
Вы можете использовать broadcast
, который позволяет прослушивать поток более одного раза, но также предотвращает прослушивание прошлых событий:
Широковещательные потоки не буферизуют события, когда нет прослушивателя.
Лучшим вариантом является использование BehaviorSubject
из класса пакета rxdart
в качестве StreamController
. BehaviorSubject
это:
Специальный StreamController, который захватывает последний элемент, который был добавлен в контроллер, и отправляет его в качестве первого элемента любому новому слушателю.
Использование так же просто, как:
StreamController<...> _controller = BehaviorSubject();