Flutter DataStream无法关闭并正确重建。 [错误状态:流已经被收听。]

时间:2020-02-06 17:50:34

标签: ios flutter data-stream

好的,所以我知道可以使用广播系统将流制作成不止一次收听流,但这并不是我要在这里做的。

我也正在对此进行编辑,因为我收到的一个答案目前尚无法解决我的问题,因此我们将不胜感激。

出于某种原因,我拥有的代码实际上并未完全删除流,并且如果重新使用它,它会尝试重新收听已被监听并关闭的同一流,但没有一个可行(很明显)。我没有尝试再次收听相同的流,而是尝试创建要收听的NEW流。 (删除并清除原始第一流中的所有信息)。

原始帖子继续如下:

我正在使用DataStream模板向和/或从程序的各个部分流式传输数据,但我不确定如何纠正此问题。我敢肯定这是一个愚蠢的newb错误,但我还没有足够使用DataStreams来理解为什么会发生这种情况。

现在不要误会我的意思,在我的程序的一个循环中运行都很好,完全没有问题。但是,一旦我完成了该程序的一个周期,如果我尝试第二遍,就会收到错误消息:

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

因此,据此我知道我的程序没有创建新的流,而是尝试重用原始流,并且我不确定100%如何停止此功能(或者即使我应该这样做)。 (老实说,我希望完成多个周期的次数从零开始,但是我想在这些错误变成问题之前解决它们。)

编辑:要遵循的最少可复制示例

文件1(main.dart)

import 'package:flutter/cupertino.dart';
import 'dart:async';
import './page2.dart';
import './stream.dart';

void main() => runApp(MyApp());

DataStream stream = DataStream();


class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return CupertinoApp(
      title: 'Splash Test',
      theme: CupertinoThemeData(
        primaryColor: Color.fromARGB(255, 0, 0, 255),
      ),
      home: MyHomePage(title: 'Splash Test Home Page'),
    );
  }
}

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

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  bool textBool = false;
  int counter = 0;

  void changeTest(context) async {
    int counter = 0;
    Timer.periodic(Duration (seconds: 2), (Timer t) {
      counter++;
      stream.dataSink.add(true);
      if (counter >= 3) {
        t.cancel();
        stream.dispose();
        Navigator.pop(context);
      } 
    },);
    Navigator.push(context, CupertinoPageRoute(builder: (context) => Page2(stream: stream)));
  }



  @override
  Widget build(BuildContext context) {

    return CupertinoPageScaffold(
      child: Center(
        child: CupertinoButton(
          child: Text('To Splash'),
          onPressed: () => changeTest(context),
        ),
      ), 
    );
  }
}

文件2(stream.dart)

import 'dart:async';

class DataStream {
  StreamController _streamController;

    StreamSink<bool> get dataSink =>
      _streamController.sink;

  Stream<bool> get dataStream =>
      _streamController.stream;

  DataStream() {
    _streamController = StreamController<bool>();
  }

  dispose() {
    _streamController?.close();
  }

}

文件3(page2.dart)

import 'package:flutter/material.dart';
import 'package:flutter/semantics.dart';

import './main.dart';
import './stream.dart';


class Page2 extends StatefulWidget {

  DataStream stream;
  Page2({this.stream});

  @override 
  State<StatefulWidget> createState() => new PageState();
}

class PageState extends State<Page2> {


bool textChanger = false;
bool firstText = true;

Text myText() {
  if (textChanger) {
    Text text1 = new Text('Text One', 
      style: TextStyle(color: Color.fromARGB(255, 0, 0, 0)));
    return text1;
  } else {
    Text text1 = new Text('Text Two', 
      style: TextStyle(color: Color.fromARGB(255, 0, 0, 0)));
    return text1;
  }
}

void changeText() {
  if (!firstText) {
    if (textChanger) {
      print('Change One');
      setState(() { 
        textChanger = false;      
      });
    } else {
      print('Change Two');
      setState(() {  
        textChanger = true;    
      });
    }  
  } else {
    firstText = false;
  }
}


  @override 
  Widget build(BuildContext context) {
    return new Scaffold(
      body: new Container(
        child: Center(
          child: myText()
        ) 
      ),);
  }

@override
  void initState() {
    super.initState();
    widget.stream.dataStream.listen((onData) {
      changeText();
    });
  }


}

有效地,在此示例中,您可以单击文本,然后转到第二页,这将在提示时正确更改文本,并在完成后返回到原始页面。那将是我程序的一个“循环”。

您会看到该程序随后立即处理了流。

问题在于,如果我第二次单击文本,它仍在尝试收听原始流,而不是创建全新的流并重新开始。

为什么?以及我该如何解决?

4 个答案:

答案 0 :(得分:4)

