Implemented an initial version of AqiDistributionChart.

This commit is contained in:
Faris Armoush
2025-06-01 11:50:34 +03:00
parent 7c55e8bbf9
commit 5940e52826
3 changed files with 266 additions and 8 deletions

View File

@ -0,0 +1,31 @@
import 'package:flutter/material.dart';
import 'package:syncrow_web/utils/color_manager.dart';
class AirQualityDataModel {
const AirQualityDataModel({
required this.date,
this.aqi,
this.pm25,
this.pm10,
this.hcho,
this.tvoc,
this.co2,
});
final DateTime date;
final double? aqi;
final double? pm25;
final double? pm10;
final double? hcho;
final double? tvoc;
final double? co2;
static const Map<String, Color> metricColors = {
'aqi': ColorsManager.goodGreen,
'pm25': ColorsManager.moderateYellow,
'pm10': ColorsManager.poorOrange,
'hcho': ColorsManager.unhealthyRed,
'tvoc': ColorsManager.severePink,
'co2': ColorsManager.hazardousPurple,
};
}

View File

@ -1,10 +1,205 @@
import 'package:fl_chart/fl_chart.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:syncrow_web/pages/analytics/models/air_quality_data_model.dart';
import 'package:syncrow_web/pages/analytics/modules/energy_management/helpers/energy_management_charts_helper.dart';
import 'package:syncrow_web/utils/color_manager.dart';
import 'package:syncrow_web/utils/extension/build_context_x.dart';
class AqiDistributionChart extends StatelessWidget { class AqiDistributionChart extends StatelessWidget {
const AqiDistributionChart({super.key}); const AqiDistributionChart({super.key, required this.chartData});
final List<AirQualityDataModel> chartData;
static const _rodStackItemsSpacing = 4;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return const Placeholder(); return BarChart(
BarChartData(
alignment: BarChartAlignment.spaceAround,
barTouchData: _barTouchData(context),
titlesData: _titlesData(context),
gridData: EnergyManagementChartsHelper.gridData(
horizontalInterval: 100,
),
borderData: EnergyManagementChartsHelper.borderData(),
barGroups: _buildBarGroups(),
groupsSpace: 12,
),
duration: Duration.zero,
);
}
List<BarChartGroupData> _buildBarGroups() {
return List.generate(chartData.length, (index) {
final data = chartData[index];
final stackItems = <BarChartRodData>[];
double currentY = 0;
if (data.aqi != null) {
stackItems.add(
BarChartRodData(
fromY: currentY,
toY: currentY + data.aqi!,
color: AirQualityDataModel.metricColors['aqi']!,
borderRadius: BorderRadius.circular(10),
width: 20,
),
);
currentY += data.aqi! + _rodStackItemsSpacing;
}
if (data.pm25 != null) {
stackItems.add(
BarChartRodData(
fromY: currentY,
toY: currentY + data.pm25!,
color: AirQualityDataModel.metricColors['pm25']!,
borderRadius: BorderRadius.circular(10),
width: 20,
),
);
currentY += data.pm25! + _rodStackItemsSpacing;
}
if (data.pm10 != null) {
stackItems.add(
BarChartRodData(
fromY: currentY,
toY: currentY + data.pm10!,
color: AirQualityDataModel.metricColors['pm10']!,
borderRadius: BorderRadius.circular(10),
width: 20,
),
);
currentY += data.pm10! + 2;
}
if (data.hcho != null) {
stackItems.add(
BarChartRodData(
fromY: currentY,
toY: currentY + data.hcho!,
color: AirQualityDataModel.metricColors['hcho']!,
borderRadius: BorderRadius.circular(10),
width: 20,
),
);
currentY += data.hcho! + 2;
}
if (data.tvoc != null) {
stackItems.add(
BarChartRodData(
fromY: currentY,
toY: currentY + data.tvoc!,
color: AirQualityDataModel.metricColors['tvoc']!,
borderRadius: BorderRadius.circular(10),
width: 20,
),
);
currentY += data.tvoc! + 2;
}
if (data.co2 != null) {
stackItems.add(
BarChartRodData(
fromY: currentY,
toY: currentY + data.co2!,
color: AirQualityDataModel.metricColors['co2']!,
borderRadius: BorderRadius.circular(10),
width: 20,
),
);
currentY += data.co2! + 2;
}
return BarChartGroupData(
x: index,
barRods: stackItems,
groupVertically: true,
);
});
}
BarTouchData _barTouchData(BuildContext context) {
return BarTouchData(
touchTooltipData: BarTouchTooltipData(
getTooltipColor: (_) => ColorsManager.whiteColors,
tooltipBorder: const BorderSide(
color: ColorsManager.semiTransparentBlack,
),
tooltipRoundedRadius: 16,
tooltipPadding: const EdgeInsets.all(8),
getTooltipItem: (group, groupIndex, rod, rodIndex) {
final data = chartData[group.x.toInt()];
final stackItems = rod.rodStackItems;
return BarTooltipItem(
'${data.date.day}/${data.date.month}\n',
context.textTheme.bodyMedium!.copyWith(
color: ColorsManager.blackColor,
fontSize: 14,
),
children: stackItems.map((item) {
final metricName = AirQualityDataModel.metricColors.entries
.firstWhere((entry) => entry.value == item.color)
.key
.toUpperCase();
return TextSpan(
text: '$metricName: ${item.toY - item.fromY}\n',
style: context.textTheme.bodySmall?.copyWith(
color: ColorsManager.blackColor,
fontSize: 12,
),
);
}).toList(),
);
},
),
);
}
FlTitlesData _titlesData(BuildContext context) {
final titlesData = EnergyManagementChartsHelper.titlesData(context);
return titlesData.copyWith(
bottomTitles: AxisTitles(
sideTitles: SideTitles(
showTitles: true,
getTitlesWidget: (value, meta) {
if (value.toInt() >= chartData.length) return const SizedBox.shrink();
return Padding(
padding: const EdgeInsetsDirectional.only(top: 20.0),
child: Text(
chartData[value.toInt()].date.day.toString(),
style: context.textTheme.bodySmall?.copyWith(
color: ColorsManager.lightGreyColor,
fontSize: 12,
),
),
);
},
reservedSize: 32,
),
),
leftTitles: titlesData.leftTitles.copyWith(
sideTitles: titlesData.leftTitles.sideTitles.copyWith(
reservedSize: 70,
getTitlesWidget: (value, meta) => Padding(
padding: const EdgeInsetsDirectional.only(end: 12),
child: FittedBox(
alignment: AlignmentDirectional.centerStart,
fit: BoxFit.scaleDown,
child: Text(
value.toInt().toString(),
style: context.textTheme.bodySmall?.copyWith(
fontSize: 12,
color: ColorsManager.lightGreyColor,
),
),
),
),
),
),
);
} }
} }

