Всегда, когда у меня есть асинхронный вызов, который возвращает мне данные о будущем, а также мне нужен этот ответ, чтобы обновить свой уровень пользовательского интерфейса, используя потоки, которые я думаю в шаблоне 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, и это может упростить эту работу, где мы имеем Фьючерсы и потоки работают вместе.
Надеюсь, это поможет.