Flutter 如何使用StreamBuilder管理引发异常的blocs?

Flutter 如何使用StreamBuilder管理引发异常的blocs?,flutter,dart,dart-http,Flutter,Dart,Dart Http,当我的提供程序在http.get()调用期间出现问题时,我正试图将快照错误状态返回到我的StreamBuilder。在我的例子中,当http.get()返回的状态不同于200(OK)时,我抛出一个异常。 我希望能够将坏状态返回到快照,并针对这种情况执行特定的代码。 现在,当我抛出异常时,应用程序就会崩溃 提供者: class FmsApiProvider { Future<List<FmsListResponse>> fetchFmsList() async {

当我的提供程序在
http.get()
调用期间出现问题时,我正试图将快照错误状态返回到我的StreamBuilder。在我的例子中,当
http.get()
返回的状态不同于200(OK)时,我抛出一个异常。 我希望能够将坏状态返回到快照,并针对这种情况执行特定的代码。 现在,当我抛出异常时,应用程序就会崩溃

提供者:

class FmsApiProvider {
  Future<List<FmsListResponse>> fetchFmsList() async {
    print("Starting fetch FMS..");
    final Response response = await httpGet('fms');
    if (response.statusCode == HttpStatus.ok) {
      // If the call to the server was successful, parse the JSON
      return fmsListResponseFromJson(response.body);
    } else {
      // If that call was not successful, throw an error.
      //return Future.error(List<FmsListResponse>());
      throw Exception('Failed to load FMSs');
    }
  }
}
class-FmsApiProvider{
Future fetchFmsList()异步{
打印(“开始提取FMS..”;
最终响应=等待httpGet('fms');
if(response.statusCode==HttpStatus.ok){
//如果对服务器的调用成功,则解析JSON
返回FmListResponseFromJSON(response.body);
}否则{
//如果该调用未成功,则抛出一个错误。
//返回Future.error(List());
抛出异常(“未能加载FMS”);
}
}
}
存储库:

class Repository {
  final fmsApiProvider = FmsApiProvider();

  Future<List<FmsListResponse>> fetchAllFms() => fmsApiProvider.fetchFmsList();
}
类存储库{
最终fmsApiProvider=fmsApiProvider();
Future fetchAllFms()=>fmsApiProvider.fetchFmsList();
}
集团:

class-fmsgloc{
最终_fmsRepository=Repository();
final _fmsFetcher=PublishSubject();
Observable get allFms=>\u fmsFetcher.stream;
fetchAllFms()异步{
List itemModel=await_fmsRepository.fetchAllFms();
_fmsFetcher.sink.add(itemModel);
}
处置{
_fmsFetcher.close();
}
}
我的StreamBuilder:

StreamBuilder(
            stream: bloc.allFms,
            builder: (context, AsyncSnapshot<List<FmsListResponse>> snapshot) {
              if (snapshot.hasData) {
                return RefreshIndicator(
                    onRefresh: () async {
                      bloc.fetchAllFms();
                    },
                    color: globals.fcsBlue,
                    child: ScrollConfiguration(
                      behavior: NoOverScrollBehavior(),
                      child: ListView.builder(
                          shrinkWrap: true,
                          itemCount:
                              snapshot.data != null ? snapshot.data.length : 0,
                          itemBuilder: (BuildContext context, int index) {
                            final fms = snapshot.data[index];
                            //Fill a global list that contains the FMS for this instances
                            globals.currentFMSs.add(
                                FMSBasicInfo(id: fms.id, code: fms.fmsCode));
                            return MyCard(
                              title: _titleContainer(fms.fmsData),
                              fmsId: fms.id,
                              wmId: fms.fmsData.workMachinesList.first
                                  .id, //pass the firs element only for compose the image url
                              imageType: globals.ImageTypeEnum.iteCellLayout,
                              scaleFactor: 4,
                              onPressed: () => _onPressed(fms),
                            );
                          }),
                    ));
              } else if (snapshot.hasError) {
                return Text('Fms snapshot error!');
              }
              return FCSLoader();
            })
StreamBuilder(
流:bloc.allFms,
生成器:(上下文,异步快照){
if(snapshot.hasData){
返回刷新指示器(
onRefresh:()异步{
bloc.fetchAllFms();
},
颜色:globals.fcsBlue,
子:滚动配置(
行为:NoOverScrollBehavior(),
子项:ListView.builder(
收缩膜:对,
项目计数:
snapshot.data!=null?snapshot.data.length:0,
itemBuilder:(构建上下文,int索引){
最终fms=快照数据[索引];
//填写包含此实例的FMS的全局列表
globals.currentfms.add(
fms基本信息(id:fms.id,代码:fms.fmsCode));
还我的卡(
标题:\标题容器(fms.fmsData),
fmsId:fms.id,
wmId:fms.fmsData.workMachinesList.first
.id,//仅为合成图像url传递firs元素
imageType:globals.ImageTypeEnum.iteCellLayout,
比例因子:4,
按下按钮:()=>\u按下按钮(fms),
);
}),
));
}else if(snapshot.hasrerror){
返回文本('Fms快照错误!');
}
返回FCSLoader();
})

抛出异常时,我希望获取快照错误,然后仅可视化页面中的文本。

您应该将api调用包装在try-catch中,然后将错误添加到接收器中

class FmsBloc {
  final _fmsRepository = Repository();

  final _fmsFetcher = PublishSubject<List<FmsListResponse>>();

  Observable<List<FmsListResponse>> get allFms => _fmsFetcher.stream;

  fetchAllFms() async {
    try {
      List<FmsListResponse> itemModel = await _fmsRepository.fetchAllFms();
      _fmsFetcher.sink.add(itemModel);
    } catch (e) {
      _fmsFetcher.sink.addError(e);
    }
  }

  dispose() {
    _fmsFetcher.close();
  }
}
class-fmsgloc{
最终_fmsRepository=Repository();
final _fmsFetcher=PublishSubject();
Observable get allFms=>\u fmsFetcher.stream;
fetchAllFms()异步{
试一试{
List itemModel=await_fmsRepository.fetchAllFms();
_fmsFetcher.sink.add(itemModel);
}捕获(e){
_fmsFetcher.sink.addError(e);
}
}
处置{
_fmsFetcher.close();
}
}

标记为正确的答案对我不起作用。在进行一些调试时,我看到问题进入了catch/throw:实际上,即使在调试控制台中看到异常,您也从来没有进入过catch/throw

对我来说,在调试中,应用程序不会崩溃,但会在异常上有一个断点,您可以使用“播放”按钮继续播放它。相反,使用Run按钮,您在没有断点的情况下会有相同的行为(就像真正的用户一样)

这是我的BLoC实现流程:http调用->提供者->存储库->BLoC->ui

我试图处理丢失internet连接的情况,但没有检查它,也没有处理一般错误情况

我的证据是抛出异常(“错误”);在中,提供程序不会传播到流的右侧。我还尝试了其他方法,如try/catch,并在代码中的不同级别应用了它们

基本上我需要实现的是调用fetcher.sink.addError('Error');但是从发生错误的提供程序内部。然后在UI中检查snapshot.hasError将返回true,并且该错误可以轻松处理

这是唯一对我有效的(丑陋的)事情:在调用提供程序本身时将sink对象作为输入,并在http调用的onCatchError()函数中通过其函数将错误添加到sink


我希望它能对某人有用。我知道这实际上不是最好的做法,但我只是需要一个快速而肮脏的解决方案。如果有人有更好的解决方案/解释,我将很乐意阅读评论。

是的,这是正确的。然后在您的StreamBuilder中,使用snapshot.hasError为错误案例指定显示的小部件。我正在执行完全相同的操作,但没有调用我的
StreamBuilder
。知道我做错了什么吗?请注意,调用
sink.add()
会调用
StreamBuilder
,但
sink.addError()
不会调用它。
class FmsBloc {
  final _fmsRepository = Repository();

  final _fmsFetcher = PublishSubject<List<FmsListResponse>>();

  Observable<List<FmsListResponse>> get allFms => _fmsFetcher.stream;

  fetchAllFms() async {
    try {
      List<FmsListResponse> itemModel = await _fmsRepository.fetchAllFms();
      _fmsFetcher.sink.add(itemModel);
    } catch (e) {
      _fmsFetcher.sink.addError(e);
    }
  }

  dispose() {
    _fmsFetcher.close();
  }
}