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 38f62cd7..f2b485f7 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 @@ -40,7 +40,7 @@ class AirQualityView extends StatelessWidget { spacing: 32, children: [ Expanded( - flex: 2, + flex: 5, child: Column( spacing: 20, children: [ @@ -49,7 +49,10 @@ class AirQualityView extends StatelessWidget { ], ), ), - Expanded(child: AirQualityEndSideWidget()), + Expanded( + flex: 3, + child: AirQualityEndSideWidget(), + ), ], ), ), diff --git a/lib/pages/analytics/modules/air_quality/widgets/air_quality_end_side_widget.dart b/lib/pages/analytics/modules/air_quality/widgets/air_quality_end_side_widget.dart index 2d6ace36..8c4bc9c1 100644 --- a/lib/pages/analytics/modules/air_quality/widgets/air_quality_end_side_widget.dart +++ b/lib/pages/analytics/modules/air_quality/widgets/air_quality_end_side_widget.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:syncrow_web/pages/analytics/modules/air_quality/widgets/aqi_sub_value_widget.dart'; import 'package:syncrow_web/pages/analytics/modules/analytics/blocs/analytics_devices/analytics_devices_bloc.dart'; import 'package:syncrow_web/pages/analytics/modules/energy_management/helpers/fetch_energy_management_data_helper.dart'; import 'package:syncrow_web/pages/analytics/modules/energy_management/widgets/analytics_device_dropdown.dart'; @@ -39,6 +40,46 @@ class AirQualityEndSideWidget extends StatelessWidget { fontSize: 12, ), ), + const Divider(), + Container( + decoration: secondarySection.copyWith(boxShadow: const []), + padding: const EdgeInsetsDirectional.all(20), + child: const Column( + spacing: 6, + children: [ + AqiSubValueWidget( + label: 'PM2.5', + value: 19, + unit: 'µg/m³', + ), + AqiSubValueWidget( + label: 'PM10', + value: 42, + unit: 'µg/m³', + ), + AqiSubValueWidget( + label: 'CO2', + value: 610, + unit: 'ppm', + ), + AqiSubValueWidget( + label: 'VOC', + value: 1, + unit: 'mg/m³', + ), + AqiSubValueWidget( + label: 'O3', + value: 55, + unit: 'µg/m³', + ), + AqiSubValueWidget( + label: 'NO2', + value: 18, + unit: 'µg/m³', + ), + ], + ), + ), ], ), ); diff --git a/lib/pages/analytics/modules/air_quality/widgets/aqi_sub_value_widget.dart b/lib/pages/analytics/modules/air_quality/widgets/aqi_sub_value_widget.dart new file mode 100644 index 00000000..d9843bca --- /dev/null +++ b/lib/pages/analytics/modules/air_quality/widgets/aqi_sub_value_widget.dart @@ -0,0 +1,139 @@ +import 'package:flutter/material.dart'; +import 'package:syncrow_web/utils/color_manager.dart'; +import 'package:syncrow_web/utils/extension/build_context_x.dart'; + +final class _AqiRange { + const _AqiRange({ + required this.max, + required this.color, + }); + + final int max; + final Color color; +} + +class AqiSubValueWidget extends StatelessWidget { + final String label; + final int value; + final String unit; + + const AqiSubValueWidget({ + super.key, + required this.label, + required this.value, + required this.unit, + }); + + static const List<_AqiRange> _ranges = [ + _AqiRange(max: 12, color: ColorsManager.green), + _AqiRange(max: 35, color: Color(0xFFFFF176)), + _AqiRange(max: 55, color: Color(0xFFFFD54F)), + _AqiRange(max: 150, color: Color(0xFFE57373)), + _AqiRange(max: 250, color: Color(0xFFBA68C8)), + _AqiRange(max: 500, color: Color(0xFFB39DDB)), + ]; + + int _getActiveSegment(int value) { + for (int i = 0; i < _ranges.length; i++) { + if (value <= _ranges[i].max) return i; + } + return _ranges.length - 1; + } + + @override + Widget build(BuildContext context) { + final activeSegment = _getActiveSegment(value); + return Container( + padding: const EdgeInsetsDirectional.all(10), + decoration: BoxDecoration( + color: ColorsManager.whiteColors, + borderRadius: BorderRadius.circular(12), + ), + child: Row( + spacing: MediaQuery.sizeOf(context).width * 0.0075, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + _buildLabel(context), + _buildSegmentedBar(activeSegment), + _buildValueAndUnit(context), + ], + ), + ); + } + + Widget _buildValueAndUnit(BuildContext context) { + return Expanded( + child: FittedBox( + fit: BoxFit.scaleDown, + alignment: AlignmentDirectional.centerEnd, + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Text( + value.toString(), + style: context.textTheme.titleMedium?.copyWith( + color: ColorsManager.blackColor, + fontWeight: FontWeight.w400, + fontSize: 14, + ), + ), + Text( + unit, + style: context.textTheme.bodySmall?.copyWith( + color: ColorsManager.blackColor, + fontWeight: FontWeight.w400, + fontSize: 10, + ), + ), + ], + ), + ), + ); + } + + Widget _buildSegmentedBar(int activeSegment) { + return Expanded( + flex: 4, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 12), + child: Row( + spacing: 4, + mainAxisAlignment: MainAxisAlignment.center, + children: List.generate(_ranges.length, (index) { + final isActive = index == activeSegment; + final color = _ranges[index].color.withValues( + alpha: isActive ? 1.0 : 0.25, + ); + return Expanded( + child: Container( + height: 5, + decoration: BoxDecoration( + color: color, + borderRadius: BorderRadius.circular(4), + ), + ), + ); + }), + ), + ), + ); + } + + Widget _buildLabel(BuildContext context) { + return Expanded( + child: FittedBox( + fit: BoxFit.scaleDown, + alignment: AlignmentDirectional.centerStart, + child: Text( + label, + style: context.textTheme.bodySmall?.copyWith( + color: ColorsManager.blackColor, + fontWeight: FontWeight.w400, + fontSize: 14, + ), + ), + ), + ); + } +}