Flutter:如果流是 Bloc 状态的一部分,如何将 StreamBuilder 与 BlocBuilder 一起使用?
Flutter: How to Use StreamBuilder With BlocBuilder in Case the Stream Is a Part of The Bloc's State?
我将使用比我的项目简化的版本,但如有必要,请随时索取更多代码。这可能更像是一个架构问题。
我的应用程序中有一个服务(接口)FooService
,它公开了一个流 fooStream
。作为 bloc class MyBloc
.
的成员,我有这项服务
现在 bloc 的状态 MyBlocState
包含 isProcessing
以指示 bloc 是否正在处理,并且它还包含 fooStream
公开服务版本 fooStream
的字段到 UI 层(所以有 3 层:service => bloc => UI)。
现在的问题是,当我想在 UI 中收听此服务流时,我不得不使用 StreamBuilder
,它本身将被包裹在 BlocBuilder
中.这在大型应用程序中会变得非常混乱。那么在欧盟国家内部代表 fooStream
的最佳方式是什么?我可以只听 bloc 内的流并在 fooStream
事件到达时向 bloc 添加事件吗?这会不会是一个不好的副作用,状态将以混乱的方式更新并且无法再跟踪状态的变化?
示例代码:
class FooService{
Stream fooStream;// the stream is exposed here
...
}
class MyBloc extends ... {
FooService myService = ...;//the service is used here as a member
...
}
class MyBlocState extends ... {
Stream? fooStream;//this value will hold the stream exposed from the service and it may be initialized in an init event in the bloc for example
...
}
现在在 UI 我必须做这件丑陋的事情(丑陋因为现在我正在从 2 个地方听状态,从 bloc 和从流,在我看来这违背了目的作为更改状态并将其维护在 UI/app 中的唯一入口点):
Widget build(BuildContext context){
return BlocBuilder<MyBloc,MyBlocState>(
builder: (context, myBlocState) {
if(myBlocState is Loading){
return CircularProgressIndicator();
}
if(myBlocState is Error){
return ErrorWidget();
}
if(myBlocState is SomeState){
return StreamBuilder(//I need to wrap the StreamBuilder inside the BlocBuilder because the stream will be null when the app launches but then it will have a value which is fooStream when I call init in the bloc
stream: myBlocState.fooStream,
builder: (context, snapshot) {
if(snapshot.hasData){
return SomeWidget();
}
else{
return SomeOtherWidget();
}
},
);
}
},
)
}
UI 理想情况下应该只是响应您的 bloc 中的状态变化,因此我强烈建议订阅 bloc 中的流并发出状态以响应流中的数据。您可以查看 timer example 以获取管理订阅的参考,以便 pause/resume。在简单的情况下,您可以在 bloc >=7.2.0 中使用 emit.forEach
或 emit.onEach
,这意味着您不需要手动维护 StreamSubscription
.
您可以在 flutter bloc with stream example 中看到 emit.onEach
的示例,并且 emit.forEach
看起来非常相似:
class TickerBloc extends Bloc<TickerEvent, TickerState> {
TickerBloc(Ticker ticker) : super(TickerInitial()) {
on<TickerStarted>(
(event, emit) async {
await emit.forEach<int>(
ticker.tick(),
onData: (tick) => TickerTick(tick),
);
},
transformer: restartable(),
);
}
}
我将使用比我的项目简化的版本,但如有必要,请随时索取更多代码。这可能更像是一个架构问题。
我的应用程序中有一个服务(接口)FooService
,它公开了一个流 fooStream
。作为 bloc class MyBloc
.
现在 bloc 的状态 MyBlocState
包含 isProcessing
以指示 bloc 是否正在处理,并且它还包含 fooStream
公开服务版本 fooStream
的字段到 UI 层(所以有 3 层:service => bloc => UI)。
现在的问题是,当我想在 UI 中收听此服务流时,我不得不使用 StreamBuilder
,它本身将被包裹在 BlocBuilder
中.这在大型应用程序中会变得非常混乱。那么在欧盟国家内部代表 fooStream
的最佳方式是什么?我可以只听 bloc 内的流并在 fooStream
事件到达时向 bloc 添加事件吗?这会不会是一个不好的副作用,状态将以混乱的方式更新并且无法再跟踪状态的变化?
示例代码:
class FooService{
Stream fooStream;// the stream is exposed here
...
}
class MyBloc extends ... {
FooService myService = ...;//the service is used here as a member
...
}
class MyBlocState extends ... {
Stream? fooStream;//this value will hold the stream exposed from the service and it may be initialized in an init event in the bloc for example
...
}
现在在 UI 我必须做这件丑陋的事情(丑陋因为现在我正在从 2 个地方听状态,从 bloc 和从流,在我看来这违背了目的作为更改状态并将其维护在 UI/app 中的唯一入口点):
Widget build(BuildContext context){
return BlocBuilder<MyBloc,MyBlocState>(
builder: (context, myBlocState) {
if(myBlocState is Loading){
return CircularProgressIndicator();
}
if(myBlocState is Error){
return ErrorWidget();
}
if(myBlocState is SomeState){
return StreamBuilder(//I need to wrap the StreamBuilder inside the BlocBuilder because the stream will be null when the app launches but then it will have a value which is fooStream when I call init in the bloc
stream: myBlocState.fooStream,
builder: (context, snapshot) {
if(snapshot.hasData){
return SomeWidget();
}
else{
return SomeOtherWidget();
}
},
);
}
},
)
}
UI 理想情况下应该只是响应您的 bloc 中的状态变化,因此我强烈建议订阅 bloc 中的流并发出状态以响应流中的数据。您可以查看 timer example 以获取管理订阅的参考,以便 pause/resume。在简单的情况下,您可以在 bloc >=7.2.0 中使用 emit.forEach
或 emit.onEach
,这意味着您不需要手动维护 StreamSubscription
.
您可以在 flutter bloc with stream example 中看到 emit.onEach
的示例,并且 emit.forEach
看起来非常相似:
class TickerBloc extends Bloc<TickerEvent, TickerState> {
TickerBloc(Ticker ticker) : super(TickerInitial()) {
on<TickerStarted>(
(event, emit) async {
await emit.forEach<int>(
ticker.tick(),
onData: (tick) => TickerTick(tick),
);
},
transformer: restartable(),
);
}
}