Flutter DataStream не закрывается и перестраивается должным образом. [Плохое состояние: поток уже прослушан.] - PullRequest
5 голосов
/ 06 февраля 2020

Хорошо, я знаю, что поток может быть создан для прослушивания потока более одного раза с использованием системы вещания, но это определенно НЕ то, что я пытаюсь сделать здесь.

I Я также редактирую это, поскольку один полученный мной ответ в настоящее время не может решить мою проблему, поэтому любая помощь будет принята с благодарностью.

По какой-то причине мой код не удаляет поток в оно полностью, и, если его использовать повторно, оно пытается повторно прослушать тот же поток, который уже прослушан и закрыт, но ни один из них не работает (очевидно). Вместо того, чтобы пытаться снова прослушивать тот же поток, я пытаюсь создать НОВЫЙ поток для прослушивания. (Удаление и очистка всей информации из исходного первого потока).

Исходное сообщение продолжается ниже:

Я использую шаблон DataStream для потоковой передачи данных в и / или из различных частей моего программа, и я не совсем уверен, как это исправить. Я уверен, что это глупая ошибка новичка, но я не использовал DataStreams достаточно, чтобы понять, почему это происходит.

Теперь не поймите меня неправильно, прохождение одного цикла моей программы прекрасно работает нет проблем вообще. Однако, как только я завершил один цикл в программе, если я попытаюсь выполнить go через второй раз, я получу ошибку:

Плохое состояние: поток уже прослушан.

Итак, из этого я знаю, что моя программа не создает новый поток, а вместо этого пытается повторно использовать исходный поток, и я не уверен на 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();
    });
  }


}

По сути, в этом примере вы можете нажать на текст и перейти на вторую страницу, которая будет корректно изменять текст, когда будет сказано, и вернется на исходную страницу после завершения. Это был бы один «цикл» моей программы.

И вы можете видеть, что эта программа сразу же удаляет поток.

Проблема в том, что если я щелкну текст во второй раз , он все еще пытается прослушать оригинальный поток, а не создавать новый поток и запускать fre sh.

Почему? И как мне это исправить?

Ответы [ 4 ]

4 голосов
/ 06 февраля 2020

Конструктор 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();
}

Просто имейте в виду, что это неправильный способ восстановления вашего пользовательского интерфейса на основе потока Мероприятия. Взгляните на класс Stream Builder .

2 голосов
/ 18 февраля 2020

Что я хотел бы сделать, это переместить поток на StatefulWidget и воссоздать его при нажатии «на Spla sh»

В реальном сценарии поместите его в виджет с сохранением состояния в дереве виджетов, где все виджеты, которые Нужен доступ, удастся его найти (в вашем случае даже выше, чем навигатор).

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


}
1 голос
/ 18 февраля 2020

Не имея возможности решить asp вашу проблему в полном объеме, я просто хотел бы заявить, что у меня часто бывают головные боли каждый раз, когда я сам использую «нормальные» потоки и RxDart был для меня как аспирин в мире потоков :) Не уверен, что это тот ответ, который вы ищете, но я все равно решил опубликовать его - вы никогда не узнаете!

0 голосов
/ 20 февраля 2020

Aha! Мне удалось это выяснить. (Спасибо всем за помощь, это очень ценится). Это была действительно глупая ошибка нуба, что, как только я сделал шаг назад, я увидел это.

Вы заметите в файле main.dart, что у меня есть строка

DataStream stream = DataStream();

И я установил это как глобальную переменную. Таким образом, любая часть программы может получить доступ к информации по мере необходимости. Вот каким способом мне нужно его настроить ... Но я забыл, что его можно создать.

Итак, я сделал это:

DataStream stream;

А потом В моем файле main.dart незадолго до нажатия навигатора я добавляю строку

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

Итак, теперь я создаю новый экземпляр потока после того, как он был должным образом удален из предыдущих битов программа. хлопает головой . Стоит ли это выяснить за неделю go.

...