How restart stream in flutter Streambuilder?

I have a simple stream that returns a countdown when an operation is performed.

static Stream<double> counterStream = (() async* {
double i = 0.1;
double z = 0.0;
while (z < 0.99) {
  await Future.delayed(Duration(seconds: 1));
  yield  z = z+i;
}
})();

I do not start it immediately when the widget is initialized, but by taping on the button that is inside StreamBuilder

 onPressed: () {
         setState(() {
         _result = DatabaseAccess.counterStream;
 });},

First time Stream run correctly, but the second time does not start, and if I close the screen and return to it again, start the stream - I get an error

Bad state: Stream has already been listened to.

I don’t know how to reload a stream that has already completed its execution and, in theory, has the status ConnectionState.done by clicking on the button.

Full class code

  class UpdatePage extends StatefulWidget {
  UpdatePage({Key key, this.title}) : super(key: key);

  static const String routeName = "/UpdatePage";
  final String title;

  @override
   _UpdatePageState createState() => new _UpdatePageState();
 }


 class _UpdatePageState extends State<UpdatePage> {

 Stream<double> _result;

@override
Widget build(BuildContext context) {


return new Scaffold(
    appBar: new AppBar(
      title: new Text(""),
    ),
    body: SafeArea(
        child: StreamBuilder<double>(
          stream:  _result,
          builder: (BuildContext context, AsyncSnapshot<double> snapshot) {
            List<Widget> children;
            if (snapshot.hasError) {
              children = <Widget>[
                Center(
                  child: Icon(
                    Icons.error_outline,
                    color: Colors.red,
                    size: 60,
                  ),
                ),
                Padding(
                  padding: const EdgeInsets.only(top: 16),
                  child: Text('Error: ${snapshot.error}'),
                )
              ];
            } else {
              switch (snapshot.connectionState) {
                case ConnectionState.none:
                  children = <Widget>[
                    Center(child: Padding(
                      padding: const EdgeInsets.all(8.0),
                      child: Container(child: Text("Обновление данных", style: new TextStyle( fontSize: 18.0))),
                    )),
                    Center(
                      child: Container(padding: EdgeInsets.all(20.0),child: RaisedButton.icon(
                        textColor: Colors.white,
                        icon:   FaIcon(FontAwesomeIcons.retweet, size: 18,),
                        color: Color(0xFF6200EE), label: Text("Обновить"),
                        onPressed: () {
                          setState(() {
                            _result = DatabaseAccess.counterStream;
                          });
                        },
                      ),),
                    ),
                  ];
                  break;
                case ConnectionState.waiting:
                  children = <Widget>[
                    Center(
                      child: new Padding(
                        padding: EdgeInsets.symmetric(horizontal: 10.0),
                      ),
                    ),
                    new CircularPercentIndicator(
                      radius: MediaQuery.of(context).size.width/2,
                      lineWidth: 4.0,
                      center: new Text('', style: new TextStyle( fontSize: 20.0)),
                    ),
                  ];
                  break;
                case ConnectionState.active:
                  children = <Widget>[
                    Center(
                      child: new Padding(
                        padding: EdgeInsets.symmetric(horizontal: 10.0),
                      ),
                    ),
                    new CircularPercentIndicator(
                      radius: MediaQuery.of(context).size.width/2,
                      lineWidth: 4.0,
                      percent: snapshot.data,
                      center: new Text('${snapshot.data.toStringAsFixed(2)}', style: new TextStyle( fontSize: 20.0)),
                      progressColor: Colors.orange,

                    ),

                  ];
                  break;
                case ConnectionState.done:
                  children = <Widget>[
                    new CircularPercentIndicator(
                      radius: MediaQuery.of(context).size.width/2,
                      lineWidth: 4.0,
                      center: new Icon(
                        Icons.done,
                        size: MediaQuery.of(context).size.width/4,
                        color: Colors.green,
                      ),
                      backgroundColor: Colors.grey,
                      progressColor: Colors.orange,
                    )
                    ,
                    Center(child: Padding(
                      padding: const EdgeInsets.all(8.0),
                      child: Container(child: Text("Обновление успешно завершено", style: new TextStyle( fontSize: 18.0))),
                    )),
                    Center(
                      child: Container(padding: EdgeInsets.all(20.0),child: RaisedButton.icon(
                        textColor: Colors.white,
                        icon:   FaIcon(FontAwesomeIcons.retweet, size: 18,),
                        color: Color(0xFF6200EE), label: Text("Обновить"),

                        onPressed: () {
                            setState(() {
                            _result = DatabaseAccess.counterStream;
                            });
                        },

                      ),),
                    ),
                  ];

                  break;
              }
            }

            return Column(
              mainAxisAlignment: MainAxisAlignment.center,
              crossAxisAlignment: CrossAxisAlignment.center,
              children: children,
            );
          },
        )
    ));

;

}

}

Upvotes: 3

Views: 4140

Answers (1)

Gazihan Alankus
Gazihan Alankus

Reputation: 11984

Since counterStream is static, it is initialized once during the lifetime of the app. During its initialization, the async* function is called once.

So, you create only one stream during the lifetime of the application. counterStream is a reference to that stream.

The second time you press the button, you set the _result to what it already was, the reference to the stream that I mentioned above. Therefore, StreamBuilder does not change anything. As far as it is concerned, it's a rebuild without change.

When you close the screen and come back, your new StreamBuilder is trying to treat the existing, already listened to stream as a new stream, hence the error.

The solution

I think you are trying to restart a countdown every time the button is pressed. Editing the code to be like this may solve it:

static Stream<double> Function() counterStream = () async* {
double i = 0.1;
double z = 0.0;
while (z < 0.99) {
  await Future.delayed(Duration(seconds: 1));
  yield  z = z+i;
}
};
 onPressed: () {
         setState(() {
         _result = DatabaseAccess.counterStream();
 });},

Upvotes: 1

Related Questions