From 666c64231fedc8902dc818d7e1aab91253be36c0 Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Thu, 19 Jun 2025 14:22:37 +0300 Subject: [PATCH 1/9] hides bars in `AqiDistributionChart` where all values are zero. --- .../widgets/aqi_distribution_chart.dart | 23 ++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/lib/pages/analytics/modules/air_quality/widgets/aqi_distribution_chart.dart b/lib/pages/analytics/modules/air_quality/widgets/aqi_distribution_chart.dart index 2f3d7ff0..d4a25e9e 100644 --- a/lib/pages/analytics/modules/air_quality/widgets/aqi_distribution_chart.dart +++ b/lib/pages/analytics/modules/air_quality/widgets/aqi_distribution_chart.dart @@ -32,8 +32,13 @@ class AqiDistributionChart extends StatelessWidget { } List _buildBarGroups() { - return List.generate(chartData.length, (index) { - final data = chartData[index]; + final groups = []; + for (var i = 0; i < chartData.length; i++) { + final data = chartData[i]; + final isAllZero = data.data.every((d) => d.percentage == 0); + if (isAllZero) { + continue; + } final stackItems = []; double currentY = 0; var isFirstElement = true; @@ -56,13 +61,15 @@ class AqiDistributionChart extends StatelessWidget { currentY += percentageData.percentage + _rodStackItemsSpacing; isFirstElement = false; } - - return BarChartGroupData( - x: index, - barRods: stackItems, - groupVertically: true, + groups.add( + BarChartGroupData( + x: i, + barRods: stackItems, + groupVertically: true, + ), ); - }); + } + return groups; } BarTouchData _barTouchData(BuildContext context) { From 78898968e89572e338c920862034d13c5d91b895 Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Thu, 19 Jun 2025 14:23:04 +0300 Subject: [PATCH 2/9] include min in `RangeOfAqiChartsHelper.titlesData.leftTitles`. --- .../modules/air_quality/helpers/range_of_aqi_charts_helper.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/pages/analytics/modules/air_quality/helpers/range_of_aqi_charts_helper.dart b/lib/pages/analytics/modules/air_quality/helpers/range_of_aqi_charts_helper.dart index 21cb2a9e..7e8f3b04 100644 --- a/lib/pages/analytics/modules/air_quality/helpers/range_of_aqi_charts_helper.dart +++ b/lib/pages/analytics/modules/air_quality/helpers/range_of_aqi_charts_helper.dart @@ -40,6 +40,7 @@ abstract final class RangeOfAqiChartsHelper { reservedSize: 70, interval: 50, maxIncluded: false, + minIncluded: true, getTitlesWidget: (value, meta) { final text = value >= 300 ? '301+' : value.toInt().toString(); return Padding( From 7172a0e3fb2ae7aa37db58560316cae57ce026c9 Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Thu, 19 Jun 2025 14:23:39 +0300 Subject: [PATCH 3/9] Matched aqi charts title's to have the same size no matter what the window size is. --- .../widgets/aqi_distribution_chart_title.dart | 37 ++++++++++--------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/lib/pages/analytics/modules/air_quality/widgets/aqi_distribution_chart_title.dart b/lib/pages/analytics/modules/air_quality/widgets/aqi_distribution_chart_title.dart index 926d28e1..f7be6ee3 100644 --- a/lib/pages/analytics/modules/air_quality/widgets/aqi_distribution_chart_title.dart +++ b/lib/pages/analytics/modules/air_quality/widgets/aqi_distribution_chart_title.dart @@ -19,7 +19,7 @@ class AqiDistributionChartTitle extends StatelessWidget { children: [ ChartsLoadingWidget(isLoading: isLoading), const Expanded( - flex: 3, + flex: 4, child: FittedBox( fit: BoxFit.scaleDown, alignment: AlignmentDirectional.centerStart, @@ -28,23 +28,26 @@ class AqiDistributionChartTitle extends StatelessWidget { ), ), ), - FittedBox( - alignment: AlignmentDirectional.centerEnd, - fit: BoxFit.scaleDown, - child: AqiTypeDropdown( - onChanged: (value) { - if (value != null) { - final bloc = context.read(); - try { - final param = _makeLoadAqiDistributionParam(context, value); - bloc.add(LoadAirQualityDistribution(param)); - } catch (_) { - return; - } finally { - bloc.add(UpdateAqiTypeEvent(value)); + Expanded( + flex: 2, + child: FittedBox( + alignment: AlignmentDirectional.centerEnd, + fit: BoxFit.scaleDown, + child: AqiTypeDropdown( + onChanged: (value) { + if (value != null) { + final bloc = context.read(); + try { + final param = _makeLoadAqiDistributionParam(context, value); + bloc.add(LoadAirQualityDistribution(param)); + } catch (_) { + return; + } finally { + bloc.add(UpdateAqiTypeEvent(value)); + } } - } - }, + }, + ), ), ), ], From ad5ada9d5528fbfc6e85f5865919d5e036ffb5ab Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Thu, 19 Jun 2025 14:24:49 +0300 Subject: [PATCH 4/9] allowed `RangeOfAqiValue` values to be nullable, and if they were null they fallback to zero. --- lib/pages/analytics/models/range_of_aqi.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/pages/analytics/models/range_of_aqi.dart b/lib/pages/analytics/models/range_of_aqi.dart index 0308d564..dfb48ecb 100644 --- a/lib/pages/analytics/models/range_of_aqi.dart +++ b/lib/pages/analytics/models/range_of_aqi.dart @@ -38,9 +38,9 @@ class RangeOfAqiValue extends Equatable { factory RangeOfAqiValue.fromJson(Map json) { return RangeOfAqiValue( type: json['type'] as String, - min: (json['min'] as num).toDouble(), - average: (json['average'] as num).toDouble(), - max: (json['max'] as num).toDouble(), + min: (json['min'] as num? ?? 0).toDouble(), + average: (json['average'] as num? ?? 0).toDouble(), + max: (json['max'] as num? ?? 0).toDouble(), ); } From 8dea89db0eebcdd6d8949e387f35e853496b6e8a Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Thu, 19 Jun 2025 15:12:54 +0300 Subject: [PATCH 5/9] Implemented AQI legend. --- .../air_quality/views/air_quality_view.dart | 12 ++++-- .../air_quality/widgets/aqi_legend.dart | 38 +++++++++++++++++++ .../widgets/chart_informative_cell.dart | 4 +- 3 files changed, 50 insertions(+), 4 deletions(-) create mode 100644 lib/pages/analytics/modules/air_quality/widgets/aqi_legend.dart diff --git a/lib/pages/analytics/modules/air_quality/views/air_quality_view.dart b/lib/pages/analytics/modules/air_quality/views/air_quality_view.dart index b6d403eb..61179d15 100644 --- a/lib/pages/analytics/modules/air_quality/views/air_quality_view.dart +++ b/lib/pages/analytics/modules/air_quality/views/air_quality_view.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:syncrow_web/pages/analytics/modules/air_quality/widgets/air_quality_end_side_widget.dart'; import 'package:syncrow_web/pages/analytics/modules/air_quality/widgets/aqi_distribution_chart_box.dart'; +import 'package:syncrow_web/pages/analytics/modules/air_quality/widgets/aqi_legend.dart'; import 'package:syncrow_web/pages/analytics/modules/air_quality/widgets/range_of_aqi_chart_box.dart'; class AirQualityView extends StatelessWidget { @@ -20,6 +21,10 @@ class AirQualityView extends StatelessWidget { child: Column( spacing: 32, children: [ + SizedBox( + height: height * 0.1, + child: const AqiLegend(), + ), SizedBox( height: height * 1.2, child: const AirQualityEndSideWidget(), @@ -40,7 +45,7 @@ class AirQualityView extends StatelessWidget { return SingleChildScrollView( child: Container( padding: _padding, - height: height * 1.1, + height: height * 1.2, child: const Column( children: [ Expanded( @@ -52,8 +57,9 @@ class AirQualityView extends StatelessWidget { child: Column( spacing: 20, children: [ - Expanded(child: RangeOfAqiChartBox()), - Expanded(child: AqiDistributionChartBox()), + Expanded(flex: 2, child: AqiLegend()), + Expanded(flex: 12, child: RangeOfAqiChartBox()), + Expanded(flex: 12, child: AqiDistributionChartBox()), ], ), ), diff --git a/lib/pages/analytics/modules/air_quality/widgets/aqi_legend.dart b/lib/pages/analytics/modules/air_quality/widgets/aqi_legend.dart new file mode 100644 index 00000000..3a00925d --- /dev/null +++ b/lib/pages/analytics/modules/air_quality/widgets/aqi_legend.dart @@ -0,0 +1,38 @@ +import 'package:flutter/material.dart'; +import 'package:syncrow_web/pages/analytics/modules/air_quality/helpers/range_of_aqi_charts_helper.dart'; +import 'package:syncrow_web/pages/analytics/modules/analytics/widgets/chart_informative_cell.dart'; +import 'package:syncrow_web/utils/style.dart'; + +class AqiLegend extends StatelessWidget { + const AqiLegend({super.key}); + + @override + Widget build(BuildContext context) { + return Container( + padding: const EdgeInsetsDirectional.all(20), + decoration: subSectionContainerDecoration.copyWith( + borderRadius: BorderRadius.circular(20), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + spacing: 16, + children: RangeOfAqiChartsHelper.gradientData.map((e) { + return Flexible( + flex: 4, + child: FittedBox( + fit: BoxFit.fill, + child: ChartInformativeCell( + color: e.$1, + title: FittedBox( + fit: BoxFit.fill, + child: Text(e.$2), + ), + height: null, + ), + ), + ); + }).toList(), + ), + ); + } +} diff --git a/lib/pages/analytics/modules/analytics/widgets/chart_informative_cell.dart b/lib/pages/analytics/modules/analytics/widgets/chart_informative_cell.dart index eec31998..f79ecb44 100644 --- a/lib/pages/analytics/modules/analytics/widgets/chart_informative_cell.dart +++ b/lib/pages/analytics/modules/analytics/widgets/chart_informative_cell.dart @@ -7,16 +7,18 @@ class ChartInformativeCell extends StatelessWidget { required this.title, required this.color, this.hasBorder = false, + this.height, }); final Widget title; final Color color; final bool hasBorder; + final double? height; @override Widget build(BuildContext context) { return Container( - height: MediaQuery.sizeOf(context).height * 0.0385, + height: height ?? MediaQuery.sizeOf(context).height * 0.0385, padding: const EdgeInsetsDirectional.symmetric( vertical: 8, horizontal: 12, From e4cc5fce50508c9f474129b4ab837d91c6b49267 Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Thu, 19 Jun 2025 15:18:18 +0300 Subject: [PATCH 6/9] Increased the size of `AqiDistributionChart` tooltip. --- .../widgets/aqi_distribution_chart.dart | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/lib/pages/analytics/modules/air_quality/widgets/aqi_distribution_chart.dart b/lib/pages/analytics/modules/air_quality/widgets/aqi_distribution_chart.dart index d4a25e9e..4807ebbd 100644 --- a/lib/pages/analytics/modules/air_quality/widgets/aqi_distribution_chart.dart +++ b/lib/pages/analytics/modules/air_quality/widgets/aqi_distribution_chart.dart @@ -80,6 +80,7 @@ class AqiDistributionChart extends StatelessWidget { color: ColorsManager.semiTransparentBlack, ), tooltipRoundedRadius: 16, + maxContentWidth: 500, tooltipPadding: const EdgeInsets.all(8), getTooltipItem: (group, groupIndex, rod, rodIndex) { final data = chartData[group.x]; @@ -88,10 +89,13 @@ class AqiDistributionChart extends StatelessWidget { final textStyle = context.textTheme.bodySmall?.copyWith( color: ColorsManager.blackColor, - fontSize: 8, + fontSize: 11, ); for (final percentageData in data.data) { + if (percentageData.percentage == 0) { + continue; + } final percentage = percentageData.percentage.toStringAsFixed(1); final type = percentageData.type[0].toUpperCase() + percentageData.type.substring(1).replaceAll('_', ' '); @@ -105,7 +109,7 @@ class AqiDistributionChart extends StatelessWidget { DateFormat('dd/MM/yyyy').format(data.date), context.textTheme.bodyMedium!.copyWith( color: ColorsManager.blackColor, - fontSize: 9, + fontSize: 12, fontWeight: FontWeight.w600, ), textAlign: TextAlign.start, @@ -125,7 +129,6 @@ class AqiDistributionChart extends StatelessWidget { final leftTitles = titlesData.leftTitles.copyWith( sideTitles: titlesData.leftTitles.sideTitles.copyWith( reservedSize: 70, - interval: 20, maxIncluded: false, minIncluded: true, getTitlesWidget: (value, meta) => Padding( @@ -147,7 +150,7 @@ class AqiDistributionChart extends StatelessWidget { final bottomTitles = AxisTitles( sideTitles: SideTitles( - showTitles: true, + showTitles: chartData.isNotEmpty, getTitlesWidget: (value, _) => FittedBox( alignment: AlignmentDirectional.bottomCenter, fit: BoxFit.scaleDown, @@ -155,11 +158,10 @@ class AqiDistributionChart extends StatelessWidget { chartData[value.toInt()].date.day.toString(), style: context.textTheme.bodySmall?.copyWith( color: ColorsManager.lightGreyColor, - fontSize: 8, + fontSize: 12, ), ), ), - reservedSize: 36, ), ); From 5201a65a978f795108423a29a758826e9884b9f3 Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Thu, 19 Jun 2025 15:19:58 +0300 Subject: [PATCH 7/9] matched sizes of bottom titles in aqi charts. --- .../modules/air_quality/helpers/range_of_aqi_charts_helper.dart | 1 + .../modules/air_quality/widgets/aqi_distribution_chart.dart | 1 + 2 files changed, 2 insertions(+) diff --git a/lib/pages/analytics/modules/air_quality/helpers/range_of_aqi_charts_helper.dart b/lib/pages/analytics/modules/air_quality/helpers/range_of_aqi_charts_helper.dart index 7e8f3b04..e4aa5b6f 100644 --- a/lib/pages/analytics/modules/air_quality/helpers/range_of_aqi_charts_helper.dart +++ b/lib/pages/analytics/modules/air_quality/helpers/range_of_aqi_charts_helper.dart @@ -23,6 +23,7 @@ abstract final class RangeOfAqiChartsHelper { return titlesData.copyWith( bottomTitles: titlesData.bottomTitles.copyWith( sideTitles: titlesData.bottomTitles.sideTitles.copyWith( + reservedSize: 36, getTitlesWidget: (value, meta) => Padding( padding: const EdgeInsetsDirectional.only(top: 20.0), child: Text( diff --git a/lib/pages/analytics/modules/air_quality/widgets/aqi_distribution_chart.dart b/lib/pages/analytics/modules/air_quality/widgets/aqi_distribution_chart.dart index 4807ebbd..e35a05e7 100644 --- a/lib/pages/analytics/modules/air_quality/widgets/aqi_distribution_chart.dart +++ b/lib/pages/analytics/modules/air_quality/widgets/aqi_distribution_chart.dart @@ -162,6 +162,7 @@ class AqiDistributionChart extends StatelessWidget { ), ), ), + reservedSize: 36, ), ); From 23c3bf11f9496dbb73f8b4910fcfbae94c9f1053 Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Thu, 19 Jun 2025 15:38:28 +0300 Subject: [PATCH 8/9] Improved alignment of `AqiLocationInfoCell`. --- .../widgets/aqi_location_info_cell.dart | 55 ++++++++++--------- 1 file changed, 28 insertions(+), 27 deletions(-) diff --git a/lib/pages/analytics/modules/air_quality/widgets/aqi_location_info_cell.dart b/lib/pages/analytics/modules/air_quality/widgets/aqi_location_info_cell.dart index fa0216a1..00233ad3 100644 --- a/lib/pages/analytics/modules/air_quality/widgets/aqi_location_info_cell.dart +++ b/lib/pages/analytics/modules/air_quality/widgets/aqi_location_info_cell.dart @@ -47,36 +47,37 @@ class AqiLocationInfoCell extends StatelessWidget { ), ), Align( - alignment: AlignmentDirectional.bottomEnd, - child: Padding( - padding: const EdgeInsetsDirectional.all(10), - child: SizedBox( - height: 40, - width: 120, - child: FittedBox( - fit: BoxFit.scaleDown, - alignment: AlignmentDirectional.bottomEnd, - child: Text( - value, - style: context.textTheme.bodySmall?.copyWith( - color: ColorsManager.vividBlue.withValues(alpha: 0.7), - fontWeight: FontWeight.w700, - fontSize: 24, + alignment: AlignmentDirectional.bottomCenter, + child: Row( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Expanded( + child: SvgPicture.asset( + svgPath, + fit: BoxFit.scaleDown, + alignment: AlignmentDirectional.bottomStart, + ), + ), + Expanded( + child: FittedBox( + fit: BoxFit.scaleDown, + alignment: AlignmentDirectional.bottomEnd, + child: Padding( + padding: const EdgeInsetsDirectional.all(10), + child: Text( + value, + style: context.textTheme.bodySmall?.copyWith( + color: ColorsManager.vividBlue.withValues( + alpha: 0.7, + ), + fontWeight: FontWeight.w700, + fontSize: 24, + ), + ), ), ), ), - ), - ), - ), - Align( - alignment: AlignmentDirectional.bottomStart, - child: SizedBox.square( - dimension: MediaQuery.sizeOf(context).width * 0.45, - child: FittedBox( - fit: BoxFit.scaleDown, - alignment: AlignmentDirectional.bottomStart, - child: SvgPicture.asset(svgPath), - ), + ], ), ), ], From 1f82e8411561cdc7582717fc2b466566ae240fc9 Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Sun, 22 Jun 2025 10:55:41 +0300 Subject: [PATCH 9/9] doesnt fetch devices on date change. --- .../helpers/fetch_air_quality_data_helper.dart | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/pages/analytics/modules/air_quality/helpers/fetch_air_quality_data_helper.dart b/lib/pages/analytics/modules/air_quality/helpers/fetch_air_quality_data_helper.dart index 223c0357..f23abd7b 100644 --- a/lib/pages/analytics/modules/air_quality/helpers/fetch_air_quality_data_helper.dart +++ b/lib/pages/analytics/modules/air_quality/helpers/fetch_air_quality_data_helper.dart @@ -24,11 +24,13 @@ abstract final class FetchAirQualityDataHelper { }) { final date = context.read().state.monthlyDate; final aqiType = context.read().state.selectedAqiType; - loadAnalyticsDevices( - context, - communityUuid: communityUuid, - spaceUuid: spaceUuid, - ); + if (shouldFetchAnalyticsDevices) { + loadAnalyticsDevices( + context, + communityUuid: communityUuid, + spaceUuid: spaceUuid, + ); + } loadRangeOfAqi( context, spaceUuid: spaceUuid,