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.forEachemit.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(),
    );
  }
}