BLoC 和多个流 - 有更好的解决方案吗?

Posted

技术标签:

【中文标题】BLoC 和多个流 - 有更好的解决方案吗?【英文标题】:BLoCs and multiple streams - Is there a better solution? 【发布时间】:2019-11-22 05:02:35 【问题描述】:

目前我正在使用 Flutter 中的 BLoC,并且我有一个关于 Bloc 中的多个流的问题。

例如,当一个屏幕有多个应该依赖于 Bloc 的小部件时。我可以将整个屏幕包裹在 StreamBuilder 中,但每次都会重新构建所有小部件。

示例块:

class TestBloc 
  final StreamController _dataController = StreamController<String>();
  final StreamController _appBarTitleController = StreamController<String>();

  TestBloc();

  Stream<String> get appBarTitle => _appBarTitleController.stream;

  Stream<DataState> get data => _dataController.stream;

  void fetchData(String path) async 
    _dataController.sink.add(PokemonDataLoading());

    Data data = await _getData();

    _dataController.sink.add(Loaded(data));
    _appBarTitleController.sink.add(data.name);
  

  Future<Data> _getData(String path) async 
    return await _dataRepository.fetchData(path);
  

  void dispose() 
    _dataController.close();
    _appBarTitleController.close();
  

在示例构建方法中,您可以看到两个不同的 StreamBuilder,一个用于应用栏标题,一个用于内容。当然,我可以在这个示例中将它们包装到一个 StreamBuilder 中,但有时这并不容易。它们可能依赖于其他数据或用户交互。

@override
Widget build(BuildContext context) 
  _testBloc.fetchData();

  return ScaffoldWithSafeArea(
    title: StreamBuilder(
      stream: _testBloc.appBarTitle,
      builder: (context, AsyncSnapshot<String> snapshot) 
        if (snapshot.hasData) 
          return Text(snapshot.data);
        
        return Text("Test");
      ,
    ),
    child: StreamBuilder<DataState>(
      stream: _testBloc.data,
      builder: (context, AsyncSnapshot<DataState> snapshot) 
        DataState state = snapshot.data;

        if (state is DataInitial) 
          return _buildLoading();
         else if (state is DataLoaded) 
          return _buildContent(state.data);
        
        return _buildLoading();
      ,
    ),
  );

对于一个屏幕上的多个流,是否有更好的解决方案?我在这里使用了很多样板代码,并希望避免这种情况。

【问题讨论】:

【参考方案1】:

为了在一个屏幕中管理多个流,最好的解决方案是让多个小部件监听相应的流。

这样,您可以通过优化小部件的构建总数来提高应用的性能。

通过这样做,您可以创建侦听 BLoC 输出(流)的小部件,并在应用的不同部分重用它们,但为了使小部件可重用,您需要将 BLoC 注入小部件。

如果你看到BLoC UI design guidelines

每个“足够复杂”的组件都有一个对应的 BLoC

这样,您的屏幕现在将由不同的组件组成,并且此组件是一个小部件,用于侦听 BLoC 的输出(流)。

所以你做对了。

如果您想稍微减少小部件中的重复代码,您可以:

创建您自己的小部件,它侦听流并直接返回 BLoC 的输出(在您的情况下,您调用状态),这样您就不需要像在 StreamBuilder 中那样使用 snapshot.data。此小部件的示例是flutter bloc 库的BlocBuilder。

使用 flutter bloc 库,该库具有在使用 BLoC 模式时减少样板代码的小部件,但如果您使用此库,您现在需要使用 bloc 库创建 BLoC,但如果您这样做,现在您减少在 BLoC 中创建 StreamControllers 的样板代码以及其他有趣的功能,因此您应该看看 bloc 和 Flutter bloc 库的强大功能。

【讨论】:

感谢您的回答。我尝试了flutter_bloc,乍一看我很喜欢。但是,这需要更多样板代码,因为我总是必须创建新事件。这些事件再次将逻辑分开,但这对于许多较小的集团来说是广泛的。另外,我不确定如何用flutter_bloc实现上面的例子。据我所知,对于 AppBar,我需要一个自己的 Bloc,而对于内容,我需要另一个。我只能对同一个 bloc 使用 Bloc Builder 一次,因为只有一个 mapEventToState 方法。 您好,您不需要为这种情况创建其他块,您的state 可以具有 data 和 title 属性,或者在您的情况下,您可以从 data 属性中获取标题。现在你可以使用2个BlocBuilder,如果你想优化标题和数据BlocBuilder的构建,你需要将函数传递给condition参数,例如condition: (previousState, currentState) =&gt; previousState.title != currenState.title;见:***.com/a/57025066/11768068。 谢谢,条件是一个不错的选择。我仍然不确定我是否更喜欢flutter_bloc,而不是像这样的自定义StreamBuilder:gist.github.com/DFelten/c00529f9641c738126d1360e692f21e0 @Daniel 你找到解决方案了吗?我有同样的问题,我不喜欢 bloc,但同时没有很多选择。除了制作自己的架构。

以上是关于BLoC 和多个流 - 有更好的解决方案吗?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 BLOC 为对象列表创建流和接收器

用于ng-repeat的多个角度滤波器 - 有更好的方法吗?

Flutter - 关闭小部件时 BLoC 流实例会导致内存泄漏吗?

使用 DI 将 BLoC 作为单例注入的效果以及在哪里关闭流?

flutter_bloc - 每个 Bloc 有多个存储库,还是只有一个?

Flutter Bloc A 在 Bloc B 中添加流,但此流不会通过 StreamBuilder 在 UI 中呈现