Почему свойство hasData StreamBuilder не имеет значения True, если Observable генерирует событие?

Мне нужно реализовать поиск и отображение результатов. Я использую Observables от RxDart.

abstract class SearchState {}
class SearchCompleted extends SearchState {}
class SearchEmpty extends SearchState {}

final _searchSubject = PublishSubject<String>();

Observable<SearchState> get result {
  return _searchSubject.switchMap((term) {
    return _search(term); // returns Observable<SearchCompleted>
  }).switchIfEmpty(Observable.just(SearchEmpty()));
}

Где-то в StreamBuilder используется этот результат как источник для потока. И при открытии экрана (без поиска) я ожидаю, что

snapshot.hasData = true

потому что мой наблюдаемый выводит SearchEmpty, но я получаю false. Что я сделал не так? Все, что мне нужно, это просто отобразить какое-нибудь сообщение, если результат поиска пуст.

UPD: После дополнительного исследования StreamBuilder, чтения документации RxDart и систематизации информации из pskink я пришел к выводу, что ошибался. switchIfEmpty () означает, что поток переключается на резервный, только если исходный поток ничего не возвращает (после помещения значения в приемник). Мне нужно использовать startWith (), который заставляет наблюдаемое излучать обязательное начальное значение. Итак, правильный код

Observable<SearchState> get result {
  return _searchSubject.switchMap((term) {
    return _search(term); // returns Observable<SearchCompleted>
  }).startWith(SearchEmpty());
}

UPD2: при первой сборке snapshop.hasData = false виджета StreamBuilder, даже с использованием startWith (), потому что connectionStatus = ConnectionStatus.waiting (т.е. когда поток готовится к приему данных). Чтобы этого избежать, вы должны установить значение для initialData свойство. Например:

StreamBuilder(
  initialData: SearchEmpty(),
  stream: result,
  builder: ...
)

Или вы можете вернуть какой-нибудь виджет, пока соединение находится в состоянии ожидания. Например:

StreamBuilder(
  stream: result,
  builder: (context, snapshot) {
    // this allow to skip using `initialData`
    if (snapshot.connectionStatus == ConnectionStatus.waiting) {
      return Center(child: CircularProgressIndicator());
    }
    // Process data
    if (snapshot.hasData) {
      if (snapshot.data is SearchEmpty()) { return Text('no items');}
      if (snapshot.data is SearchCompleted()) { return ListView(...);}
    }
  }
),

person BambinoUA    schedule 20.05.2019    source источник
comment
что возвращает search(term), если ничего не найдено? Observable.empty()?   -  person pskink    schedule 21.05.2019
comment
Что я заметил, если я открываю страницу в первый раз (термин пустой), поток кода не идет внутрь switchMap (), т.е. наблюдаемый результат возвращается сразу и hasData = false. Да, когда _search () не имеет найденных записей, он возвращает SearchEmpty (). Но вопрос в том, почему result не переключается на резервный поток, когда он ничего не испускает при запуске.   -  person BambinoUA    schedule 21.05.2019
comment
попробуйте это и посмотрите журналы: PublishSubject<String> subject = PublishSubject(); subject .switchMap((s) => _search(s).switchIfEmpty(Observable.just('*** NOT FOUND ***'))) .listen(print); - проверьте это гипотетическим _search() методом: Observable<String> _search(String term) { return term.length < 8 ? Observable.just('[$term]') : Observable.empty(); }   -  person pskink    schedule 21.05.2019
comment
Я сделал это и не увидел никаких результатов. Но когда я это сделал _searchSubject.add('test'), я увидел test Эта модификация subject.switchMap((s) => _search(s)).switchIfEmpty(Observable.just('*** NOT FOUND ***')).listen(print); также не дает ответа.   -  person BambinoUA    schedule 21.05.2019
comment
Как я уже упоминал в комментарии выше, я уже сделал то, что вы предложили. Вместо использования TextController я просто напрямую отправляю значение в поток. Но (повторяю :)) проблема в том, что когда ничего не отправляется, я не вижу *** NOT FOUND *** в консоли. Похоже, это событие не запускается, когда поток пуст. :(   -  person BambinoUA    schedule 21.05.2019
comment
почему ты хочешь не найти, если ты ничего не набирал? или я что-то упускаю? если нет, попробуйте Observable#startWith может быть?   -  person pskink    schedule 21.05.2019
comment
Почему? Потому что ... Когда исходный наблюдаемый объект не генерирует никаких элементов, этот оператор подписывается на данный резервный поток и вместо этого генерирует элементы из этого наблюдаемого объекта. switchIfEmpty Я хочу что-то отправить, если observable ничего не отправляет. Может быть, я неправильно понял написанный документ?   -  person BambinoUA    schedule 21.05.2019
comment
startWith не помогает. Он добавляет в начало startValue только тогда, когда я отправлю что-то в поток. И мне не нужно отправлять пустую строку, потому что это запрещено (почему мне нужно искать пустую строку?) Мне нужно отправить какое-то значение по умолчанию, когда поиск не выполняется еще или когда я хочу восстановить последний результат поиска, когда я закрыл, а затем повторно открыть страницу.   -  person BambinoUA    schedule 21.05.2019
comment
Позвольте нам продолжить это обсуждение в чате.   -  person BambinoUA    schedule 21.05.2019


Ответы (1)


На данный момент у меня была что-то вроде этой проблемы, я работал над этим, получая DataSnapshot, который возвращает null, если нет ключа / значения.

if (snapshot.hasData && snapshot.data.snapshot.value != null) {
  return //your code
}
else if(snapshot.hasData && snapshot.data.snapshot.value == null){
  return //no items found
}
else{
  return //your code
}

Таким образом, я мог управлять StreamBuilder, когда у него нет данных.

Это также будет работать:

if(snapshot.hasData){
  if(snapshot.data.snapshot.value == null){
    return //no data found
  }
  else{
    return //data
  }
}
person Escobar    schedule 21.05.2019