简体   繁体   English

Flutter嵌套的StreamBuilders导致Bad状态:Stream已被监听

[英]Flutter nested StreamBuilders causing Bad state: Stream has already been listened to

I'm trying to build a Flutter app using the BLoC pattern described in the video Flutter / AngularDart – Code sharing, better together (DartConf 2018) 我正在尝试使用视频Flutter / AngularDart中描述的BLoC模式构建一个Flutter应用程序- 代码共享,更好地在一起(DartConf 2018)

A BLoC is basically a view model with Sink inputs and Stream outputs. BLoC基本上是一个带有Sink输入和Stream输出的视图模型。 In my example it looks a bit like this: 在我的例子中,它看起来有点像这样:

 class BLoC {
    // inputs
    Sink<String> inputTextChanges;
    Sink<Null> submitButtonClicks;

    // outputs
    Stream<bool> showLoading;
    Stream<bool> submitEnabled;
 }

I have the BLoC defined in a widget near the root of the hierarchy and it is passed down to widgets beneath it, including nested StreamBuilders . 我在层次结构根目录附近的小部件中定义了BLoC,并将其传递给它下面的小部件,包括嵌套的StreamBuilders Like so: 像这样:

BLoC小部件层次结构

The top StreamBuilder listens to a showLoading stream on the BLoC so that it can rebuild to show an overlaid progress spinner. 顶级StreamBuilder侦听showLoading流,以便它可以重建以显示重叠的进度微调器。 The bottom StreamBuilder listens to a submitEnabled stream to enable/disable a button. 底部StreamBuilder侦听submitEnabled流以启用/禁用按钮。

The problem is whenever the showLoading stream causes the top StreamBuilder to rebuild the widget it rebuilds nested widgets too. 问题是当showLoading流导致顶级StreamBuilder重建窗口小部件时,它也会重建嵌套窗口小部件。 This in itself is fine and expected. 这本身就很好并且预期。 However this results in the bottom StreamBuilder being recreated. 但是,这会导致重新创建底部的StreamBuilder When this happens it attempts to re-subscribe to the existing submitEnabled stream on the BLoC causing Bad state: Stream has already been listened to 当发生这种情况时,它会尝试重新订阅submitEnabled上的现有submitEnabled流,从而导致Bad state: Stream has already been listened to

Is there any way to accomplish this without making all of the outputs BroadcastStreams ? 有没有办法在不创建所有输出BroadcastStreams情况下实现这一目标?

(There is also a chance that I'm fundamentally misunderstanding the BLoC pattern.) (我也有可能从根本上误解BLoC模式。)


Actual code example below: 下面的实际代码示例:

import 'package:flutter/material.dart';
import 'package:rxdart/rxdart.dart';
import 'dart:async';

void main() => runApp(BlocExampleApp());

class BlocExampleApp extends StatefulWidget {

  BlocExampleApp({Key key}) : super(key: key);

  @override
  _BlocExampleAppState createState() => _BlocExampleAppState();
}

class _BlocExampleAppState extends State<BlocExampleApp> {

  Bloc bloc = Bloc();

  @override
  Widget build(BuildContext context) =>
      MaterialApp(
        home: Scaffold(
            appBar: AppBar(elevation: 0.0),
            body: new StreamBuilder<bool>(
                stream: bloc.showLoading,
                builder: (context, snapshot) =>
                snapshot.data
                    ? _overlayLoadingWidget(_buildContent(context))
                    : _buildContent(context)
            )
        ),
      );

  Widget _buildContent(context) =>
      Column(
          crossAxisAlignment: CrossAxisAlignment.center,
          children: <Widget>[
            TextField(
              onChanged: bloc.inputTextChanges.add,
            ),
            StreamBuilder<bool>(
                stream: bloc.submitEnabled,
                builder: ((context, snapshot) =>
                    MaterialButton(
                      onPressed: snapshot.data ? () => bloc.submitClicks.add(null) : null,
                      child: Text('Submit'),
                    )
                )
            )
          ]
      );

  Widget _overlayLoadingWidget(Widget content) =>
      Stack(
        children: <Widget>[
          content,
          Container(
            color: Colors.black54,
          ),
          Center(child: CircularProgressIndicator()),
        ],
      );
}

class Bloc {
  final StreamController<String> _inputTextChanges = StreamController<String>();
  final StreamController<Null> _submitClicks = StreamController();

  // Inputs
  Sink<String> get inputTextChanges => _inputTextChanges.sink;

  Sink<Null> get submitClicks => _submitClicks.sink;

  // Outputs
  Stream<bool> get submitEnabled =>
      Observable<String>(_inputTextChanges.stream)
          .distinct()
          .map(_isInputValid);

  Stream<bool> get showLoading => _submitClicks.stream.map((_) => true);

  bool _isInputValid(String input) => true;

  void dispose() {
    _inputTextChanges.close();
    _submitClicks.close();
  }
}

As i understand BLoC you should only have one output stream which is connected to a StreamBuilder. 据我了解BLoC,您应该只有一个连接到StreamBuilder的输出流。 This output stream emits a model which contains all required state. 此输出流发出包含所有必需状态的模型。

You can see how its done here: https://github.com/ReactiveX/rxdart/blob/master/example/flutter/github_search/lib/github_search_widget.dart 您可以在此处查看其完成情况: https //github.com/ReactiveX/rxdart/blob/master/example/flutter/github_search/lib/github_search_widget.dart

New Link: https://github.com/ReactiveX/rxdart/blob/master/example/flutter/github_search/lib/search_widget.dart 新链接: https //github.com/ReactiveX/rxdart/blob/master/example/flutter/github_search/lib/search_widget.dart

If you need to combine multiple steams to generate you model (sowLoading and submitEnabled), you can use Observable.combineLatest from RxDart to merge multiple streams into one stream. 如果需要组合多个流来生成模型(sowLoading和submitEnabled),则可以使用RxDart中的Observable.combineLatest将多个流合并为一个流。 I use this approach and it works really nice. 我使用这种方法,它的工作非常好。

使用BehaviorSubject而不是StreamController.BehaviorSubject将最近的事件发送给使用者

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 Flutter BLoC:使用嵌套的 StreamBuilders 是不好的做法吗? - Flutter BLoC: Is using nested StreamBuilders a bad practice? FLUTTER:对于无限数量的嵌套屏幕,最好的 state 管理方法是什么 - FLUTTER: What is the best state management approach for infinite number of nested screens Flutter:如果流是 Bloc 状态的一部分,如何将 StreamBuilder 与 BlocBuilder 一起使用? - Flutter: How to Use StreamBuilder With BlocBuilder in Case the Stream Is a Part of The Bloc's State? Flutter:ChangeNotifier 子类持有 ScrollController 是不好的做法吗? - Flutter : Is it bad practice for a ChangeNotifier subclass to hold a ScrollController? State 在 Flutter 中处理多页 - State handeling in Flutter with multiple pages 使用3个嵌套的for循环不好吗?(或更多!) - Is it a bad practice to use 3 nested for loops?(or more!) 如果企业应用程序没有遵循适当的设计会发生什么 - What happens if a proper design has been not followed for an enterprise application 是否可以全球化已在类中声明的实例? - Is it possible to globalize an instance which has been declared in a Class? Web应用程序开发的体系结构旨在帮助CMS系统之间的互操作性。 完成了吗 - Architecture for Web-App development aimed at aiding interoperability between CMS systems. Has it been done? 如何在调用responde.end()后执行NodeJS / connect中间件? - How to have a NodeJS/connect middleware execute after responde.end() has been invoked?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM