如何在颤动中使用 bloc 模式进行错误处理?

Posted

技术标签:

【中文标题】如何在颤动中使用 bloc 模式进行错误处理?【英文标题】:How to do error handling with bloc pattern in flutter? 【发布时间】: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 可以倾听并采取行动? 我也想知道这个....在这里看到一个 -> ***.com/questions/52914493/… 【参考方案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),它位于任何路由之上。所以只需要在根路由上调用警报即可。

【讨论】:

以上是关于如何在颤动中使用 bloc 模式进行错误处理?的主要内容,如果未能解决你的问题,请参考以下文章

应该将 Bloc 模式用于颤动文本字段,如果是的话如何?

使用 get_it 时颤动 bloc 处理

为啥 AnimatedList 在颤动中不使用 bloc 状态的列表构建?

如何在颤动中使用带有 Bloc 的冷冻包?

如何在颤动中使用 hydated_bloc 保持状态?

如何使用 BLoC 在颤动的屏幕之间传递数据?