flutter - 如何在 flutter Streambuilder 中重启流?

标签 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 运行正确,但第二次没有启动,如果我关闭屏幕并再次返回它,启动流 - 我收到错误

Bad state: Stream has already been listened to.

我不知道如何通过单击按钮重新加载已完成执行且理论上状态为 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,
            );
          },
        )
    ));

;

最佳答案

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

关于flutter - 如何在 flutter Streambuilder 中重启流?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64756829/

相关文章:

api - string' 不是 'int' 类型 'index' 的子类型,同时将 api 数据提取到 ListView 中。 flutter

flutter - setState根据流更新值

dart - 点击 "under"SpriteWidget 层

调用 pushReplacement 后 Flutter StreamProvider 未更新

flutter - 在 BottomNavigationBar 选项卡之间传递 StreamBuilder => 错误状态 : Stream has already been listened to

flutter - StreamBuilder 子更新在导航后不呈现

flutter - 避免 ListView 不必要的刷新

ios - 无法在 iOS 上使用 ionic2 退出应用程序?有没有办法退出应用程序?

android - Flutter 平面按钮颜色属性不起作用

database - Flutter sqflite 打开已有数据库