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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 10:50:00