Flutter setState()在build阶段调用引发Widgets Library异常求助
解决Flutter中StreamBuilder构建时调用setState的异常
错误原因
异常的根源是在StreamBuilder的builder回调内直接调用setState()。builder方法属于Flutter的UI构建核心流程,当该方法执行时,框架正处于组件构建阶段,此时调用setState会触发新的重建请求,导致框架状态冲突,进而抛出异常。
你的代码中,在StreamBuilder的builder里处理WebSocket消息时,直接调用了包含setState的updateChartData方法,这正是触发问题的原因。
解决方案
将WebSocket消息的处理逻辑从StreamBuilder的builder中移出,改为在initState中直接监听Stream,数据更新后再通过setState刷新UI,StreamBuilder仅负责根据最新数据渲染图表(或直接移除StreamBuilder,因为已单独处理流监听)。
修改后的完整代码:
import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:syncfusion_flutter_charts/charts.dart'; import 'package:intl/intl.dart'; import 'package:web_socket_channel/io.dart'; const String apiKey = "apikey"; class Chart extends StatefulWidget { const Chart({Key? key}) : super(key: key); @override _ChartState createState() => _ChartState(); } class _ChartState extends State<Chart> { late final IOWebSocketChannel channel; List<DataPoint> chartData = []; @override void initState() { super.initState(); channel = IOWebSocketChannel.connect( 'wss://streamer.cryptocompare.com/v2?api_key=$apiKey', ); subscribeToStream(); // 直接监听WebSocket流,处理数据更新 channel.stream.listen((message) { print("Received from Cryptocompare: $message"); final decodedMessage = jsonDecode(message.toString()); final messageType = decodedMessage['TYPE']; if (messageType == '2') { if (decodedMessage.containsKey('PRICE')) { num price = decodedMessage['PRICE']; updateChartData(price); print('Price: $price'); } } }); } void subscribeToStream() { final subRequest = { "action": "SubAdd", "subs": ["2~Coinbase~BTC~USD"] }; final subRequestJson = jsonEncode(subRequest); channel.sink.add(subRequestJson); } @override void dispose() { channel.sink.close(); super.dispose(); } void updateChartData(num price) { setState(() { chartData.add(DataPoint(DateTime.now().toString(), price.toDouble())); }); } @override Widget build(BuildContext context) { return MaterialApp( home: Scaffold( body: SfCartesianChart( primaryXAxis: CategoryAxis(), primaryYAxis: NumericAxis( numberFormat: NumberFormat('#.5'), // 保留一位小数 ), series: <LineSeries<DataPoint, String>>[ LineSeries<DataPoint, String>( dataSource: chartData, xValueMapper: (DataPoint data, _) => data.x, yValueMapper: (DataPoint data, _) => data.y, ), ], ), ), ); } } class DataPoint { final String x; final double y; DataPoint(this.x, this.y); }
备选方案(保留StreamBuilder场景)
如果需要保留StreamBuilder处理连接状态、加载状态等逻辑,可以通过addPostFrameCallback延迟执行setState,修改updateChartData方法:
void updateChartData(num price) { WidgetsBinding.instance.addPostFrameCallback((_) { setState(() { chartData.add(DataPoint(DateTime.now().toString(), price.toDouble())); }); }); }
这种方式会等待当前UI构建完成后,再执行setState,避免构建过程中的状态冲突。
内容的提问来源于stack exchange,提问作者nornaleyda rosli
相关产品推荐
相关产品推荐

