From 595966d30631163cd02e2417b24d14f1d5104fa0 Mon Sep 17 00:00:00 2001 From: Faris Armoush Date: Wed, 28 May 2025 14:22:35 +0300 Subject: [PATCH] implemented gauge. --- .../air_quality/views/air_quality_view.dart | 2 +- .../air_quality_end_side_gauge_and_info.dart | 267 ++++++++++++++---- pubspec.yaml | 1 + 3 files changed, 218 insertions(+), 52 deletions(-) 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 b4ae5f1b..c24922cf 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 @@ -51,7 +51,7 @@ class AirQualityView extends StatelessWidget { ), ), Expanded( - flex: 2, + flex: 3, child: AirQualityEndSideWidget(), ), ], diff --git a/lib/pages/analytics/modules/air_quality/widgets/air_quality_end_side_gauge_and_info.dart b/lib/pages/analytics/modules/air_quality/widgets/air_quality_end_side_gauge_and_info.dart index 086eda9e..8e01a99a 100644 --- a/lib/pages/analytics/modules/air_quality/widgets/air_quality_end_side_gauge_and_info.dart +++ b/lib/pages/analytics/modules/air_quality/widgets/air_quality_end_side_gauge_and_info.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; +import 'package:gauge_indicator/gauge_indicator.dart'; import 'package:syncrow_web/utils/color_manager.dart'; import 'package:syncrow_web/utils/constants/assets.dart'; import 'package:syncrow_web/utils/extension/build_context_x.dart'; @@ -10,65 +11,229 @@ class AirQualityEndSideGaugeAndInfo extends StatelessWidget { @override Widget build(BuildContext context) { return Expanded( - flex: 3, + flex: 2, child: Row( children: [ - const Expanded(flex: 2, child: Placeholder()), + const Expanded( + flex: 3, + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.start, + children: [ + Expanded(flex: 1, child: AqiGauge(aqi: 200)), + ], + ), + ), const Spacer(), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - 'Air Quality:', - style: context.textTheme.bodySmall?.copyWith( - color: ColorsManager.textPrimaryColor, - fontWeight: FontWeight.w400, - fontSize: 14, - ), - ), - Text( - 'Perfect', - style: context.textTheme.bodySmall?.copyWith( - color: ColorsManager.green, - fontWeight: FontWeight.w400, - fontSize: 30, - ), - ), - DefaultTextStyle( - style: context.textTheme.bodySmall!.copyWith( - color: ColorsManager.textPrimaryColor, - fontWeight: FontWeight.w400, - fontSize: 12, - ), - child: Row( - children: [ - SvgPicture.asset( - Assets.aqiTemperature, - height: 12, - width: 12, - colorFilter: const ColorFilter.mode( - ColorsManager.goodGreen, - BlendMode.srcIn, + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + flex: 2, + child: FittedBox( + alignment: AlignmentDirectional.centerStart, + child: Text( + 'Air Quality:', + style: context.textTheme.bodySmall?.copyWith( + color: ColorsManager.textPrimaryColor, + fontWeight: FontWeight.w400, + fontSize: 14, ), ), - const SizedBox(width: 4), - const Text('30C'), - const SizedBox(width: 10), - SvgPicture.asset( - Assets.aqiHumidity, - height: 12, - width: 12, - colorFilter: const ColorFilter.mode( - ColorsManager.textPrimaryColor, - BlendMode.srcIn, + ), + ), + Expanded( + child: FittedBox( + fit: BoxFit.scaleDown, + alignment: AlignmentDirectional.centerStart, + child: Text( + 'Perfect', + style: context.textTheme.bodySmall?.copyWith( + color: ColorsManager.green, + fontWeight: FontWeight.w400, + fontSize: 30, ), ), - const SizedBox(width: 4), - const Text('30%'), - ], + ), ), - ), - ], + Expanded( + child: FittedBox( + fit: BoxFit.scaleDown, + alignment: AlignmentDirectional.centerStart, + child: DefaultTextStyle( + style: context.textTheme.bodySmall!.copyWith( + color: ColorsManager.textPrimaryColor, + fontWeight: FontWeight.w400, + fontSize: 12, + ), + child: Row( + children: [ + SvgPicture.asset( + Assets.aqiTemperature, + height: 12, + width: 12, + colorFilter: const ColorFilter.mode( + ColorsManager.goodGreen, + BlendMode.srcIn, + ), + ), + const SizedBox(width: 4), + const Text('30C'), + const SizedBox(width: 10), + SvgPicture.asset( + Assets.aqiHumidity, + height: 12, + width: 12, + colorFilter: const ColorFilter.mode( + ColorsManager.textPrimaryColor, + BlendMode.srcIn, + ), + ), + const SizedBox(width: 4), + const Text('30%'), + ], + ), + ), + ), + ), + ], + ), + ), + ], + ), + ); + } +} + +class AqiGauge extends StatelessWidget { + const AqiGauge({super.key, required this.aqi}); + + final double aqi; + + Color _getPointerColor(double value) { + if (value <= 50) { + return ColorsManager.goodGreen; + } else if (value <= 100) { + return ColorsManager.moderateYellow; + } else if (value <= 150) { + return ColorsManager.poorOrange; + } else { + if (value <= 225) { + final t = (value - 151) / (225 - 151); + return Color.lerp( + ColorsManager.unhealthyRed, + ColorsManager.severePink, + t, + )!; + } else { + final t = (value - 226) / (300 - 226); + return Color.lerp( + ColorsManager.severePink, + ColorsManager.hazardousPurple, + t, + )!; + } + } + } + + @override + Widget build(BuildContext context) { + return AnimatedRadialGauge( + value: aqi, + debug: false, + duration: const Duration(milliseconds: 200), + initialValue: 0, + alignment: Alignment.bottomCenter, + builder: (context, child, value) { + return Align( + alignment: AlignmentDirectional.bottomCenter, + child: FittedBox( + fit: BoxFit.scaleDown, + alignment: AlignmentDirectional.bottomCenter, + child: Text.rich( + TextSpan( + text: '${value.toInt()}', + style: context.textTheme.bodySmall?.copyWith( + color: ColorsManager.textPrimaryColor, + fontWeight: FontWeight.w700, + fontSize: 30, + ), + children: [ + const TextSpan( + text: 'AQI', + style: TextStyle( + color: ColorsManager.textPrimaryColor, + fontWeight: FontWeight.w400, + fontSize: 12, + ), + ), + ], + ), + overflow: TextOverflow.ellipsis, + textAlign: TextAlign.center, + ), + ), + ); + }, + axis: GaugeAxis( + progressBar: const GaugeProgressBar.basic(color: Colors.transparent), + style: const GaugeAxisStyle( + cornerRadius: Radius.circular(16), + thickness: 10, + segmentSpacing: 4, + ), + min: 0, + max: 300, + pointer: GaugePointer.circle( + position: const GaugePointerPosition.surface(), + shadow: const BoxShadow( + color: ColorsManager.transparentColor, + blurRadius: 0, + offset: Offset(0, 0), + ), + radius: MediaQuery.sizeOf(context).width * 0.004, + color: ColorsManager.whiteColors, + border: GaugePointerBorder( + width: 4, + color: _getPointerColor(aqi), + ), + ), + transformer: const GaugeAxisTransformer.colorFadeIn( + background: Colors.transparent, + interval: Interval(0, 0), + ), + segments: [ + const GaugeSegment( + from: 0, + to: 50, + cornerRadius: Radius.circular(16), + color: ColorsManager.goodGreen, + ), + const GaugeSegment( + from: 51, + to: 100, + cornerRadius: Radius.circular(16), + color: ColorsManager.moderateYellow, + ), + const GaugeSegment( + from: 101, + to: 150, + cornerRadius: Radius.circular(16), + color: ColorsManager.poorOrange, + ), + const GaugeSegment( + from: 151, + to: 300, + cornerRadius: Radius.circular(16), + gradient: GaugeAxisGradient( + colorStops: [0.0, 0.5, 1.0], + colors: [ + ColorsManager.unhealthyRed, + ColorsManager.severePink, + ColorsManager.hazardousPurple, + ], + ), ), ], ), diff --git a/pubspec.yaml b/pubspec.yaml index 7decc506..2d09f0bb 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -61,6 +61,7 @@ dependencies: firebase_crashlytics: ^4.3.2 firebase_database: ^11.3.2 bloc: ^9.0.0 + gauge_indicator: ^0.4.3 dev_dependencies: