Альтернатива ScanStreamTransformer, которая вызывается один раз для каждого события, а не один раз для прослушивателя.

Думаю, теперь я вижу, что происходит с transform() в Bloc.dart классе. ScanStreamTransformer внутри конструктора BloC.dart вызывается один раз для каждого прослушивателя (внутри класса One.dart есть 3 слушателя, поэтому он вызывается 3 раза). Я хотел бы изменить это поведение, чтобы оно вызывалось один раз для каждого события, независимо от того, сколько слушателей подключено, например. вызов _mainBloc.inValue(widget.value) вызовет преобразование только один раз, сейчас он вызывается 3 раза, потому что внутри функции One.dart build() есть 3 слушателя (см. streamBuilder()).

Я надеюсь, что это немного яснее по сравнению с предыдущим сообщением (теперь удалено).

class OneState extends State<One>{


  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    _mainBloc.inValue(widget.value);
  }
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: Column(
        children: <Widget>[
         streamBuilder(),
         streamBuilder(),
         streamBuilder()
        ],
      ),
    );
  }

  streamBuilder(){
    return           StreamBuilder(
      stream: _mainBloc.values$,
      builder: (context, AsyncSnapshot<Map<String, Future<String>>> snapshot){
        if(snapshot.connectionState  == ConnectionState.waiting) return Center(child: Container(child: new CircularProgressIndicator()));
        if(!snapshot.hasData) return Center(child: Container(child: Text("No Data"),));
        return FutureBuilder(
          future: snapshot.data[widget.value],
          builder: (contextFuture, AsyncSnapshot<String> snapshotFuture){
            if(snapshotFuture.connectionState == ConnectionState.waiting)
              return Center(child: Container(child: new CircularProgressIndicator()));

            return Center(
              child: Container(
                child: Text(snapshotFuture.data),
              ),
            );
          },
        );
      },
    );
  }

}

блок

class MainBloc{
  final ApiRequest _provider;

  MainBloc(this._provider){
    values$ = _value.stream.transform(
        ScanStreamTransformer((Map<String, Future<String>> cache, String symbol, index){
          print('transformer');
          cache[symbol] = _provider.fetchData();
          return cache;
        },
          <String, Future<String>>{},
        ));
  }

  final _value = BehaviorSubject<String>();

  Observable<Map<String, Future<String>>> values$;

  Function(String symbol) get inValue => _value.sink.add;


  dispose(){
    _value.close();
  }

}

person Jamie White    schedule 27.11.2019    source источник
comment
какова цель использования этой комбинации StreamBuilder + FutureBuilder? чего вы хотите достичь? может быть ScanStreamTransformer это не то, что вам действительно нужно?   -  person pskink    schedule 28.11.2019
comment
У меня есть виджет с отслеживанием состояния, который отображает информацию о пользователе, класс принимает имя пользователя в качестве параметра, внутри данные класса извлекаются с сервера, используя имя пользователя в качестве уникального идентификатора. класс имеет более одного построителя потоков. Причина использования карты внутри convert () заключается в том, чтобы предотвратить отображение на экране неверных данных - при загрузке экрана он отображает «старую» информацию о пользователе с «настоящим» пользователем, в результате чего на экране отображаются данные, которые частично принадлежат разным пользовательским объектам, тогда как если кешировать пользователей, тогда я могу сделать, например. snapshotFuture.data[username] и это, кажется, отображает все данные для конкретного пользователя.   -  person Jamie White    schedule 28.11.2019
comment
Я имею в виду, что вы должны использовать только SteeamBuilder и предоставить правильный Stream, который предоставляет ваши полные данные (а не промежуточные Future)   -  person pskink    schedule 28.11.2019
comment
Иногда поток содержит старый пользовательский объект и отображает старый до того, как будет получен новый объект.   -  person Jamie White    schedule 28.11.2019
comment
см. asyncMqp или подобные меры   -  person pskink    schedule 28.11.2019
comment
То, что вы предлагаете, не работает. Единственный метод, который, по-видимому, предотвращает смешивание данных, - это выше. У вас есть пример?   -  person Jamie White    schedule 28.11.2019
comment
почти каждый учебник по дротикам охватывает asyncMap, так как это практически то же самое, что и метод map - единственная разница в том, что он может быть асинхронным, что означает, что функция сопоставления может возвращать Future   -  person pskink    schedule 28.11.2019
comment
Можем ли мы просто сосредоточиться на заданном вопросе, не отвлекаясь? Я постарался максимально конкретизировать вопрос.   -  person Jamie White    schedule 28.11.2019
comment
это не отвлечение, это фундаментальный принцип использования только StreamBuilder, и любые вещи, связанные с данными (например, преобразование асинхронного потока), должны выполняться в вашей модели данных (я имею в виду в вашем BLoC, а не внутри вложенного FutureBuilder), простой пример: pastebin.com/GziDTpNC — здесь switchMap используется в StreamBuilder, но все зависит от того, как вы хотите обрабатывать свои данные. - проверьте, например, asyncMap / flatMap / concatMap, а также   -  person pskink    schedule 29.11.2019
comment
"What you suggest does not work. The only method that seems prevent the data from being mixed is the above" - сейчас работает? Вы пробовали нажимать кнопки элемента #x и видеть текст выше?   -  person pskink    schedule 30.11.2019
comment
сканирование из пакета transform_stream делает то, что я хочу, но для его использования требуется StreamController.   -  person Jamie White    schedule 30.11.2019


Ответы (1)


Добавьте .asBroadcastStream(). Нравиться:

values$ =_value.stream.transform(
        ScanStreamTransformer((Map<String, Future<String>> cache, String symbol, index){
          print('transformer');
          cache[symbol] = _provider.fetchData();
          return cache;
        },
          <String, Future<String>>{},
        )).asBroadcastStream();

Это должно остановить тройной вызов.

person Daniel Oliveira    schedule 17.12.2019