You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将Socket转为asBroadcastStream供多个StreamBuilder使用

Flutter Socket单订阅流转广播流多监听解决方案

错误原因

  • 在State属性初始化阶段直接访问widget对象,此时State尚未与Widget实例绑定,属于非法调用,会触发异常
  • 对同一个单订阅Socket流反复调用asBroadcastStream()方法,会抛出BadState错误
  • 在build方法中直接调用stream.listen(),每次页面重绘都会新增重复监听,引发资源泄漏和逻辑异常

正确实现方案

核心修改点

  1. 用late关键字声明广播流变量,在仅执行一次的initState生命周期中完成广播流转换
  2. 所有StreamBuilder统一使用转换后的广播流实例
  3. 自定义监听放在initState中实现,同时保存订阅实例在dispose阶段取消

完整修改代码

class _MyHomePageState extends State<MyHomePage> {
  final inputController = TextEditingController();
  ScrollController scrollcontrol = ScrollController();
  List<String> messageList = [];
  // 声明广播流变量
  late Stream<Uint8List> socketBroadcastStream;
  // 可选:如果需要自定义监听,保存订阅实例用于后续取消
  StreamSubscription? _customSub;

  @override
  void initState() {
    super.initState();
    // 仅调用一次asBroadcastStream完成转换
    socketBroadcastStream = widget.socket.asBroadcastStream();
    // 可选:如果需要额外自定义监听,在这里注册
    _customSub = socketBroadcastStream.listen((value) {
      print(String.fromCharCodes(value));
    });
  }

  @override
  void dispose() {
    // 取消自定义监听
    _customSub?.cancel();
    inputController.dispose();
    widget.socket.destroy();
    widget.socket.close();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: const Color.fromRGBO(236, 236, 236, 1),
      // 多监听实现:AppBar标题监听
      appBar: AppBar(
        title: StreamBuilder<Uint8List>(
          stream: socketBroadcastStream,
          builder: (context, snapshot) {
            return mycustomwidget();
          },
        ),
      ),
      body: SafeArea(
        child: Column(
          children: <Widget>[
            // 消息列表监听统一使用转换后的广播流
            StreamBuilder<Uint8List>(
              stream: socketBroadcastStream,
              builder: (context, snapshot) {
                if (snapshot.hasData == false || String.fromCharCodes(snapshot.data!).contains("joined Connected to the server!") == true) {
                  return Container(height: MediaQuery.of(context).size.height - 300);
                }
                String sent = String.fromCharCodes(snapshot.data!);
                messageList.add(sent);
                return Container(
                  color: const Color.fromRGBO(236, 235, 236, 100),
                  height: MediaQuery.of(context).size.height - 300,
                  child: ListView.builder(
                    controller: scrollcontrol,
                    shrinkWrap: true,
                    itemCount: messageList.length,
                    itemBuilder: (BuildContext context, int index) {
                      String messagerecieved = messageList[index];
                      List<String> messagedisected = messagerecieved.split(" ");
                      if (messagedisected[0] != "h:" && messagerecieved.contains("joined Connected to the server!") == false) {
                        return OtherMessage(message: messageList[index]);
                      } else {
                        return OwnMessage(message: messageList[index]);
                      }
                    },
                  ),
                );
              },
            ),
            Row(
              children: [
                Container(
                  margin: EdgeInsets.only(left: 12),
                  width: 330,
                  decoration: BoxDecoration(color: Color.fromRGBO(214, 214, 214, 100), borderRadius: BorderRadius.circular(14.65)),
                  child: TextFormField(
                    keyboardType: TextInputType.multiline,
                    minLines: 1,
                    maxLines: 5,
                    decoration: const InputDecoration(
                      hintText: 'Enter your message',
                      border: InputBorder.none,
                      focusedBorder: InputBorder.none,
                      enabledBorder: InputBorder.none,
                      errorBorder: InputBorder.none,
                      disabledBorder: InputBorder.none,
                      contentPadding: EdgeInsets.only(left: 15, bottom: 11, top: 11, right: 15),
                    ),
                    controller: inputController,
                  ),
                ),
                IconButton(
                  padding: const EdgeInsets.only(left: 10, right: 12, top: 10),
                  iconSize: 50,
                  onPressed: () {
                    if (messageList.isNotEmpty) {
                      scrollcontrol.animateTo(scrollcontrol.position.maxScrollExtent, duration: const Duration(milliseconds: 100), curve: Curves.easeOut);
                    }
                    String message = inputController.text;
                    widget.socket.write("h: " + message);
                    inputController.clear();
                  },
                  icon: SvgPicture.asset('assets/images/SendButton.svg'),
                )
              ],
            ),
          ],
        ),
      ),
    );
  }
}

内容的提问来源于stack exchange,提问作者Dwikavindra Haryo Radithya

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.23 22:24:06