88 lines
2.6 KiB
Dart
88 lines
2.6 KiB
Dart
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,
|
||
],
|
||
),
|
||
);
|
||
}
|
||
}
|