使用 Bloc、RxDart 和 StreamBuilder、无状态 vs 有状态和处置的 Flutter 干净架构
我正在尝试实现一个干净的架构,不依赖于业务逻辑层中的框架。
以下示例是仅包含文本
的屏幕。我在存储库中进行 API Rest 调用,并将响应添加到通过将更新 Text
的 StreamBuilder
侦听的 BehaviorSubject
。由于是 StatefulWidget
,我使用 dispose
方法来关闭 BehaviorSubject
的 StreamController
。
该示例是简化的,没有错误/加载状态处理,没有依赖项注入,基类,处置接口等。
class Bloc {
final UserReposiotry _userReposiotry;
final BehaviorSubject<int> _activeUsersCount = BehaviorSubject.seeded(0);
Bloc(this._userReposiotry) {
_getActiveUsersCount();
}
void _getActiveUsersCount() async {
final response = await _userReposiotry.getActiveUsersCount();
_activeUsersCount.add(response.data);
}
ValueStream<int> get activeUsersCount => _activeUsersCount.stream;
void dispose() async {
await _activeUsersCount.drain(0);
_activeUsersCount.close();
}
}
class StatefulScreen extends StatefulWidget {
final Bloc bloc;
const StatefulScreen({Key? key, required this.bloc}) : super(key: key);
@override
State<StatefulScreen> createState() => _StatefulScreenState();
}
class _StatefulScreenState extends State<StatefulScreen> {
@override
Widget build(BuildContext context) {
final stream = widget.bloc.activeUsersCount;
return StreamBuilder<int>(
stream: stream,
initialData: stream.value,
builder: (context, snapshot) {
return Text(snapshot.data.toString());
}
);
}
@override
void dispose() {
widget.bloc.dispose();
super.dispose();
}
}
我对这种方法有以下疑问。
StreamBuilder
自动取消stream
订阅,但不会关闭StreamController
。我知道如果你正在读取文件就应该关闭它,但是在这种情况下,如果我不手动关闭它,一旦StatefulScreen
不再位于导航堆栈中,它是否会被销毁,还是内存泄漏?- 我见过很多人使用
StatelessWidget
而不是StatefulWidget
使用Stream
和StreamBuilder
方法,如果是的话确实需要关闭BehaviorSubject
这是一个问题,因为我们没有dispose
方法,我找到了WillPopScope
但它不会' t 在所有导航情况下都会触发并且并且更重要的是,像WillPopScope
这样的方法会更高效,还是在一个内部有一个StatefulWidget
wrapper (BlocProvider
)StatelessWidget
只是为了进行处理,而不是直接使用StatefulWidget
,如果是这样,您能指出该实现的示例吗? - 我目前正在为具有动画或控制器(地图、文本输入、网页浏览...)或需要关闭的流的小部件选择
StatefulWidget
,其余的StatelessWidget
,这是正确的还是我错过了什么? - 关于
drain
方法,我使用它是因为在 API 休息调用正在进行时返回时遇到错误,我找到了 RxDart 团队的成员说实际上没有必要调用drain
所以我也对此感到困惑......,错误:
从 addStream 添加项目时无法关闭主题
感谢您的宝贵时间。
I'm trying to implement a clean architecture with no dependency of the framework in the business' logic layers.
The following example is a Screen with only a Text
. I make an API Rest call in the repository and add the response to a BehaviorSubject
that is listened through a StreamBuilder
that will update the Text
. Since is an StatefulWidget
I'm using the dispose
method to close the BehaviorSubject
's StreamController
.
The example is simplified, no error/loading state handling, no dependency injection, base classes, dispose interfaces etc.
class Bloc {
final UserReposiotry _userReposiotry;
final BehaviorSubject<int> _activeUsersCount = BehaviorSubject.seeded(0);
Bloc(this._userReposiotry) {
_getActiveUsersCount();
}
void _getActiveUsersCount() async {
final response = await _userReposiotry.getActiveUsersCount();
_activeUsersCount.add(response.data);
}
ValueStream<int> get activeUsersCount => _activeUsersCount.stream;
void dispose() async {
await _activeUsersCount.drain(0);
_activeUsersCount.close();
}
}
class StatefulScreen extends StatefulWidget {
final Bloc bloc;
const StatefulScreen({Key? key, required this.bloc}) : super(key: key);
@override
State<StatefulScreen> createState() => _StatefulScreenState();
}
class _StatefulScreenState extends State<StatefulScreen> {
@override
Widget build(BuildContext context) {
final stream = widget.bloc.activeUsersCount;
return StreamBuilder<int>(
stream: stream,
initialData: stream.value,
builder: (context, snapshot) {
return Text(snapshot.data.toString());
}
);
}
@override
void dispose() {
widget.bloc.dispose();
super.dispose();
}
}
I have the following doubts regarding this approach.
StreamBuilder
cancels thestream
subscription automatically, but it doesn't close theStreamController
. I know that you should close it if you are reading a file, but in this case, if I don't manually close it, once theStatefulScreen
is no longer in the navigation stack, could it be destroyed, or it would be a memory leak?- I've seen a lot of people using
StatelessWidget
instead ofStatefulWidget
usingStream
andStreamBuilder
approach, if it is really needed to close theBehaviorSubject
it is a problem since we don't have thedispose
method, I found about theWillPopScope
but it won't fire in all navigation cases and also and more important would it be more performant an approach likeWillPopScope
, or having anStatefulWidget
wrapper (BlocProvider
) inside anStatelessWidget
just to do the dispose, than using anStatefulWidget
directly, and if so could you point to an example of that implementation? - I'm currently choosing
StatefulWidget
for widgets that have animations o controllers (map, text input, pageview...) or streams that I need to close, the restStatelessWidget
, is this correct or am I missing something? - About the
drain
method, I'm using it because I've encountered an error navigating back while an API rest call was on progress, I found a member of the RxDart team saying it isn't really necessary to calldrain
so I'm confused about this too..., the error:
You cannot close the subject while items are being added from addStream
Thanks for your time.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论