如何在Flutter中使用Bloc模式进行错误处理?

时间:2018-10-05 01:14:36

标签: error-handling flutter reactive-programming reactiveui

想象一下我正在使用集团来处理网络请求。如果请求失败,则根据平台的不同,处理失败的方法也会有所不同。在我的Web应用程序上,我想将用户重定向到错误页面,而在IOS应用程序上,我想显示一个对话框。

由于只能使用bloc并共享它来处理业务逻辑,并且错误处理部分与业务逻辑无关,因此我们应该请ui部分来处理错误处理。

ui可以将错误回调发送到bloc,并且bloc将在发生错误时运行它。我们还可以通过在不同平台上发送不同的回调,以特定于平台的方式处理错误。

然后有两个问题:

是否有更合适的方法来做到这一点?

如何将回调发送到集团?

在扑朔迷离中,我们只有在initState生命周期方法之后才能访问bloc(因为我们从构建器上下文中获取bloc,而后者仅在initState之后)。然后,我们只能在build方法中发送回调。

这样,我们将在每次重建时向bloc重复发送回调(这些重复没有意义)。     使用react,可以在诸如componentDidMount之类的生命周期中完成一次初始化。 在颤抖中,我们如何达到只运行一次初始化的目标?

3 个答案:

答案 0 :(得分:0)

这就是我们在团队中的处理方式:

首先,我们以如下方式构建主页(导航根):

Master

我们这样声明 @override Widget build(BuildContext context) { return BlocBuilder<SuspectEvent, SuspectState>( bloc: _bloc, builder: (context, state) { if (state.cameras.isEmpty) _bloc.dispatch(GetCamerasEvent()); if (!_isExceptionHandled) { _shouldHandleException( hasException: state.hasException, handleException: state.handleException); } return Scaffold( ... (仍在主页上):

_shouldHandleException

在我们的街区,我们有:

  _shouldHandleException(
      {@required bool hasException, @required Exception handleException}) {
    if (hasException) {
      if (handleException is AuthenticationException) {
        _isExceptionHandled = true;
        SchedulerBinding.instance.addPostFrameCallback((_) async {
          InfoDialog.showMessage(
                  context: context,
                  infoDialogType: DialogType.error,
                  text: 'Please, do your login again.',
                  title: 'Session expired')
              .then((val) {
            Navigator.popUntil(context, ModalRoute.withName('/'));
            this._showLogin();
          });
        });
      } else if (handleException is BusinessException) {
        _isExceptionHandled = true;
        SchedulerBinding.instance.addPostFrameCallback((_) async {
          InfoDialog.showMessage(
                  context: context,
                  infoDialogType: DialogType.alert,
                  text: handleException.toString(),
                  title: 'Verify your fields')
              .then((val) {
            _bloc.dispatch(CleanExceptionEvent());
            _isExceptionHandled = false;
          });
        });
      } else {
        _isExceptionHandled = true;
        SchedulerBinding.instance.addPostFrameCallback((_) async {
          InfoDialog.showMessage(
                  context: context,
                  infoDialogType: DialogType.error,
                  text: handleException.toString(),
                  title: 'Error on request')
              .then((val) {
            _bloc.dispatch(CleanExceptionEvent());
            _isExceptionHandled = false;
          });
        });
      }
    }
  }

在我们的错误处理(位于主页上)中, @override Stream<SuspectState> mapEventToState(SuspectEvent event) async* { try { if (event is GetCamerasEvent) { ... //(our logic) yield (SuspectState.newValue(state: currentState) ..cameras = _cameras ..suspects = _suspects); } ... //(other events) } catch (error) { yield (SuspectState.newValue(state: currentState) ..hasException = true ..handleException = error); } } 仅仅是InfoDialog(来自Flutter),它位于任何路由的顶部。因此,只需要在根路由上调用警报即可。

答案 1 :(得分:0)

如果将BLoC封装在initState方法中,则可以通过scheduleMicrotask方法访问BLoC,以便它在initState方法完成后运行:

@override
void initState() {
  super.initState();
  // Do initialization here.
  scheduleMicrotask(() {
    // Do stuff that uses the BLoC here.
  });
}

您还可以签出this answer to a different question来概述简单BLoC模式,该模式仅在BLoC上直接调用异步方法,而不是将事件放入接收器。

这将允许这样的代码:

Future<void> login() {
  try {
    // Do the network stuff, like logging the user in or whatever.
    Bloc.of(context).login(userController.text, emailController.text);
  } on ServerNotReachableException {
    // Redirect the user, display a prompt or change this
    // widget's state to display an error. It's up to you.
  }
}

答案 2 :(得分:0)

您可以使用superEnum包为Bloc创建状态和事件。(在这里,您可以通过执行以下操作为Error声明状态:

  @Data(fields: [DataField<Error>('error')])
  OrderLoadingFailedState,

(如果有人需要使用它的示例,请告诉我,我将向您显示示例)