Как обнаружить, что асинхронная операция закончена в streambuilder? - PullRequest
0 голосов
/ 25 апреля 2019

Я использую StreamBuilder вот так -

StreamController streamController = StreamController.broadcast();

StreamBuilder(
    stream: streamController.stream,
    builder: (BuildContext context, AsyncSnapshot snapshot) {
      return (snapshot.hasData == true)  //THIS CONDITION!!!
          ? CircularProgressIndicator()
          : myWidget();
)

Я добавляю в свой поток вот так -

onPressed: () {
     streamController.add(null);
 },

Я не могу понять условие, которое я должен проверитьотобразить индикатор прогресса или мой виджет.Поскольку я не передаю никаких данных, я не могу использовать hasData.Я попытался передать фиктивные данные, но hasData никогда не становится ложным (даже после завершения асинхронной функции).Я пытался использовать connectionState, но это всегда активно.Я понятия не имею, почему он не переходит в состояние waiting.Это когда я использую FutureBuilder.(Я думал, что могу просто проверить, что если состояние waiting, показать индикатор прогресса, но это не работает. Почему ??)

Пожалуйста, помогите.

1 Ответ

1 голос
/ 27 апреля 2019

Всегда, когда у меня есть асинхронный вызов, который возвращает мне данные о будущем, а также мне нужен этот ответ, чтобы обновить свой уровень пользовательского интерфейса, используя потоки, которые я думаю в шаблоне BLoC. Требуется немного больше кода, но он упростит вашу проблему и сделает ваш код более читабельным, масштабируемым, а также сохранит потоки и асинхронные вызовы из кода пользовательского интерфейса, как и другие вещи.

В вашем случае все просто:

enum DownloadState { NO_DOWNLOAD,  DOWNLOADING, SUCCESS }

Перечисление для отслеживания состояния вызова async с помощью вызова сетевого API на примере.

class Bloc {

  final ApiClass api = new ApiClass();   // simulating your downloader object
  final StreamController controller = StreamController<DownloadState>.broadcast(); 
  Stream get dataState => controller.stream; // exposing your stream output

 void _changeState( final DownloadState state ) => controller.sink.add( state );

  void downloadData(){
      _changeState( DowloadState.DOWNLOADING );     
      // assuming that this call returns a Future object.
      api.downloadData().then(  (yourNetworkData) {
         // handle your downloaded data 
        _changeState( DownloadState.SUCCESS );
       }  ).catchError( (apiError) =>  controller.sink.addError( apiError ); ); 
 }
}

Класс BLoC, который представляет поток, и внутри этого класса мы создаем метод, который на примере вызывает API-интерфейс сети, и когда результаты извлекаются, мы можем хранить значения, преобразовывать эти значения, отправлять в пользовательский интерфейс с использованием потоков и других вещей. Здесь, в downloadData после завершения объекта Future, мы просто помещаем в поток значение DownloadState, и это заставит виджеты, которые прослушивают controller.stream, перестраиваться на уровне пользовательского интерфейса с помощью StreamBuilder. Если мы получили какую-то ошибку от сетевого вызова, мы поместим ее в вывод ошибки потока controller.sink.addError, а на уровне пользовательского интерфейса мы сможем проверить это с помощью свойства snapshot.hasError.

В вашем слое пользовательского интерфейса ...

Bloc bloc = Bloc(); // you can create a instance of BLoC in initState or in widget contructor...
StreamBuilder<DownloadState>(
    stream: bloc.dataState, // bloc get method that returns stream output.
    initialData: DownloadState.NO_DOWNLOAD. // this will be the inital value. It's optional
    builder: (BuildContext context, AsyncSnapshot snapshot) {
      if (snapshot.hasData){
          switch(snapshot.data){
               case  DownloadState.NO_DOWNLOAD:
                  return NoDownloadWidget();
               case  DownloadState.DOWNLOADING:
                  return CircularProgressIndicator();
                case  DownloadState.SUCCESS:
                   return myWidget();
          }
      }
       return ErrorWidget( snapshot.error );
)

и в событии onPress

onPressed: () => bloc.downloadData();

При таком подходе вы удаляете свои потоковые объекты и контроллеры уровня пользовательского интерфейса, вызовы setState не требуются, у вас будет определенный файл с вашей бизнес-логикой и внешними вызовами API, и это может упростить эту работу, где мы имеем Фьючерсы и потоки работают вместе.

Надеюсь, это поможет.

...