如何在streambuilder中检测到异步操作结束? [英] How to detect an asynchronous operation is over in streambuilder?

查看:82
本文介绍了如何在streambuilder中检测到异步操作结束?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在这样使用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,显示进度指示器,但这不起作用.为什么?)

请帮助.

解决方案

总是在我有一个异步调用返回我的Future数据时,并且我也需要此响应来使用我认为是BLoC模式的流来更新我的UI层.需要更多的代码,但是它将简化您的问题,并使您的代码更具可读性,可伸缩性,并且像其他操作一样,将流和异步调用从UI代码中排除.

您的情况很简单:

enum DownloadState { NO_DOWNLOAD,  DOWNLOADING, SUCCESS }

通过示例来跟踪async调用Network 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类,它公开一个流,并在该类内部创建示例,该方法通过示例调用Network API,并且在获取结果时,我们可以保存值,转换此值,使用流等将其发送到UI.在downloadData中,在完成Future对象之后,我们在流中放入一个DownloadState值,这将强制正在监听controller.stream的小部件在StreamBuilder帮助下在UI层中重建.如果我们从网络调用中收到一些错误,则将其放入流错误输出controller.sink.addError中,并在UI层中使用snapshot.hasError属性进行检查.

在您的UI层中...

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();

使用这种方法,您可以删除UI层流对象和控制器,不需要setState调用,您将拥有一个包含业务逻辑和外部api调用的特定文件,这可以使我们拥有这种工作的工作更加轻松期货和信息流一起工作.

我希望能帮上忙.

I am using StreamBuilder like this -

StreamController streamController = StreamController.broadcast();

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

I am adding to my stream like this -

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

I am unable to understand the condition I should check to display the progress indicator or my widget. Since I am not passing any data, I can't use hasData. I tried passing dummy data but hasData never becomes false (even after the async function is over). I tried using connectionState but that is always active. I have no idea why it is not changing to waiting state. It does when I use FutureBuilder. (I thought I could just check that if the state is waiting, show the progress indicator but that doesn't work. Why??)

Please help.

解决方案

Always when I have an async call that returns me Future data and also I need this response to update my UI layer using streams I think in BLoC pattern. Is needed a little bit more code but will simplify your problem and make your code more readable, scaleable, and keep streams and async calls out from UI code as other things too.

In your case It's simple:

enum DownloadState { NO_DOWNLOAD,  DOWNLOADING, SUCCESS }

An enum to track what is the state of the async call a Network API call by example.

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 ); ); 
 }
}

A BLoC class that expose a stream and inside this class we create method that call a Network API by example and when the results is fetched we can hold the values, transform this values, send to UI using streams and other things. Here in downloadData after the Future object is completed we just put into stream a DownloadState value and this will force the widgets that are listening controller.stream be rebuilded in UI layer with StreamBuilder help. If we got some error from network call we put this in stream error output controller.sink.addError and in UI layer we'll able to check this with snapshot.hasError property.

In your UI layer...

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 );
)

and in the onPress Event

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

With this approach you remove of your UI layer stream objects and controllers, no setState calls is needed, you will have a specific file with your business logic and external api calls and this can make more easy this kind of job where we have Futures and Streams working together.

I Hope it helps.

这篇关于如何在streambuilder中检测到异步操作结束?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