StreamController默认构造函数创建一个仅允许单个侦听器的流。

StreamController({void onListen(), void onPause(), void onResume(), dynamic onCancel(), bool sync: false })
A controller with a stream that supports only one single subscriber. [...]

如果要使用多重侦听器,请使用broadcast命名构造函数。

factory StreamController.broadcast({void onListen(), void onCancel(), bool sync: false })
A controller where stream can be listened to more than once. [...]

如果您希望流中只有一个订阅者,请记住使用小部件的dispose方法取消订阅。

DataStream stream;
StreamSubscription subscription;

@override
void initState() {
  super.initState();
  subsription = widget.stream.listen((onData) {
    changeText();
  });
}

@override
void dispose() {
  subscription?.cancel();
  super.dispose();
}

请记住,这不是基于流事件重建UI的正确方法。看一下Stream Builder类。

答案 1 :(得分:2)

我要做的是将流移动到StatefulWidget并在“飞溅”水龙头上重新创建它

在实际情况下,将其放到小部件树中的有状态小部件中,所有需要访问的小部件都可以找到它(在您的情况下,甚至比导航器还高)。

import 'package:flutter/cupertino.dart';
import 'dart:async';
import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return CupertinoApp(
      title: 'Splash Test',
      theme: CupertinoThemeData(
        primaryColor: Color.fromARGB(255, 0, 0, 255),
      ),
      home: MyHomePage(title: 'Splash Test Home Page'),
    );
  }
}

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

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  bool textBool = false;
  int counter = 0;

  DataStream stream = DataStream();

  void changeTest(context) async {
    setState(() {
      stream = DataStream();
    });
    int counter = 0;
    Timer.periodic(Duration (seconds: 2), (Timer t) {
      counter++;
      stream.dataSink.add(true);
      if (counter >= 3) {
        t.cancel();
        stream.dispose();
        Navigator.pop(context);
      }
    },);
    Navigator.push(context, CupertinoPageRoute(builder: (context) => Page2(stream: stream)));
  }

  @override
  Widget build(BuildContext context) {
    return CupertinoPageScaffold(
      child: Center(
        child: CupertinoButton(
          child: Text('To Splash'),
          onPressed: () => changeTest(context),
        ),
      ),
    );
  }
}



class DataStream {
  StreamController _streamController;

  StreamSink<bool> get dataSink =>
      _streamController.sink;

  Stream<bool> get dataStream =>
      _streamController.stream;

  DataStream() {
    _streamController = StreamController<bool>();
  }

  dispose() {
    _streamController?.close();
  }

}



class Page2 extends StatefulWidget {

  DataStream stream;
  Page2({this.stream});

  @override
  State<StatefulWidget> createState() => new PageState();
}

class PageState extends State<Page2> {


  bool textChanger = false;
  bool firstText = true;

  Text myText() {
    if (textChanger) {
      Text text1 = new Text('Text One',
          style: TextStyle(color: Color.fromARGB(255, 0, 0, 0)));
      return text1;
    } else {
      Text text1 = new Text('Text Two',
          style: TextStyle(color: Color.fromARGB(255, 0, 0, 0)));
      return text1;
    }
  }

  void changeText() {
    if (!firstText) {
      if (textChanger) {
        print('Change One');
        setState(() {
          textChanger = false;
        });
      } else {
        print('Change Two');
        setState(() {
          textChanger = true;
        });
      }
    } else {
      firstText = false;
    }
  }


  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      body: new Container(
          child: Center(
              child: myText()
          )
      ),);
  }

  @override
  void initState() {
    super.initState();
    widget.stream.dataStream.listen((onData) {
      changeText();
    });
  }


}

答案 2 :(得分:1)

在无法完全理解您的问题的情况下,我只想说一下,每次我自己使用“正常”流并且RxDart阿司匹林在我的世界中:)不知道这是否是您要找的答案,但我想我还是会发布它-您永远不会知道!

答案 3 :(得分:0)

啊哈!我设法弄清楚了。 (感谢大家的帮助,我们非常感谢)。实际上,这是一个非常愚蠢的菜鸟错误,我退后一步就看到了。

您会在main.dart文件中注意到我所在的行

DataStream stream = DataStream();

我将此设置为全局变量。因此,程序的任何部分都可以根据需要访问信息。我有点需要设置它的方式...但是我忘了可以实例化它。

所以我所做的就是将其更改为:

DataStream stream;

然后在我的main.dart文件中,就在按下导航器之前,我添加了一行

stream = new DataStream();
Navigator.push(context, CupertinoPageRoute(builder: (context) => Page2(stream: stream)));

因此,在从程序的较早部分正确处理完流之后,现在我要创建该流的新实例。 s头。应该在一周前解决这个问题。