如何在颤动中使用 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 模式进行错误处理?的主要内容,如果未能解决你的问题,请参考以下文章