0

In my application I have several streams.

This is from example app from appsflyer. I use it for deep linking my application.

https://pub.dev/packages/appsflyer_sdk

in first page we have ;

return Scaffold(
  appBar: AppBar(
    title: Column(
      children: <Widget>[
        Text('AppsFlyer SDK example app'),
        FutureBuilder<String>(
            future: _appsflyerSdk.getSDKVersion(),
            builder: (BuildContext context, AsyncSnapshot snapshot) {
              return Text(snapshot.hasData ? snapshot.data : "");
            })
      ],
    ),
  ),
  body: FutureBuilder<dynamic>(
      future: _appsflyerSdk.initSdk(
          registerConversionDataCallback: true,
          registerOnAppOpenAttributionCallback: true),
      builder: (BuildContext context, AsyncSnapshot snapshot) {
        if (snapshot.connectionState == ConnectionState.waiting) {
          return Center(child: CircularProgressIndicator());
        } else {
          if (snapshot.hasData) {
            return HomeContainer(
              onData:
                  _appsflyerSdk.conversionDataStream?.asBroadcastStream(),
              onAttribution: _appsflyerSdk.appOpenAttributionStream
                  ?.asBroadcastStream(),
              trackEvent: logEvent,
            );
          } else {
            return Center(child: Text("Error initializing sdk"));
          }
        }
      }),
);

in HomeContainer

  Stream<Map> onData;
  Stream<Map> onAttribution;
  Future<bool> Function(String, Map) trackEvent;

  HomeContainer({this.onData, this.onAttribution, this.trackEvent});

inside build;

Column(
            children: <Widget>[

              StreamBuilder<dynamic>(
              stream: widget.onData,
              builder:
                  (BuildContext context, AsyncSnapshot<dynamic> snapshot) {
                return TextBorder(
                  controller: TextEditingController(
                      text: snapshot.hasData
                          ? Utils.formatJson(snapshot.data)
                          : "No conversion data"),
                  labelText: "Conversion Data:",
                );
              }),
              Padding(
                padding: EdgeInsets.only(top: 12.0),
              ),
              StreamBuilder<dynamic>(
              stream: widget.onAttribution,
              builder:
                  (BuildContext context, AsyncSnapshot<dynamic> snapshot) {
                return TextBorder(
                  controller: TextEditingController(
                      text: snapshot.hasData
                          ? _processData(snapshot.data)
                          : "No attribution data"),
                  labelText: "Attribution Data:",
                );
              }),
             
            ],
          ),

What I have done is to put a button to reload the screens, using a notifyListeners.

When I do that, I get following error;

Bad state: Stream has already been listened to.

I assume, the problem may be due to these streams are still open when I reload the page.

But I don't know how to close them.

Can anyone provide some assistance on this?

Janaka
  • 2,505
  • 4
  • 33
  • 57

1 Answers1

1
StreamBuilder<dynamic>(
  stream: widget.onData?.asBroadcastStream(),

You're calling asBroadcastStream() multiple times (every time the build method is called) on widget.onData. You should be able to just use:

StreamBuilder<dynamic>(
  stream: widget.onData,

And if widget.onData is listened to elsewhere, make sure widget.onData itself is a broadcast stream.

spkersten
  • 2,849
  • 21
  • 20
  • Thank you for your answer. Tried stream: widget.onData, The error is still there. Is there anything else I could try? – Janaka Jan 08 '21 at 09:42
  • 1
    The second part of the answer: Make sure `widget.onData` itself is a broadcast stream. So wherever you first get/construct the stream, use `asBroadcastStream`. – spkersten Jan 08 '21 at 09:50
  • I am not very familiar with streams. I have made modifications to the code as you have suggested. But I am still getting the same error. I updated the current version of the code in my post. Is it possible for you to suggest a solution? – Janaka Jan 08 '21 at 10:53
  • 1
    In the FutureBuilder's builder method you have two `asBroadcastStream()`. build(er) methods could be called multiple times, so you shouldn't do anything in them that should only happen once. If you remove those `asBroadcastStream()` calls, it might work (rebuilding the same StreamBuilder with the same stream doesn't cause it to listen to it again, so that is fine). – spkersten Jan 08 '21 at 12:15
  • 1
    A second thing you do in a build(er) method that should only happen once is the creation of future (e.g. `_appsflyerSdk.initSdk`). You better do those things in a place that guarantees they only happen once. For example in the `main` method (if it is initialisation of some global entity) or in the `initState` of a `StatefulWidget` – spkersten Jan 08 '21 at 12:17