如何将Socket转为asBroadcastStream供多个StreamBuilder使用
Flutter Socket单订阅流转广播流多监听解决方案
错误原因
- 在State属性初始化阶段直接访问
widget对象,此时State尚未与Widget实例绑定,属于非法调用,会触发异常 - 对同一个单订阅Socket流反复调用
asBroadcastStream()方法,会抛出BadState错误 - 在
build方法中直接调用stream.listen(),每次页面重绘都会新增重复监听,引发资源泄漏和逻辑异常
正确实现方案
核心修改点
- 用
late关键字声明广播流变量,在仅执行一次的initState生命周期中完成广播流转换 - 所有
StreamBuilder统一使用转换后的广播流实例 - 自定义监听放在
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
相关产品推荐
相关产品推荐

