import 'package:fl_chart/fl_chart.dart'; import 'package:flutter/material.dart'; /// 一个扇区 class DonutSlice { final double value; final Color color; final String label; const DonutSlice({required this.value, required this.color, this.label = ''}); } /// 图表配色:以主题橙棕/金色为主,穿插少量对比色便于区分分类 const List kChartPalette = [ Color(0xFFC8956E), // 橙棕(主色) Color(0xFFFFB74D), // 亮橙 Color(0xFFE57373), // 红 Color(0xFF81C784), // 绿 Color(0xFF64B5F6), // 蓝 Color(0xFFBA68C8), // 紫 Color(0xFF4DB6AC), // 青 Color(0xFFF06292), // 粉 Color(0xFFA1887F), // 棕灰 Color(0xFF90A4AE), // 蓝灰 ]; Color chartColorAt(int index) => kChartPalette[index % kChartPalette.length]; /// 通用环形图(首页预算环 + 统计页分类饼图共用) /// /// 内部处理了 fl_chart 的空数据问题:`PieChartSectionData.value` 必须 > 0, /// 全 0 / NaN 会算出 NaN 角度导致渲染异常甚至崩溃,所以这里统一过滤并兜底成灰环。 /// fl_chart 类型只出现在本文件,升级图表库时只需改这一处。 class DonutChart extends StatelessWidget { const DonutChart({ super.key, required this.slices, this.size = 150, this.centerSpaceRadius = 46, this.sectionsSpace = 2, this.center, }); final List slices; final double size; final double centerSpaceRadius; final double sectionsSpace; /// 圆心内容,例如 Text('68%') final Widget? center; @override Widget build(BuildContext context) { final valid = slices.where((s) => s.value.isFinite && s.value > 0).toList(); // 无有效数据时退化为单段灰环,避免空数据渲染问题 final effective = valid.isEmpty ? const [DonutSlice(value: 1, color: Color(0xFFE0C9A6))] : valid; final ringRadius = (size / 2 - centerSpaceRadius).clamp(8.0, double.infinity); return SizedBox( width: size, height: size, child: Stack( alignment: Alignment.center, children: [ PieChart( PieChartData( sections: effective .map((s) => PieChartSectionData( value: s.value, color: s.color, radius: ringRadius, showTitle: false, )) .toList(), centerSpaceRadius: centerSpaceRadius, sectionsSpace: sectionsSpace, ), ), ?center, ], ), ); } }