TabBarView中具有WebSockets流的Streambuilder:错误状态:流已被侦听

时间:2020-01-07 19:16:28

标签: flutter dart websocket

我想听在TabBarView中显示的Widget内部的websocket流:

import 'package:flutter/material.dart';
import 'package:web_socket_channel/io.dart';
import 'package:web_socket_channel/web_socket_channel.dart';

class RabitHouse extends StatefulWidget {
  final channel = IOWebSocketChannel.connect('ws://echo.websocket.org');

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

class _RabitHouseState extends State<RabitHouse> {
  @override
  Widget build(BuildContext context) {
    return DefaultTabController(
        length: 2,
        child: Scaffold(
          appBar: AppBar(
            bottom: TabBar(tabs: [
              Tab(
                icon: Icon(
                  Icons.adb,
                ),
              ),
              Tab(
                  icon: Icon(
                Icons.android,
              )),
            ]),
          ),
          body: TabBarView(children: [
            Rabit(channel: widget.channel),
            Rabit(channel: widget.channel),
          ]),
        ));
  }

  @override
  void dispose() {
    widget.channel.sink.close();
    super.dispose();
  }
}

class Rabit extends StatefulWidget {
  final WebSocketChannel channel;

  const Rabit({Key key, this.channel}) : super(key: key);

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

class _RabitState extends State<Rabit> {
  @override
  Widget build(BuildContext context) {
    return StreamBuilder(
      stream: widget.channel.stream,
      builder: (context, snapshot) => Text('pew pew'),
    );
  }
}

但是,当我更改选项卡时,会出现异常:

════════小部件库捕获到异常══════════════════════════════════ ═════════════════════ 构建Rabit(状态:_RabitState#cd14b)时引发了以下StateError: 错误状态:流已被收听。

我想念什么?

2 个答案:

答案 0 :(得分:1)

默认情况下,Dart中的流是单听器,这意味着如果您尝试多次收听它们,它们将引发错误。如果要在多个地方收听流,则需要将其转换为广播流:

class _RabitHouseState extends State<RabitHouse> {
  Stream broadcastStream;

  @override
  void initState() {
    broadcastStream = widget.channel.stream.asBroadcastStream();
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return DefaultTabController(
        length: 2,
        child: Scaffold(
          appBar: AppBar(
            bottom: TabBar(tabs: [
              Tab(
                icon: Icon(
                  Icons.adb,
                ),
              ),
              Tab(
                  icon: Icon(
                Icons.android,
              )),
            ]),
          ),
          body: TabBarView(children: [
            Rabit(stream: broadcastStream),
            Rabit(stream: broadcastStream),
          ]),
        ));
  }

  @override
  void dispose() {
    widget.channel.sink.close();
    super.dispose();
  }
}
class Rabit extends StatefulWidget {
  final Stream stream;

  const Rabit({Key key, this.stream}) : super(key: key);

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

class _RabitState extends State<Rabit> {
  @override
  Widget build(BuildContext context) {
    return StreamBuilder(
      stream: widget.stream,
      builder: (context, snapshot) => Text('pew pew'),
    );
  }
}

答案 1 :(得分:1)

如果您两次“订阅”同一流,则可以收听。创建两个WebSocket通道,然后以asBroadcastStream()的方式监听它们:

class RabitHouse extends StatefulWidget {
  final channel1 = IOWebSocketChannel.connect('ws://echo.websocket.org');
  final channel2 = IOWebSocketChannel.connect('ws://echo.websocket.org');

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

class _RabitHouseState extends State<RabitHouse> {
  Stream stream1;
  Stream stream2;

  @override
  void initState() {
    stream1  = widget.channel1.stream.asBroadcastStream();
    stream2  = widget.channel2.stream.asBroadcastStream();

    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return DefaultTabController(
        length: 2,
        child: Scaffold(
          appBar: AppBar(
            bottom: TabBar(tabs: [
              Tab(
                icon: Icon(
                  Icons.adb,
                ),
              ),
              Tab(
                  icon: Icon(
                    Icons.android,
                  )
              ),
            ]),
          ),
          body: TabBarView(children: [
            Rabit(stream: stream1),
            Rabit(stream: stream2),
          ]),
        )
    );
  }

  @override
  void dispose() {
    widget.channel1.sink.close();
    widget.channel2.sink.close();
    super.dispose();
  }
}

class Rabit extends StatefulWidget {
  final Stream stream;

  const Rabit({Key key, this.stream}) : super(key: key);

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

class _RabitState extends State<Rabit> {
  @override
  Widget build(BuildContext context) {
    return StreamBuilder(
      stream: widget.stream,
      builder: (context, snapshot) => Text('pew pew'),
    );
  }
}

如果您想了解有关流的更多信息,请访问dart.dev

有两种流

单个订阅流:最常见的流包含 是较大整体的一部分的事件序列。活动需要 以正确的顺序交付,并且没有丢失任何一个。这个 是您在阅读文件或接收网络时获得的信息流 请求。

这样的流只能被收听一次。稍后再听 可能意味着错过了最初的事件,然后剩下的 流没有意义。当您开始收听时,数据将 提取并分块提供。

广播流另一种流是针对个人的 一次可以处理的邮件。这种流可以是 例如,用于浏览器中的鼠标事件。

您可以随时开始收听此类视频流, 您收听时触发的事件。多个听众可以 同时听,之后您可以稍后再听 取消先前的订阅。