title: Flutter nested StreamBuilders causing Bad state: Stream has already been listened to
body: <p>I'm trying to build a Flutter app using the BLoC pattern described in the video <a href="https://www.youtube.com/watch?v=PLHln7wHgPE&amp;feature=youtu.be" rel="noreferrer" title="Flutter / AngularDart – Code sharing, better together &#40;DartConf 2018&#41;">Flutter / AngularDart – Code sharing, better together (DartConf 2018)</a></p>
  
  <p>A BLoC is basically a view model with <code>Sink</code> inputs and <code>Stream</code> outputs. In my example it looks a bit like this:</p>
  
  <pre><code> class BLoC {
      // inputs
      Sink&lt;String&gt; inputTextChanges;
      Sink&lt;Null&gt; submitButtonClicks;
  
      // outputs
      Stream&lt;bool&gt; showLoading;
      Stream&lt;bool&gt; submitEnabled;
   }
  </code></pre>
  
  <p>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 <code>StreamBuilders</code>. Like so:</p>
  
  <p><a href="https://i.stack.imgur.com/RQlJL.png" rel="noreferrer"><img src="https://i.stack.imgur.com/RQlJL.png" alt="BLoC widget hierarchy"></a></p>
  
  <p>The top <code>StreamBuilder</code> listens to a <code>showLoading</code> stream on the BLoC so that it can rebuild to show an overlaid progress spinner. The bottom <code>StreamBuilder</code> listens to a <code>submitEnabled</code> stream to enable/disable a button.</p>
  
  <p>The problem is whenever the <code>showLoading</code> stream causes the top <code>StreamBuilder</code> to rebuild the widget it rebuilds nested widgets too. This in itself is fine and expected. However this results in the bottom <code>StreamBuilder</code> being recreated. When this happens it attempts to re-subscribe to the existing <code>submitEnabled</code> stream on the BLoC causing <code>Bad state: Stream has already been listened to</code></p>
  
  <p>Is there any way to accomplish this without making all of the outputs <code>BroadcastStreams</code>?</p>
  
  <p>(There is also a chance that I'm fundamentally misunderstanding the BLoC pattern.)</p>
  
  <hr>
  
  <p>Actual code example below:</p>
  
  <pre><code>import 'package:flutter/material.dart';
  import 'package:rxdart/rxdart.dart';
  import 'dart:async';
  
  void main() =&gt; runApp(BlocExampleApp());
  
  class BlocExampleApp extends StatefulWidget {
  
    BlocExampleApp({Key key}) : super(key: key);
  
    @override
    _BlocExampleAppState createState() =&gt; _BlocExampleAppState();
  }
  
  class _BlocExampleAppState extends State&lt;BlocExampleApp&gt; {
  
    Bloc bloc = Bloc();
  
    @override
    Widget build(BuildContext context) =&gt;
        MaterialApp(
          home: Scaffold(
              appBar: AppBar(elevation: 0.0),
              body: new StreamBuilder&lt;bool&gt;(
                  stream: bloc.showLoading,
                  builder: (context, snapshot) =&gt;
                  snapshot.data
                      ? _overlayLoadingWidget(_buildContent(context))
                      : _buildContent(context)
              )
          ),
        );
  
    Widget _buildContent(context) =&gt;
        Column(
            crossAxisAlignment: CrossAxisAlignment.center,
            children: &lt;Widget&gt;[
              TextField(
                onChanged: bloc.inputTextChanges.add,
              ),
              StreamBuilder&lt;bool&gt;(
                  stream: bloc.submitEnabled,
                  builder: ((context, snapshot) =&gt;
                      MaterialButton(
                        onPressed: snapshot.data ? () =&gt; bloc.submitClicks.add(null) : null,
                        child: Text('Submit'),
                      )
                  )
              )
            ]
        );
  
    Widget _overlayLoadingWidget(Widget content) =&gt;
        Stack(
          children: &lt;Widget&gt;[
            content,
            Container(
              color: Colors.black54,
            ),
            Center(child: CircularProgressIndicator()),
          ],
        );
  }
  
  class Bloc {
    final StreamController&lt;String&gt; _inputTextChanges = StreamController&lt;String&gt;();
    final StreamController&lt;Null&gt; _submitClicks = StreamController();
  
    // Inputs
    Sink&lt;String&gt; get inputTextChanges =&gt; _inputTextChanges.sink;
  
    Sink&lt;Null&gt; get submitClicks =&gt; _submitClicks.sink;
  
    // Outputs
    Stream&lt;bool&gt; get submitEnabled =&gt;
        Observable&lt;String&gt;(_inputTextChanges.stream)
            .distinct()
            .map(_isInputValid);
  
    Stream&lt;bool&gt; get showLoading =&gt; _submitClicks.stream.map((_) =&gt; true);
  
    bool _isInputValid(String input) =&gt; true;
  
    void dispose() {
      _inputTextChanges.close();
      _submitClicks.close();
    }
  }
  </code></pre>