View File

@ -1,4 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:syncrow_web/pages/analytics/models/air_quality_data_model.dart';
import 'package:syncrow_web/pages/analytics/modules/air_quality/widgets/aqi_distribution_chart.dart'; import 'package:syncrow_web/pages/analytics/modules/air_quality/widgets/aqi_distribution_chart.dart';
import 'package:syncrow_web/pages/analytics/modules/air_quality/widgets/aqi_distribution_chart_title.dart'; import 'package:syncrow_web/pages/analytics/modules/air_quality/widgets/aqi_distribution_chart_title.dart';
import 'package:syncrow_web/utils/style.dart'; import 'package:syncrow_web/utils/style.dart';
@ -13,15 +14,46 @@ class AqiDistributionChartBox extends StatelessWidget {
decoration: subSectionContainerDecoration.copyWith( decoration: subSectionContainerDecoration.copyWith(
borderRadius: BorderRadius.circular(30), borderRadius: BorderRadius.circular(30),
), ),
child: const Column( child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
AqiDistributionChartTitle(isLoading: false), const AqiDistributionChartTitle(isLoading: false),
SizedBox(height: 10), const SizedBox(height: 10),
Divider(), const Divider(),
SizedBox(height: 20), const SizedBox(height: 20),
Expanded(child: AqiDistributionChart()), Expanded(
child: AqiDistributionChart(
chartData: [
AirQualityDataModel(
date: DateTime.now(),
aqi: 50,
pm25: 30,
pm10: 40,
co2: 120,
hcho: 10,
tvoc: 50,
),
AirQualityDataModel(
date: DateTime.now(),
aqi: 50,
pm25: 25,
pm10: 40,
co2: 120,
hcho: 10,
tvoc: 50,
),
AirQualityDataModel(
date: DateTime.now(),
aqi: 50,
pm25: 25,
pm10: 40,
co2: 120,
hcho: 10,
tvoc: 50,
),
],
)),
], ],
), ),
); );