【问题标题】:How to do error handling with bloc pattern in flutter?如何在颤动中使用 bloc 模式进行错误处理?
【发布时间】:2019-03-10 10:55:22
【问题描述】:

想象一下,我正在使用一个 bloc 来处理网络请求。如果请求失败,则处理失败的方式会因平台而异。在我的网络应用程序上,我想将用户重定向到错误页面,而在我的 IOS 应用程序上,我想显示一个对话框。

由于 bloc 应该只用于处理业务逻辑,而错误处理部分与业务逻辑无关,我们应该让 UI 部分负责错误处理。

UI 可以向 bloc 发送错误回调,当错误发生时 bloc 将运行它。我们还可以通过在不同平台发送不同的回调,以特定平台的方式处理错误。

然后是我的两个问题:

有更合适的方法吗?

如何将回调发送到集团?

在 Flutter 中,我们只能在 initState 生命周期方法之后访问 bloc(因为我们从构建器上下文中获取 bloc,它只在 initState 之后出现)。那么我们只能在 build 方法中发送回调。

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

【问题讨论】:

  • 我认为您高估了传递函数引用的成本。
  • 我对 Flutter 和 Dart 还很陌生,但我在想如何处理错误流,以便整个集团将它们下沉,并且 UI 可以倾听并采取行动?
  • 我也想知道这个....在这里看到一个 -> stackoverflow.com/questions/52914493/…

标签: error-handling flutter reactive-programming reactiveui


【解决方案1】:

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

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

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

【讨论】:

    【解决方案2】:

    如果将 BLoC 包装在 scheduleMicrotask 方法中,则可以在 initState 方法中访问 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.
      }
    }
    

    【讨论】:

      【解决方案3】:

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

      首先我们像这样构建我们的主页(导航根):

        @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 只是一个showDialog(来自 Flutter),它位于任何路由之上。所以只需要在根路由上调用警报即可。

      【讨论】:

        猜你喜欢
        • 2020-01-17
        • 2020-01-31
        • 2021-01-27
        • 1970-01-01
        • 2021-04-19
        • 1970-01-01
        • 2020-01-17
        • 2019-05-24
        • 1970-01-01
        相关资源
        最近更新 更多