如何在Flutter Streambuilder中重新启动流?

时间:2020-11-09 17:57:43

标签: flutter stream-builder

我有一个简单的流,该流在执行操作时返回倒计时。

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;
}
})();

在初始化小部件时,我不会立即启动它,而是点击StreamBuilder内部的按钮

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

第一次Stream正常运行,但是第二次没有启动,如果我关闭屏幕并再次返回,则启动Stream-我收到错误消息

状态不良:流已经被收听。

我不知道如何重新加载已经完成执行的流,并且从理论上讲,通过单击按钮,其状态为ConnectionState.done

完整的课程代码

  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,
            );
          },
        )
    ));

;

}

}

1 个答案:

答案 0 :(得分:1)

由于counterStream是静态的,因此在应用程序的生命周期内将其初始化一次。在初始化期间,async*函数将被调用一次。

因此,在应用程序的生命周期内只能创建一个流。 counterStream是对该流的引用。

第二次按下按钮时,将_result设置为已经存在的内容,即对我上面提到的流的引用。因此,StreamBuilder不会更改任何内容。就其而言,这是一个无需更改的重建。

当您关闭屏幕并返回时,您的新StreamBuilder试图将已侦听的现有流视为新流,因此会出现错误。

解决方案

我认为您是在每次按下按钮时都尝试重新开始倒计时。像这样编辑代码可以解决该问题:

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();
 });},