Files
2026-09-21 17:37:53 +08:00

88 lines
2.6 KiB
Dart
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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<Color> 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<DonutSlice> 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,
],
),
);
}
}