diff --git a/lib/view/barometer_screen.dart b/lib/view/barometer_screen.dart index 9e2f83d49..c20a3151f 100644 --- a/lib/view/barometer_screen.dart +++ b/lib/view/barometer_screen.dart @@ -79,20 +79,36 @@ class _BarometerScreenState extends State { CommonScaffold( title: barometerTitle, onGuidePressed: _showInstrumentGuide, - body: SafeArea( - child: Column( - children: [ - const Expanded( - flex: 45, - child: BarometerCard(), - ), - Expanded( - flex: 55, - child: _buildChartSection(), - ), - ], - ), - ), + body: SafeArea(child: LayoutBuilder(builder: (context, constraints) { + final isLargeScreen = constraints.maxWidth > 900; + if (isLargeScreen) { + return Row( + children: [ + const Expanded( + flex: 35, + child: BarometerCard(), + ), + Expanded( + flex: 65, + child: _buildChartSection(), + ), + ], + ); + } else { + return Column( + children: [ + const Expanded( + flex: 45, + child: BarometerCard(), + ), + Expanded( + flex: 55, + child: _buildChartSection(), + ), + ], + ); + } + })), ), if (_showGuide) InstrumentOverviewDrawer( diff --git a/lib/view/luxmeter_screen.dart b/lib/view/luxmeter_screen.dart index 320034ea9..d534f2d07 100644 --- a/lib/view/luxmeter_screen.dart +++ b/lib/view/luxmeter_screen.dart @@ -91,20 +91,36 @@ class _LuxMeterScreenState extends State { CommonScaffold( title: luxMeterTitle, onGuidePressed: _showInstrumentGuide, - body: SafeArea( - child: Column( - children: [ - const Expanded( - flex: 45, - child: LuxMeterCard(), - ), - Expanded( - flex: 55, - child: _buildChartSection(), - ), - ], - ), - ), + body: SafeArea(child: LayoutBuilder(builder: (context, constraints) { + final isLargeScreen = constraints.maxWidth > 900; + if (isLargeScreen) { + return Row( + children: [ + const Expanded( + flex: 35, + child: LuxMeterCard(), + ), + Expanded( + flex: 65, + child: _buildChartSection(), + ), + ], + ); + } else { + return Column( + children: [ + const Expanded( + flex: 45, + child: LuxMeterCard(), + ), + Expanded( + flex: 55, + child: _buildChartSection(), + ), + ], + ); + } + })), ), if (_showGuide) InstrumentOverviewDrawer( diff --git a/lib/view/soundmeter_screen.dart b/lib/view/soundmeter_screen.dart index 161ddebcb..d034dac1e 100644 --- a/lib/view/soundmeter_screen.dart +++ b/lib/view/soundmeter_screen.dart @@ -58,17 +58,38 @@ class _SoundMeterScreenState extends State { title: soundMeterTitle, onGuidePressed: _showInstrumentGuide, body: SafeArea( - child: Column( - children: [ - const Expanded( - flex: 45, - child: SoundMeterCard(), - ), - Expanded( - flex: 55, - child: _buildChartSection(), - ), - ], + child: LayoutBuilder( + builder: (context, constraints) { + final isLargeScreen = constraints.maxWidth > 900; + + if (isLargeScreen) { + return Row( + children: [ + const Expanded( + flex: 35, + child: SoundMeterCard(), + ), + Expanded( + flex: 65, + child: _buildChartSection(), + ), + ], + ); + } else { + return Column( + children: [ + const Expanded( + flex: 45, + child: SoundMeterCard(), + ), + Expanded( + flex: 55, + child: _buildChartSection(), + ), + ], + ); + } + }, ), ), ), diff --git a/lib/view/widgets/barometer_card.dart b/lib/view/widgets/barometer_card.dart index 594becaa6..56e62dd5e 100644 --- a/lib/view/widgets/barometer_card.dart +++ b/lib/view/widgets/barometer_card.dart @@ -47,87 +47,68 @@ class _BarometerCardState extends State { padding: EdgeInsets.all(cardPadding), child: LayoutBuilder( builder: (context, constraints) { - return Row( - children: [ - Expanded( - flex: screenWidth < 500 ? 40 : 35, - child: Column( - children: [ - Expanded( - flex: 75, - child: Instrumentstats( - titleFontSize: titleFontSize, - statFontSize: statFontSize, - maxValue: maxPressure, - minValue: minPressure, - avgValue: avgPressure, - unit: atm, - ), + if (isLargeScreen) { + return Column( + children: [ + Expanded( + flex: 40, + child: Center( + child: GaugeWidget( + gaugeSize: gaugeSize, + currentValue: currentPressure, + minValue: 0, + maxValue: 2, + unit: atm, + currentValueFontSize: pressureValueFontSize, ), - Expanded( - flex: 25, - child: - _buildAltitudeTile(currentAltitude, statFontSize), - ), - ], + ), + ), + Expanded( + flex: 60, + child: Instrumentstats( + titleFontSize: titleFontSize, + statFontSize: statFontSize, + maxValue: maxPressure, + minValue: minPressure, + avgValue: avgPressure, + unit: atm, + currentAltitude: currentAltitude, + ), ), - ), - Expanded( - flex: screenWidth < 500 ? 60 : 65, - child: GaugeWidget( - gaugeSize: gaugeSize, - currentValue: currentPressure, - minValue: 0, - maxValue: 2, + ], + ); + } else { + return Row( + children: [ + Expanded( + flex: screenWidth < 500 ? 40 : 35, + child: Instrumentstats( + titleFontSize: titleFontSize, + statFontSize: statFontSize, + maxValue: maxPressure, + minValue: minPressure, + avgValue: avgPressure, unit: atm, - currentValueFontSize: pressureValueFontSize), - ), - ], - ); + currentAltitude: currentAltitude, + ), + ), + Expanded( + flex: screenWidth < 500 ? 60 : 65, + child: GaugeWidget( + gaugeSize: gaugeSize, + currentValue: currentPressure, + minValue: 0, + maxValue: 2, + unit: atm, + currentValueFontSize: pressureValueFontSize), + ), + ], + ); + } }, ), ), ), ); } - - Widget _buildAltitudeTile(double currentAltitude, double fontSize) { - final screenWidth = MediaQuery.of(context).size.width; - final padding = screenWidth < 400 ? 15.0 : 20.0; - - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - Center( - child: Text( - '$altitudeLabel ($meterUnit)', - style: TextStyle( - color: cardContentColor, - fontSize: fontSize, - fontWeight: FontWeight.w600, - ), - ), - ), - const SizedBox(height: 4), - Center( - child: Container( - padding: EdgeInsets.symmetric(horizontal: padding, vertical: 3), - decoration: BoxDecoration( - border: Border.all(color: instrumentStatBoxColor), - borderRadius: BorderRadius.circular(6), - ), - child: Text( - currentAltitude.toStringAsFixed(2), - style: TextStyle( - color: cardContentColor, - fontSize: fontSize, - fontWeight: FontWeight.bold, - ), - ), - ), - ), - ], - ); - } } diff --git a/lib/view/widgets/gauge_widget.dart b/lib/view/widgets/gauge_widget.dart index 4c77729a6..008a03e41 100644 --- a/lib/view/widgets/gauge_widget.dart +++ b/lib/view/widgets/gauge_widget.dart @@ -12,129 +12,174 @@ class GaugeWidget extends StatelessWidget { final double minValue; final double maxValue; final String unit; - const GaugeWidget( - {super.key, - required this.gaugeSize, - required this.currentValue, - required this.maxValue, - required this.minValue, - required this.unit, - required this.currentValueFontSize}); + + const GaugeWidget({ + super.key, + required this.gaugeSize, + required this.currentValue, + required this.maxValue, + required this.minValue, + required this.unit, + required this.currentValueFontSize, + }); + @override Widget build(BuildContext context) { - double range = maxValue - minValue; - double normalizedValue = (currentValue - minValue) / range; - double gaugeValue = normalizedValue * 100; - gaugeValue = gaugeValue.clamp(0.0, 100.0); - return Column( - mainAxisAlignment: MainAxisAlignment.center, + return LayoutBuilder( + builder: (context, constraints) { + final maxSize = min(constraints.maxWidth, constraints.maxHeight); + final adaptiveGaugeSize = + min(gaugeSize, maxSize * 0.9).clamp(80.0, 300.0); + + final adaptiveFontSize = (adaptiveGaugeSize * 0.08).clamp(10.0, 20.0); + final errorFontSize = (adaptiveFontSize * 0.4).clamp(6.0, 10.0); + + double range = maxValue - minValue; + double normalizedValue = + range > 0 ? (currentValue - minValue) / range : 0; + double gaugeValue = (normalizedValue * 100).clamp(0.0, 100.0); + + return Center( + child: SizedBox( + width: adaptiveGaugeSize, + height: adaptiveGaugeSize, + child: _buildGauge( + adaptiveGaugeSize, gaugeValue, adaptiveFontSize, errorFontSize), + ), + ); + }, + ); + } + + Widget _buildGauge( + double size, double gaugeValue, double fontSize, double errorFontSize) { + return Stack( + alignment: Alignment.center, children: [ - Flexible( - child: Stack( - alignment: Alignment.center, - children: [ - ClipOval( - child: Container( - width: gaugeSize, - height: gaugeSize, - constraints: BoxConstraints( - maxWidth: MediaQuery.of(context).size.width * 0.5, - maxHeight: MediaQuery.of(context).size.width * 0.5, - ), - decoration: BoxDecoration( - color: gaugeBackgroundColor, - shape: BoxShape.circle, - ), - child: Padding( - padding: EdgeInsets.fromLTRB(gaugeSize * 0.06, 0, - gaugeSize * 0.06, gaugeSize * 0.13), - child: AnimatedRadialGauge( - duration: const Duration(milliseconds: 500), - curve: Curves.elasticOut, - radius: gaugeSize * 0.45, - value: gaugeValue, - axis: GaugeAxis( - min: 0, - max: 100, - degrees: 270, - style: GaugeAxisStyle( - thickness: gaugeSize * 0.05, - background: gaugeAxisColor, - segmentSpacing: 2, - ), - progressBar: GaugeProgressBar.basic( - color: gaugeProgressColor, - ), - pointer: GaugePointer.needle( - width: gaugeSize * 0.09, - height: gaugeSize * 0.35, - borderRadius: 12, - color: gaugeNeedleColor, - ), - ), - ), - ), - ), + ClipOval( + child: Container( + width: size, + height: size, + decoration: BoxDecoration( + color: gaugeBackgroundColor, + shape: BoxShape.circle, + ), + child: Padding( + padding: EdgeInsets.fromLTRB( + size * 0.06, + 0, + size * 0.06, + size * 0.13, ), - Container( - width: gaugeSize * 0.6, - height: gaugeSize * 0.6, - decoration: const BoxDecoration( - color: Colors.transparent, - shape: BoxShape.circle, - ), - child: Column( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - Text( - '${currentValue.toStringAsFixed(1)} $unit', - style: TextStyle( - fontSize: currentValueFontSize * 1.0, - fontWeight: FontWeight.bold, - color: blackTextColor, - ), - ), - if (currentValue > maxValue) - Padding( - padding: const EdgeInsets.only(top: 4), - child: Text( - maxScaleError, - style: TextStyle( - fontSize: currentValueFontSize * 0.4, - color: gaugeMaxScaleLimitColor, - fontWeight: FontWeight.w500, - ), - ), - ), - ], + child: _buildAnimatedGauge(size, gaugeValue), + ), + ), + ), + _buildCenterDisplay(size, fontSize, errorFontSize), + ..._buildTickMarks(size), + ], + ); + } + + Widget _buildAnimatedGauge(double size, double gaugeValue) { + return AnimatedRadialGauge( + duration: const Duration(milliseconds: 500), + curve: Curves.elasticOut, + radius: size * 0.45, + value: gaugeValue, + axis: GaugeAxis( + min: 0, + max: 100, + degrees: 270, + style: GaugeAxisStyle( + thickness: (size * 0.05).clamp(3.0, 15.0), + background: gaugeAxisColor, + segmentSpacing: 2, + ), + progressBar: GaugeProgressBar.basic( + color: gaugeProgressColor, + ), + pointer: GaugePointer.needle( + width: (size * 0.09).clamp(4.0, 20.0), + height: (size * 0.35).clamp(15.0, 80.0), + borderRadius: ((size * 0.09).clamp(4.0, 20.0)) / 2, + color: gaugeNeedleColor, + ), + ), + ); + } + + Widget _buildCenterDisplay( + double size, double fontSize, double errorFontSize) { + return Container( + width: size * 0.6, + height: size * 0.6, + decoration: const BoxDecoration( + color: Colors.transparent, + shape: BoxShape.circle, + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Flexible( + child: FittedBox( + fit: BoxFit.scaleDown, + child: Text( + '${currentValue.toStringAsFixed(1)} $unit', + style: TextStyle( + fontSize: fontSize, + fontWeight: FontWeight.bold, + color: blackTextColor, ), + textAlign: TextAlign.center, ), - ...List.generate(8, (index) { - final angle = (index * 45.0 - 135) * (3.14159 / 180); - final tickRadius = gaugeSize * 0.35; - final tickLength = gaugeSize * 0.04; - return Positioned( - left: gaugeSize / 2 + (tickRadius * cos(angle)) - 1, - top: gaugeSize / 2 + - (tickRadius * sin(angle)) - - tickLength / 2, - child: Transform.rotate( - angle: angle + (3.14159 / 2), - child: Container( - width: 3, - height: tickLength, - decoration: BoxDecoration( - color: Colors.white.withAlpha(250), - borderRadius: BorderRadius.circular(1), - ), + ), + ), + if (currentValue > maxValue) + Flexible( + child: Padding( + padding: const EdgeInsets.only(top: 4), + child: FittedBox( + fit: BoxFit.scaleDown, + child: Text( + maxScaleError, + style: TextStyle( + fontSize: errorFontSize, + color: gaugeMaxScaleLimitColor, + fontWeight: FontWeight.w500, ), + textAlign: TextAlign.center, ), - ); - }), - ], + ), + ), + ), + ], + ), + ); + } + + List _buildTickMarks(double size) { + return List.generate(8, (index) { + final angle = (index * 45.0 - 135) * (pi / 180); + final tickRadius = size * 0.35; + final tickLength = (size * 0.04).clamp(3.0, 12.0); + final tickWidth = (size * 0.008).clamp(1.5, 4.0); + + return Positioned( + left: size / 2 + (tickRadius * cos(angle)) - tickWidth / 2, + top: size / 2 + (tickRadius * sin(angle)) - tickLength / 2, + child: Transform.rotate( + angle: angle + (pi / 2), + child: Container( + width: tickWidth, + height: tickLength, + decoration: BoxDecoration( + color: Colors.white.withAlpha(250), + borderRadius: BorderRadius.circular(tickWidth / 2), + ), ), ), - ], - ); + ); + }); } } diff --git a/lib/view/widgets/instruments_stats.dart b/lib/view/widgets/instruments_stats.dart index 183e11e7c..5f951c421 100644 --- a/lib/view/widgets/instruments_stats.dart +++ b/lib/view/widgets/instruments_stats.dart @@ -9,54 +9,85 @@ class Instrumentstats extends StatelessWidget { final double minValue; final double maxValue; final double avgValue; + final double? currentAltitude; - const Instrumentstats( - {super.key, - required this.unit, - required this.titleFontSize, - required this.avgValue, - required this.maxValue, - required this.minValue, - required this.statFontSize}); + const Instrumentstats({ + super.key, + required this.unit, + required this.titleFontSize, + required this.avgValue, + required this.maxValue, + required this.minValue, + required this.statFontSize, + this.currentAltitude, + }); @override Widget build(BuildContext context) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Center( - child: Text( - builtIn, - style: TextStyle( - color: blackTextColor, - fontSize: titleFontSize, - fontWeight: FontWeight.bold, + return LayoutBuilder( + builder: (context, constraints) { + final availableHeight = constraints.maxHeight; + final titleHeight = titleFontSize * 1.5; + final remainingHeight = availableHeight - titleHeight - 16; + + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + SizedBox( + height: titleHeight, + child: Center( + child: Text( + builtIn, + style: TextStyle( + color: blackTextColor, + fontSize: titleFontSize, + fontWeight: FontWeight.bold, + ), + ), + ), ), - ), - ), - Expanded( - child: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - StatItem( - label: '$maxLabel ($unit)', - value: maxValue, - fontSize: statFontSize), - StatItem( - label: '$minLabel ($unit)', - value: minValue, - fontSize: statFontSize), - StatItem( - label: '$avgLabel ($unit)', - value: avgValue, - fontSize: statFontSize), - ], + const SizedBox(height: 8), + Expanded( + child: SingleChildScrollView( + child: ConstrainedBox( + constraints: BoxConstraints( + minHeight: remainingHeight > 0 ? remainingHeight : 0, + ), + child: IntrinsicHeight( + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + StatItem( + label: '$maxLabel ($unit)', + value: maxValue, + fontSize: statFontSize, + ), + StatItem( + label: '$minLabel ($unit)', + value: minValue, + fontSize: statFontSize, + ), + StatItem( + label: '$avgLabel ($unit)', + value: avgValue, + fontSize: statFontSize, + ), + if (currentAltitude != null) + StatItem( + label: '$altitudeLabel ($meterUnit)', + value: currentAltitude!, + fontSize: statFontSize, + ), + ], + ), + ), + ), + ), ), - ), - ), - ], + ], + ); + }, ); } } @@ -66,50 +97,59 @@ class StatItem extends StatelessWidget { final double value; final double fontSize; - const StatItem( - {super.key, - required this.label, - required this.fontSize, - required this.value}); + const StatItem({ + super.key, + required this.label, + required this.fontSize, + required this.value, + }); @override Widget build(BuildContext context) { final screenWidth = MediaQuery.of(context).size.width; final padding = screenWidth < 400 ? 15.0 : 20.0; + return Flexible( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - Center( - child: Text( - label, - style: TextStyle( - color: cardContentColor, - fontSize: fontSize, - fontWeight: FontWeight.w600, - ), - ), - ), - const SizedBox(height: 4), - Center( - child: Container( - padding: EdgeInsets.symmetric(horizontal: padding, vertical: 3), - decoration: BoxDecoration( - border: Border.all(color: instrumentStatBoxColor), - borderRadius: BorderRadius.circular(6), - ), + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 2.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Center( child: Text( - value.toStringAsFixed(2), + label, style: TextStyle( color: cardContentColor, fontSize: fontSize, - fontWeight: FontWeight.bold, + fontWeight: FontWeight.w600, + ), + textAlign: TextAlign.center, + overflow: TextOverflow.ellipsis, + maxLines: 2, + ), + ), + const SizedBox(height: 4), + Center( + child: Container( + padding: EdgeInsets.symmetric(horizontal: padding, vertical: 3), + decoration: BoxDecoration( + border: Border.all(color: instrumentStatBoxColor), + borderRadius: BorderRadius.circular(6), + ), + child: Text( + value.toStringAsFixed(2), + style: TextStyle( + color: cardContentColor, + fontSize: fontSize, + fontWeight: FontWeight.bold, + ), + overflow: TextOverflow.ellipsis, ), ), ), - ), - ], + ], + ), ), ); } diff --git a/lib/view/widgets/luxmeter_card.dart b/lib/view/widgets/luxmeter_card.dart index 8dafb916a..da38ee921 100644 --- a/lib/view/widgets/luxmeter_card.dart +++ b/lib/view/widgets/luxmeter_card.dart @@ -44,31 +44,59 @@ class _LuxMeterCardState extends State { padding: EdgeInsets.all(cardPadding), child: LayoutBuilder( builder: (context, constraints) { - return Row( - children: [ - Expanded( - flex: screenWidth < 500 ? 40 : 35, - child: Instrumentstats( - titleFontSize: titleFontSize, - statFontSize: statFontSize, - maxValue: maxLux, - minValue: minLux, - avgValue: avgLux, - unit: 'Lx', + if (isLargeScreen) { + return Column( + children: [ + Expanded( + flex: 40, + child: GaugeWidget( + gaugeSize: gaugeSize, + currentValue: currentLux, + minValue: 0, + maxValue: 10000, + unit: 'Lx', + currentValueFontSize: luxValueFontSize), ), - ), - Expanded( - flex: screenWidth < 500 ? 60 : 65, - child: GaugeWidget( - gaugeSize: gaugeSize, - currentValue: currentLux, - minValue: 0, - maxValue: 10000, + Expanded( + flex: 60, + child: Instrumentstats( + titleFontSize: titleFontSize, + statFontSize: statFontSize, + maxValue: maxLux, + minValue: minLux, + avgValue: avgLux, unit: 'Lx', - currentValueFontSize: luxValueFontSize), - ), - ], - ); + ), + ), + ], + ); + } else { + return Row( + children: [ + Expanded( + flex: screenWidth < 500 ? 40 : 35, + child: Instrumentstats( + titleFontSize: titleFontSize, + statFontSize: statFontSize, + maxValue: maxLux, + minValue: minLux, + avgValue: avgLux, + unit: 'Lx', + ), + ), + Expanded( + flex: screenWidth < 500 ? 60 : 65, + child: GaugeWidget( + gaugeSize: gaugeSize, + currentValue: currentLux, + minValue: 0, + maxValue: 10000, + unit: 'Lx', + currentValueFontSize: luxValueFontSize), + ), + ], + ); + } }, ), ), diff --git a/lib/view/widgets/soundmeter_card.dart b/lib/view/widgets/soundmeter_card.dart index 3ed0619f2..3108b634d 100644 --- a/lib/view/widgets/soundmeter_card.dart +++ b/lib/view/widgets/soundmeter_card.dart @@ -45,31 +45,62 @@ class _SoundMeterCardState extends State { padding: EdgeInsets.all(cardPadding), child: LayoutBuilder( builder: (context, constraints) { - return Row( - children: [ - Expanded( - flex: screenWidth < 500 ? 40 : 35, - child: Instrumentstats( - titleFontSize: titleFontSize, - statFontSize: statFontSize, - maxValue: maxDb, - minValue: minDb, - avgValue: avgDb, - unit: db, + if (isLargeScreen) { + return Column( + children: [ + Expanded( + flex: 40, + child: Center( + child: GaugeWidget( + gaugeSize: gaugeSize, + currentValue: currentDb, + minValue: 0, + maxValue: 200, + unit: db, + currentValueFontSize: dbValueFontSize, + ), + ), ), - ), - Expanded( - flex: screenWidth < 500 ? 60 : 65, - child: GaugeWidget( - gaugeSize: gaugeSize, - currentValue: currentDb, - minValue: 0, - maxValue: 200, + Expanded( + flex: 60, + child: Instrumentstats( + titleFontSize: titleFontSize, + statFontSize: statFontSize, + maxValue: maxDb, + minValue: minDb, + avgValue: avgDb, unit: db, - currentValueFontSize: dbValueFontSize), - ), - ], - ); + ), + ), + ], + ); + } else { + return Row( + children: [ + Expanded( + flex: screenWidth < 500 ? 40 : 35, + child: Instrumentstats( + titleFontSize: titleFontSize, + statFontSize: statFontSize, + maxValue: maxDb, + minValue: minDb, + avgValue: avgDb, + unit: db, + ), + ), + Expanded( + flex: screenWidth < 500 ? 60 : 65, + child: GaugeWidget( + gaugeSize: gaugeSize, + currentValue: currentDb, + minValue: 0, + maxValue: 200, + unit: db, + currentValueFontSize: dbValueFontSize), + ), + ], + ); + } }, ), ),