要让欧盟向前the步的神话就此消失:没有完美的方式来处理国家问题.
通常,good architecture is practical:它是可伸缩和可扩展的,并且只需要很少的开销.
因为人们对实用性的看法不同,架构总是会有不同的看法,所以请对以下内容持保留态度,因为我将就如何为您的应用程序采用BLOC阐述我的个人观点.
BLOC是一种非常有前途的Flutter 状态管理方法,因为它有一个标志性的元素:STREAMS.
它们允许将UI与业务逻辑解耦,并且它们很好地使用了在部件子树过时后重新构建整个部件子树的快速方法.
因此,自然地,进出集团的每一次通信都应该使用流,对吗?
+----+ Stream +------+
| UI | --------> | BLoC |
| | <-------- | |
+----+ Stream +------+
嗯,算是吧.
重要的是要记住state management architecture is a means to an end点;你不应该只是为了它而做事,而应该保持开放的心态,仔细判断每个选项的利弊.
我们将块与UI分开的原因是,块不需要关心UI是如何构造的-它只提供一些很好的简单流,数据发生什么都是UI的责任.
但是,尽管流已被证明是将信息从块传输到UI的一种奇妙方式,但它们在另一个方向上增加了不必要的开销:
STREAMS被设计为传输连续的数据流(甚至在名称中也是如此),但是大多数情况下,UI只需要触发块中的单个事件.这就是为什么有时你会看到一些Stream<void>
或类似的陈词滥调的解决方案,仅仅是为了坚持严格的集团做事方式.
此外,如果我们基于来自块的流推送新的路由,块基本上会控制UI流-但是拥有直接控制UI和业务逻辑的代码正是我们试图阻止的事情!
这就是为什么一些开发人员(包括我)只是打破了完全基于流的解决方案,而采用了一种从UI触发块中事件的自定义方式.
就我个人而言,我只是使用方法调用(通常返回Future
)来触发块的事件:
+----+ method calls +------+
| UI | ----------------> | BLoC |
| | <---------------- | |
+----+ Stream, Future +------+
在这里,对于"活动"的数据,BLoC返回Stream
秒,对于方法调用返回Future
秒.
让我们看看你的例子是如何实现的:
- 该区块可以提供用户是否登录的
Stream<bool>
,甚至Stream<Account>
,其中Account
包含用户的帐户信息.
- BLoC还可以提供一个异步
Future<void> signIn(String username, String password)
方法,如果登录成功或抛出错误,该方法将不返回任何内容.
- UI可以自己处理输入管理,并在按下登录按钮后触发如下内容:
try {
setState(() => _isLoading = true); // This could display a loading spinner of sorts.
await Bloc.of(context).signIn(_usernameController.text, _passwordController.text);
Navigator.of(context).pushReplacement(...); // Push logged in screen.
} catch (e) {
setState(() => _isLoading = false);
// TODO: Display the error on the screen.
}
这样,您就可以很好地分离关注点:
- 该块实际上只是做它应该做的事情-处理业务逻辑(在本例中,让用户登录).
- UI只关心两件事:
最后,我想指出的是,这只是一种可能的解决方案,它是通过try 在复杂应用程序中处理状态的不同方式而随着时间的推移而发展起来的.
了解关于状态管理如何工作的不同观点很重要,因此我鼓励您在挖洞上更深入地探讨这个话题,也许可以通过观看来自Google I/O的第"Pragmatic State Management in Flutter"次会议来实现.
EDIT:刚在Brian Egan's architecture samples年发现了这种 struct ,它被称为"简单集团".如果你想了解不同的体系 struct ,我真的建议你看看回购协议.
?当试图向一个块操作提供多个参数时,它会变得更加难看-因为这样您就需要定义一个包装类来将其传递给Stream.
²我承认,启动应用程序时会有点难看:你需要某种启动屏幕,只需判断集团的流,并根据用户是否登录将其重定向到适当的屏幕.这一规则的例外是因为用户执行了一个操作——启动应用程序——但Flutter 框架不允许我们直接参与其中(据我所知,至少不优雅).