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

使用Flutter Provider与fl_chart时遇setState/build期间调用错误求助

问题原因分析
  1. 构建过程中触发状态更新:showingSections()在组件build()方法中被调用,而你在这个方法里调用了Provider.of<RecProvider>(context, listen: false).getFilteredList(),如果该方法内部调用了notifyListeners()(Provider更新状态的标准方式),就会在组件构建过程中触发状态更新,导致setState() or markNeedsBuild() called during build错误——Flutter禁止在组件构建期间修改状态,否则会引发重复构建的循环冲突。
  2. 数组越界风险:你用List.generate(24, (i) {...})循环,但gouvr列表只有6个元素,当i≥6时访问gouvr[i]会直接抛出数组越界异常。

解决方案

1. 重构Provider逻辑,提前计算所有省份的统计数据

不要在build阶段反复调用getFilteredList,而是在Provider内部一次性计算好所有省份的统计结果,存储为一个Map,build时直接读取即可。

修改RecProvider:

class RecProvider extends ChangeNotifier {
  List<Reclamation> reclamationList = [];
  Map<String, int> provinceCountMap = {}; // 存储每个省份的诉求数量

  // 获取数据后自动计算统计
  Future<void> getPosts() async {
    // 保留原有的数据获取逻辑
    reclamationList = await fetchReclamationData();
    // 计算每个省份的诉求数量
    _calculateProvinceCounts();
    notifyListeners();
  }

  void _calculateProvinceCounts() {
    provinceCountMap.clear();
    // 遍历所有诉求统计省份数量
    for (var rec in reclamationList) {
      String province = rec.province; // 假设诉求模型中有province字段对应gouvr元素
      provinceCountMap[province] = (provinceCountMap[province] ?? 0) + 1;
    }
    // 确保gouvr中的省份都有记录,无数据的设为0
    for (var province in gouvr) {
      provinceCountMap.putIfAbsent(province, () => 0);
    }
  }
}

2. 修改组件代码,避免在build中修改状态

移除showingSections()里的getFilteredList调用,直接从Provider读取提前计算好的统计数据:

class PieChart2State extends State<PieChartSample2> {
  @override
  void initState() {
    super.initState();
    final provider = Provider.of<RecProvider>(context, listen: false);
    if (provider.reclamationList.isEmpty) {
      provider.getPosts();
    }
  }

  int touchedIndex = -1;

  @override
  Widget build(BuildContext context) {
    final provider = Provider.of<RecProvider>(context);
    return AspectRatio(
      aspectRatio: 1.3,
      child: Row(
        children: <Widget>[
          const SizedBox(height: 18),
          Expanded(
            child: AspectRatio(
              aspectRatio: 1,
              child: PieChart(
                PieChartData(
                  pieTouchData: PieTouchData(
                    touchCallback: (FlTouchEvent event, pieTouchResponse) {
                      setState(() {
                        if (!event.isInterestedForInteractions ||
                            pieTouchResponse == null ||
                            pieTouchResponse.touchedSection == null) {
                          touchedIndex = -1;
                          return;
                        }
                        touchedIndex = pieTouchResponse.touchedSection!.touchedSectionIndex;
                      });
                    },
                  ),
                  borderData: FlBorderData(show: false),
                  sectionsSpace: 0,
                  centerSpaceRadius: 40,
                  sections: showingSections(provider),
                ),
              ),
            ),
          ),
        ],
      ),
    );
  }

  List<PieChartSectionData> showingSections(RecProvider provider) {
    final random = Random();
    final total = provider.reclamationList.length;
    // 遍历gouvr生成图表区域,避免数组越界
    return gouvr.asMap().entries.map((entry) {
      final i = entry.key;
      final province = entry.value;
      final count = provider.provinceCountMap[province] ?? 0;
      if (count == 0) return null; // 无数据的省份不显示

      final isTouched = i == touchedIndex;
      final fontSize = isTouched ? 25.0 : 16.0;
      final radius = isTouched ? 60.0 : 50.0;
      const shadows = [Shadow(color: Colors.black, blurRadius: 2)];

      return PieChartSectionData(
        color: Color.fromARGB(
          255,
          random.nextInt(256),
          random.nextInt(256),
          random.nextInt(256),
        ),
        value: total == 0 ? 0 : (100 * count) / total,
        title: province,
        radius: radius,
        titleStyle: TextStyle(
          fontSize: fontSize,
          fontWeight: FontWeight.bold,
          color: Colors.black,
          shadows: shadows,
        ),
      );
    }).whereType<PieChartSectionData>().toList();
  }
}

3. 关键改进点

  • 将统计逻辑从build阶段移到Provider内部,在数据加载完成后一次性计算,避免重复计算和状态更新冲突。
  • 使用gouvr.asMap()循环,彻底解决数组越界问题。
  • 在build中仅读取Provider状态,不修改状态,符合Flutter状态管理规范。
  • 处理了总诉求数为0的情况,避免除以0的异常。

内容的提问来源于stack exchange,提问作者Eya Rais

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 13:18:04