flutter_for_openharmony城市井盖地图app实战+资产健康度实现
资产健康度可视化系统 该系统通过饼图直观展示井盖资产健康状态,划分为优秀(45%)、良好(30%)、一般(15%)、较差(10%)四个等级,采用绿/蓝/橙/红四色区分风险程度。核心功能包括: 数据分层:标准化资产健康等级 可视化呈现:PieChart组件实现占比分布展示,优化中心圆半径(38px)和扇区间距(2px) 布局设计:采用Card+Column嵌套结构,固定饼图高度(260px)保证稳定

1. 这个功能解决什么问题
资产健康度帮助管理者"直观了解"井盖资产状态,核心价值体现在以下维度:
- 健康度分层展示:将井盖资产状态划分为优秀、良好、一般、较差四个等级,实现状态标准化
- 可视化呈现:通过饼图直观展示不同健康等级资产的占比,降低数据理解成本
- 数据模拟能力:支持固定比例的模拟数据配置,便于功能调试和演示
- 视觉体验优化:定制饼图颜色、中心圆尺寸、页面布局,提升用户视觉体验
这个页面是典型的"数据可视化"场景,适合做 PieChart + PieChartSectionData 的最佳实践示例。
2. 相关文件一览
lib/feature_pages.dart(AssetHealthPage):核心页面文件,包含资产健康度页面的UI布局、饼图配置和数据渲染逻辑
3. 饼图数据配置
AssetHealthPage 核心是通过 PieChartData 配置资产健康度分布数据,关键配置项说明:
sections:饼图扇区数组,每个扇区对应一个健康等级value:扇区数值,代表该等级资产的占比/数量color:扇区颜色,通过色彩区分不同健康等级centerSpaceRadius:中心空白圆半径,优化饼图视觉效果sectionsSpace:扇区间距,避免扇区粘连,提升可读性
final pie = PieChartData(
sections: [
PieChartSectionData(value: 45, color: Colors.green, title: '优秀'),
PieChartSectionData(value: 30, color: Colors.blue, title: '良好'),
PieChartSectionData(value: 15, color: Colors.orange, title: '一般'),
PieChartSectionData(value: 10, color: Colors.red, title: '较差'),
],
centerSpaceRadius: 38,
sectionsSpace: 2,
);
上述代码中:
- 优秀等级占比45%,使用绿色(视觉上代表健康、正常)
- 良好等级占比30%,使用蓝色(代表稳定、无风险)
- 一般等级占比15%,使用橙色(轻度警示)
- 较差等级占比10%,使用红色(高风险警示)
- 中心圆半径设为38,既保留饼图完整性,又避免视觉拥挤
- 扇区间距设为2,保证各扇区边界清晰
4. 页面布局
资产健康度页面采用多层嵌套布局,核心设计思路:
- 外层Padding:给页面整体预留12px边距,避免内容贴边
- Card组件:包裹核心内容,增加阴影和圆角,提升视觉层次
- 内层Padding:给Card内部内容预留12px边距,优化内容与容器的间距
- Column纵向布局:分离标题和饼图,保证内容结构清晰
- 固定高度SizedBox:限制饼图高度为260px,避免布局自适应导致的抖动
Padding(
padding: const EdgeInsets.all(12),
child: Card(
child: Padding(
padding: const EdgeInsets.all(12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text('资产健康度分布'),
const SizedBox(height: 12),
SizedBox(height: 260, child: PieChart(pie)),
],
),
),
),
)
布局细节优化说明:
crossAxisAlignment: CrossAxisAlignment.start:让标题左对齐,符合用户阅读习惯SizedBox(height: 12):标题与饼图之间预留12px间距,避免内容紧凑PieChart(pie):将配置好的饼图数据传入组件,完成可视化渲染
5. 完整页面代码(工程真实内容)
完整的资产健康度页面包含页面骨架和核心逻辑,关键组成部分:
StatelessWidget:页面为静态展示型,使用无状态组件提升性能Scaffold:基础页面骨架,包含AppBar和bodyAppBar:设置页面标题,提供导航返回能力- 嵌套布局:延续上述的Padding+Card+Column布局逻辑
class AssetHealthPage extends StatelessWidget {
const AssetHealthPage({super.key});
Widget build(BuildContext context) {
final pie = PieChartData(
sections: [
PieChartSectionData(value: 45, color: Colors.green, title: '优秀'),
PieChartSectionData(value: 30, color: Colors.blue, title: '良好'),
],
centerSpaceRadius: 38,
sectionsSpace: 2,
);
return Scaffold(
appBar: AppBar(title: const Text('资产健康度')),
body: Padding(
padding: const EdgeInsets.all(12),
child: Card(
child: Padding(
padding: const EdgeInsets.all(12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text('资产健康度分布'),
const SizedBox(height: 12),
SizedBox(height: 260, child: PieChart(pie)),
],
),
),
),
),
);
}
}
代码精简说明:
- 上述代码仅保留优秀、良好两个扇区示例,减少代码长度
- 核心布局结构完整保留,确保页面渲染逻辑不变
- 无状态组件特性:页面数据无动态变化,无需维护状态,简化代码
6. 资产健康度数据模型
为实现资产健康度数据的规范化管理,设计完整的数据模型体系,核心设计原则:
- 完整性:覆盖资产健康度全维度信息
- 可扩展性:支持新增字段和关联数据
- 类型安全:使用枚举和自定义类,避免类型错误
- 可追溯性:记录创建时间、更新时间、检查时间等关键时间节点
6.1 核心健康度数据模型
AssetHealthData 是核心数据模型,包含资产健康度的全量信息,关键字段说明:
- 基础信息:id、assetId、assetName、assetType,标识资产唯一身份
- 健康状态:healthLevel(等级)、healthScore(得分)、scoreChange(得分变化)
- 检查记录:lastInspection(上次检查)、nextInspection(下次检查)
- 关联数据:metrics(健康指标)、issues(健康问题)
class AssetHealthData {
final String id;
final String assetId;
final String assetName;
final String assetType;
final HealthLevel healthLevel;
final double healthScore;
final double previousScore;
final double scoreChange;
final DateTime lastInspection;
final DateTime? nextInspection;
final List<HealthMetric> metrics;
字段设计细节:
healthScore:浮点型,支持精确到小数点后多位的健康得分previousScore:记录上一次得分,用于计算变化趋势nextInspection:可空类型,部分资产可能暂无下次检查计划metrics:健康指标列表,支持多维度健康评估
final List<HealthIssue> issues;
final Map<String, dynamic> properties;
final DateTime createdAt;
final DateTime? lastUpdated;
const AssetHealthData({
required this.id,
required this.assetId,
required this.assetName,
required this.assetType,
required this.healthLevel,
required this.healthScore,
required this.previousScore,
required this.scoreChange,
required this.lastInspection,
this.nextInspection,
required this.metrics,
required this.issues,
required this.properties,
required this.createdAt,
this.lastUpdated,
});
}
补充字段说明:
issues:健康问题列表,记录资产存在的具体问题properties:扩展属性,支持存储自定义的资产属性createdAt:数据创建时间,必填项,确保数据可追溯lastUpdated:可空类型,首次创建时暂无更新时间
6.2 健康指标数据模型
HealthMetric 用于描述资产的具体健康指标,设计要点:
- 量化指标:name(名称)、value(数值)、maxValue(最大值)、unit(单位)
- 状态标识:status(指标状态),快速判断指标是否正常
- 时间维度:measuredAt(测量时间),记录指标采集时间
class HealthMetric {
final String name;
final double value;
final double maxValue;
final String unit;
final HealthStatus status;
final String? description;
final DateTime measuredAt;
const HealthMetric({
required this.name,
required this.value,
required this.maxValue,
required this.unit,
required this.status,
this.description,
required this.measuredAt,
});
}
指标设计细节:
maxValue:用于计算指标达标率(value/maxValue)description:可空类型,非必填的指标说明HealthStatus:枚举类型,限定指标状态为正常/警告/严重
6.3 健康问题数据模型
HealthIssue 用于跟踪资产的健康问题,核心设计:
- 问题标识:id、title、description,清晰描述问题
- 严重程度:severity,区分低/中/高/紧急四个等级
- 状态管理:status,记录问题的处理状态(未处理/处理中/已解决/已关闭)
- 时间跟踪:detectedAt(发现时间)、resolvedAt(解决时间)
class HealthIssue {
final String id;
final String title;
final String description;
final IssueSeverity severity;
final IssueStatus status;
final DateTime detectedAt;
final DateTime? resolvedAt;
final String? resolvedBy;
final List<String> attachments;
问题管理细节:
resolvedBy:记录解决人,便于责任追溯attachments:附件列表,支持存储问题相关的图片/文档路径metadata:扩展元数据,支持自定义问题属性
final Map<String, dynamic> metadata;
const HealthIssue({
required this.id,
required this.title,
required this.description,
required this.severity,
required this.status,
required this.detectedAt,
this.resolvedAt,
this.resolvedBy,
this.attachments = const [],
this.metadata = const {},
});
}
默认值设计:
attachments:默认空数组,避免空指针metadata:默认空Map,简化初始化
6.4 枚举类型定义
枚举类型用于限定字段取值范围,提升代码类型安全,设计说明:
HealthLevel:资产健康等级,对应饼图的四个扇区HealthStatus:指标状态,用于单个健康指标的状态判断IssueSeverity:问题严重程度,用于问题优先级排序IssueStatus:问题处理状态,用于问题生命周期管理
enum HealthLevel {
excellent,
good,
fair,
poor,
}
enum HealthStatus {
normal,
warning,
critical,
}
枚举命名规范:
- 使用小写+下划线命名法,符合Dart枚举命名规范
- 命名语义化,excellent(优秀)、fair(一般)、critical(严重)等直观易懂
enum IssueSeverity {
low,
medium,
high,
critical,
}
enum IssueStatus {
open,
inProgress,
resolved,
closed,
}
状态流转设计:
IssueStatus遵循问题处理流程:open → inProgress → resolved → closed- 覆盖问题从发现到关闭的全生命周期
7. 资产健康度状态管理
使用Provider实现资产健康度数据的状态管理,核心优势:
- 响应式更新:数据变化时自动刷新UI
- 数据共享:跨组件共享健康度数据
- 统一管理:集中处理数据加载、过滤、刷新逻辑
- 状态隔离:将数据逻辑与UI逻辑分离,提升代码可维护性
7.1 状态管理基础类
AssetHealthProvider 继承 ChangeNotifier,核心状态字段说明:
_healthData:资产健康度数据列表,存储全量数据_healthDistribution:健康等级分布统计,用于饼图渲染_loading:加载状态,控制加载中UI展示_error:错误信息,记录数据加载失败原因_selectedFilter:选中的过滤条件,用于数据筛选
class AssetHealthProvider extends ChangeNotifier {
List<AssetHealthData> _healthData = [];
Map<HealthLevel, int> _healthDistribution = {};
bool _loading = false;
String? _error;
DateTime? _lastRefreshTime;
HealthLevel? _selectedFilter;
List<AssetHealthData> get healthData => _healthData;
Map<HealthLevel, int> get healthDistribution => _healthDistribution;
bool get loading => _loading;
String? get error => _error;
状态访问设计:
- 私有字段(下划线开头):防止外部直接修改状态
- 公开getter方法:提供只读访问,保证状态修改的可控性
- 关键状态:
_lastRefreshTime记录最后刷新时间,用于数据时效性判断
DateTime? get lastRefreshTime => _lastRefreshTime;
HealthLevel? get selectedFilter => _selectedFilter;
Future<void> loadHealthData() async {
_loading = true;
_error = null;
notifyListeners();
try {
await Future.delayed(const Duration(seconds: 1));
_healthData = _generateMockHealthData();
_calculateHealthDistribution();
_lastRefreshTime = DateTime.now();
_loading = false;
notifyListeners();
} catch (e) {
_error = e.toString();
_loading = false;
notifyListeners();
}
}
数据加载逻辑:
- 加载前:设置loading为true,清空错误信息,通知UI刷新
- 模拟延迟:使用Future.delayed模拟网络请求延迟(1秒)
- 数据生成:调用_generateMockHealthData生成模拟数据
- 分布计算:调用_calculateHealthDistribution统计健康等级分布
- 异常处理:捕获所有异常,记录错误信息,恢复loading状态
7.2 状态操作方法
状态管理类提供丰富的操作方法,核心功能:
- 过滤:setFilter设置健康等级过滤条件
- 刷新:refreshData重新加载数据
- 数据筛选:filteredHealthData返回过滤后的数据集
- 分布计算:_calculateHealthDistribution统计各等级资产数量
void setFilter(HealthLevel? level) {
_selectedFilter = level;
notifyListeners();
}
Future<void> refreshData() async {
await loadHealthData();
}
List<AssetHealthData> get filteredHealthData {
if (_selectedFilter == null) return _healthData;
return _healthData.where((data) => data.healthLevel == _selectedFilter).toList();
}
void _calculateHealthDistribution() {
_healthDistribution.clear();
for (final level in HealthLevel.values) {
_healthDistribution[level] = _healthData.where((data) => data.healthLevel == level).length;
}
}
方法设计细节:
setFilter:修改过滤条件后立即通知UI刷新refreshData:复用loadHealthData方法,简化代码filteredHealthData:使用getter方法,实现过滤数据的实时计算_calculateHealthDistribution:遍历所有健康等级,统计对应资产数量
7.3 模拟数据生成
_generateMockHealthData 用于生成模拟数据,设计要点:
- 随机数种子:固定种子(42)保证生成数据的一致性
- 资产类型多样化:包含井盖、管道、阀门等多种资产类型
- 健康得分区间:60-100分,符合健康度评估的常规区间
- 得分变化:previousScore在当前得分±10分范围内,模拟得分波动
List<AssetHealthData> _generateMockHealthData() {
final assetTypes = ['井盖', '管道', '阀门', '泵站', '检查井'];
final rng = Random(42);
return List.generate(50, (index) {
final assetType = assetTypes[index % assetTypes.length];
final healthScore = 60.0 + rng.nextDouble() * 40.0;
final previousScore = healthScore + (rng.nextInt(21) - 10);
final healthLevel = _getHealthLevel(healthScore);
return AssetHealthData(
id: 'HEALTH_${index.toString().padLeft(3, '0')}',
assetId: 'ASSET_${index.toString().padLeft(3, '0')}',
assetName: '${assetType}${index + 1}',
assetType: assetType,
healthLevel: healthLevel,
healthScore: healthScore,
模拟数据细节:
- 资产ID格式化:补零到3位,保证ID格式统一(如HEALTH_001)
- 资产名称:资产类型+序号,便于识别(如井盖1、管道2)
- 得分限制:previousScore通过clamp方法限制在0-100分范围内
- 健康等级:通过_getHealthLevel方法根据得分自动判定
previousScore: previousScore.clamp(0.0, 100.0),
scoreChange: healthScore - previousScore,
lastInspection: DateTime.now().subtract(Duration(days: rng.nextInt(30))),
nextInspection: DateTime.now().add(Duration(days: 30 + rng.nextInt(30))),
metrics: _generateMockMetrics(rng),
issues: _generateMockIssues(healthLevel, rng),
properties: {
'location': '位置${index + 1}',
'installationDate': DateTime(2015 + rng.nextInt(8)).toIso8601String(),
'manufacturer': ['厂家A', '厂家B', '厂家C'][index % 3],
},
createdAt: DateTime.now().subtract(Duration(days: rng.nextInt(365))),
lastUpdated: DateTime.now(),
);
});
}
扩展数据模拟:
- 检查时间:lastInspection随机生成30天内的日期,nextInspection生成30-60天后的日期
- 扩展属性:包含位置、安装日期、生产厂家等资产基础信息
- 创建时间:随机生成一年内的日期,模拟历史数据
- 关联数据:调用_generateMockMetrics和_generateMockIssues生成指标和问题数据
7.4 辅助数据生成
7.4.1 健康指标模拟
_generateMockMetrics 生成模拟健康指标,设计说明:
- 指标类型固定:结构完整性、密封性能、承载能力三个核心指标
- 数值区间:根据指标特性设置不同的数值范围
- 状态随机:部分指标状态随机为正常/警告,模拟真实场景
List<HealthMetric> _generateMockMetrics(Random rng) {
return [
HealthMetric(
name: '结构完整性',
value: 70 + rng.nextDouble() * 30,
maxValue: 100,
unit: '%',
status: rng.nextBool() ? HealthStatus.normal : HealthStatus.warning,
measuredAt: DateTime.now(),
),
HealthMetric(
name: '密封性能',
value: 60 + rng.nextDouble() * 40,
maxValue: 100,
unit: '%',
status: rng.nextBool() ? HealthStatus.normal : HealthStatus.warning,
measuredAt: DateTime.now(),
),
指标数值设计:
- 结构完整性:70-100%,代表资产结构基本稳定
- 密封性能:60-100%,波动范围更大,模拟密封性能的不稳定性
- 承载能力:80-100kg,保持较高水平,符合井盖基础安全要求
HealthMetric(
name: '承载能力',
value: 80 + rng.nextDouble() * 20,
maxValue: 100,
unit: 'kg',
status: HealthStatus.normal,
measuredAt: DateTime.now(),
),
];
}
7.4.2 健康问题模拟
_generateMockIssues 根据健康等级生成模拟问题,设计逻辑:
- 优秀等级:无问题,返回空列表
- 良好等级:1个问题
- 一般等级:2个问题
- 较差等级:3个问题
- 问题严重程度:随机分配,模拟真实场景
List<HealthIssue> _generateMockIssues(HealthLevel level, Random rng) {
if (level == HealthLevel.excellent) return [];
final issueCount = level == HealthLevel.poor ? 3 : level == HealthLevel.fair ? 2 : 1;
return List.generate(issueCount, (index) {
return HealthIssue(
id: 'ISSUE_${DateTime.now().millisecondsSinceEpoch}_$index',
title: '健康问题 ${index + 1}',
description: '检测到的健康问题描述',
severity: IssueSeverity.values[index % IssueSeverity.values.length],
status: IssueStatus.open,
detectedAt: DateTime.now().subtract(Duration(hours: rng.nextInt(24))),
问题数据细节:
- 问题ID:时间戳+索引,保证唯一性
- 发现时间:24小时内随机时间,模拟近期发现的问题
- 附件:默认包含1张图片附件,模拟现场拍照记录
- 元数据:包含优先级信息,便于问题排序处理
attachments: ['attachment_${index}_1.jpg'],
metadata: {
'priority': ['high', 'medium', 'low'][index % 3],
},
);
});
}
7.4.3 健康等级判定
_getHealthLevel 根据得分自动判定健康等级,判定规则:
- 90分及以上:优秀(excellent)
- 75-89分:良好(good)
- 60-74分:一般(fair)
- 60分以下:较差(poor)
HealthLevel _getHealthLevel(double score) {
if (score >= 90) return HealthLevel.excellent;
if (score >= 75) return HealthLevel.good;
if (score >= 60) return HealthLevel.fair;
return HealthLevel.poor;
}
}
判定规则设计:
- 区间划分合理,符合常规的健康度评估标准
- 边界值包含(>=),避免区间重叠或遗漏
- 按得分从高到低判断,逻辑清晰易读
8. 高级资产健康度组件
高级资产健康度组件在基础页面的基础上扩展了更多功能,核心增强点:
- 状态管理集成:结合Provider实现数据的响应式加载和展示
- 过滤功能:支持按健康等级筛选资产
- 刷新功能:提供手动刷新数据的入口
- 加载状态:展示加载中、空数据等状态
- 数据概览:新增健康度统计卡片,展示核心指标
8.1 组件基础结构
AdvancedAssetHealthWidget 为有状态组件,核心生命周期逻辑:
initState:组件初始化时加载数据build:根据Provider状态渲染不同UI- 状态监听:使用Consumer监听Provider数据变化
class AdvancedAssetHealthWidget extends StatefulWidget {
const AdvancedAssetHealthWidget({super.key});
State<AdvancedAssetHealthWidget> createState() => _AdvancedAssetHealthWidgetState();
}
class _AdvancedAssetHealthWidgetState extends State<AdvancedAssetHealthWidget> {
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) {
final provider = Provider.of<AssetHealthProvider>(context, listen: false);
provider.loadHealthData();
});
}
初始化逻辑:
addPostFrameCallback:确保在组件构建完成后再加载数据listen: false:初始化时不监听状态变化,避免不必要的重建- 自动加载:组件初始化后自动加载数据,提升用户体验
Widget build(BuildContext context) {
return Consumer<AssetHealthProvider>(
builder: (context, provider, child) {
return Scaffold(
appBar: AppBar(
title: const Text('资产健康度'),
actions: [
IconButton(
onPressed: () => _showFilterDialog(context, provider),
icon: const Icon(Icons.filter_list),
),
IconButton(
onPressed: provider.refreshData,
icon: const Icon(Icons.refresh),
),
],
),
AppBar设计:
- 过滤按钮:打开过滤弹窗,选择健康等级
- 刷新按钮:触发数据刷新,更新资产健康度数据
- 图标选择:使用系统内置图标,保证视觉一致性
body: provider.loading
? const Center(child: CircularProgressIndicator())
: provider.healthData.isEmpty
? const Center(child: Text('暂无健康度数据'))
: _buildContent(context, provider),
);
},
);
}
状态UI处理:
- 加载中:展示CircularProgressIndicator,提示用户数据加载中
- 空数据:展示友好的空数据提示,提升用户体验
- 正常状态:调用_buildContent构建核心内容
8.2 核心内容构建
_buildContent 构建组件的核心内容,布局结构:
- RefreshIndicator:支持下拉刷新,提升交互体验
- Column纵向布局:从上到下依次展示概览卡片、过滤栏、饼图、资产列表
- Expanded:资产列表使用Expanded,占满剩余空间
Widget _buildContent(BuildContext context, AssetHealthProvider provider) {
return RefreshIndicator(
onRefresh: provider.refreshData,
child: Column(
children: [
_buildSummaryCard(context, provider),
const SizedBox(height: 8),
_buildFilterBar(context, provider),
const SizedBox(height: 8),
_buildHealthChart(context, provider),
const SizedBox(height: 8),
Expanded(
child: _buildAssetList(context, provider),
),
],
),
);
}
布局间距设计:
- 各模块之间设置8px间距,保证页面呼吸感
- RefreshIndicator包裹整个内容,支持下拉刷新所有数据
- Expanded保证资产列表在屏幕剩余空间内滚动
8.3 概览卡片组件
_buildSummaryCard 展示健康度核心统计信息,设计要点:
- 关键指标:总资产数、优秀资产数、平均得分
- 卡片样式:使用Card组件,增加圆角和阴影
- 布局:Row+Expanded实现三列等宽布局
- 样式:使用主题文本样式,保证视觉一致性
Widget _buildSummaryCard(BuildContext context, AssetHealthProvider provider) {
final totalAssets = provider.healthData.length;
final excellentAssets = provider.healthDistribution[HealthLevel.excellent] ?? 0;
final averageScore = provider.healthData.fold(0.0, (sum, data) => sum + data.healthScore) / totalAssets;
return Card(
margin: const EdgeInsets.all(16),
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
children: [
Text(
'健康度概览',
style: Theme.of(context).textTheme.titleLarge?.copyWith(
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 12),
统计计算逻辑:
fold方法计算所有资产健康得分的总和,再除以总数得到平均分- 空值处理:优秀资产数使用?? 0,避免空指针
- 文本样式:使用主题的titleLarge样式,加粗显示标题
Row(
children: [
Expanded(
child: _buildSummaryItem(
context,
'总资产数',
'$totalAssets',
Colors.blue,
),
),
const SizedBox(width: 12),
Expanded(
child: _buildSummaryItem(
context,
'优秀资产',
'$excellentAssets',
Colors.green,
),
),
const SizedBox(width: 12),
Expanded(
child: _buildSummaryItem(
context,
'平均得分',
'${averageScore.toStringAsFixed(1)}',
Colors.orange,
),
),
],
),
],
),
),
);
}
概览项设计:
- 三列等宽布局,保证视觉平衡
- 不同指标使用不同颜色,增强区分度
- 平均分保留1位小数,保证数据精度和可读性
8.4 健康度饼图组件
_buildHealthChart 重构饼图组件,增强数据展示能力,核心优化:
- 动态数据:基于Provider的健康分布数据生成饼图
- 扇区标题:包含等级名称和数量,提升信息密度
- 图例展示:新增Wrap布局的图例,便于查看各等级数量
- 颜色匹配:使用与健康等级对应的颜色,保证视觉一致性
Widget _buildHealthChart(BuildContext context, AssetHealthProvider provider) {
final distribution = provider.healthDistribution;
final sections = HealthLevel.values.map((level) {
final count = distribution[level] ?? 0;
final color = _getHealthLevelColor(level);
return PieChartSectionData(
value: count.toDouble(),
color: color,
title: '${_getHealthLevelDisplayName(level)}\n$count',
titleStyle: const TextStyle(
color: Colors.white,
fontSize: 12,
fontWeight: FontWeight.bold,
),
);
}).toList();
饼图配置优化:
- 中心圆半径增大到60,提升视觉效果
- 扇区标题样式统一:白色、12号字体、加粗
- 动态生成扇区:遍历所有健康等级,保证数据完整
final pieData = PieChartData(
sections: sections,
centerSpaceRadius: 60,
sectionsSpace: 2,
);
return Card(
margin: const EdgeInsets.symmetric(horizontal: 16),
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'健康度分布',
style: Theme.of(context).textTheme.titleMedium?.copyWith(
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 12),
饼图容器设计:
- 固定高度200px,保证饼图展示效果
- Card包裹,与页面其他模块视觉风格统一
- 内边距16px,保证内容与容器的间距
SizedBox(
height: 200,
child: PieChart(pieData),
),
const SizedBox(height: 12),
Wrap(
spacing: 16,
runSpacing: 8,
children: HealthLevel.values.map((level) {
final count = distribution[level] ?? 0;
final color = _getHealthLevelColor(level);
return Row(
mainAxisSize: MainAxisSize.min,
children: [
Container(
width: 12,
height: 12,
decoration: BoxDecoration(
color: color,
shape: BoxShape.circle,
),
),
const SizedBox(width: 6),
Text(
'${_getHealthLevelDisplayName(level)}: $count',
style: TextStyle(
color: Colors.grey.shade600,
fontSize: 12,
),
),
],
);
}).toList(),
),
],
),
),
);
}
}
图例设计:
- Wrap布局:自动换行,适配不同屏幕宽度
- 圆形色块:12px直径的圆形,与饼图扇区颜色一致
- 文字说明:等级名称+数量,使用灰色600,12号字体,保证可读性
- 间距设置:水平间距16px,垂直间距8px,优化布局
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
更多推荐



所有评论(0)