From e8d309f57c07190e51f920f923d0c78952092b3f Mon Sep 17 00:00:00 2001 From: Yugesh-Kumar-S Date: Wed, 18 Jun 2025 17:07:27 +0530 Subject: [PATCH 1/5] Handled error in luxmeter and small ui changes --- lib/constants.dart | 6 + lib/providers/luxmeter_state_provider.dart | 78 ++++++-- lib/view/luxmeter_screen.dart | 210 ++++++++++++--------- 3 files changed, 196 insertions(+), 98 deletions(-) diff --git a/lib/constants.dart b/lib/constants.dart index d8f1e505c..4f983bb16 100644 --- a/lib/constants.dart +++ b/lib/constants.dart @@ -231,3 +231,9 @@ String soundMeterError = 'Sound sensor error:'; String soundMeterInitialError = 'Sound sensor initialization error:'; String db = 'dB'; String soundMeterTitle = 'Sound Meter'; +String luxMeterIOSError = "App doesn't have access to the light sensor"; +String luxMeterDesktopError = "Light sensor not supported on desktop"; +String luxMeterWebError = "Light sensor not supported on web"; +String luxMeterPlatformError = "Light sensor not supported on this platform"; +String noLightSensor = "Device does not have light sensor"; +String lightSensorErrorDetails = "Light sensor error details:"; diff --git a/lib/providers/luxmeter_state_provider.dart b/lib/providers/luxmeter_state_provider.dart index e2f182ab3..a33ef93a6 100644 --- a/lib/providers/luxmeter_state_provider.dart +++ b/lib/providers/luxmeter_state_provider.dart @@ -1,7 +1,7 @@ import 'dart:async'; import 'dart:math'; +import 'dart:io'; import 'package:fl_chart/fl_chart.dart'; -import 'package:flutter/material.dart'; import 'package:pslab/others/logger_service.dart'; import 'package:light/light.dart'; import 'package:flutter/foundation.dart'; @@ -22,33 +22,85 @@ class LuxMeterStateProvider extends ChangeNotifier { double _luxMax = 0; double _luxSum = 0; int _dataCount = 0; - void initializeSensors() { + bool _sensorAvailable = true; + + Function(String)? onSensorError; + + void initializeSensors({Function(String)? onError}) { + onSensorError = onError; + + if (!_isPlatformSupported()) { + _handleUnsupportedPlatform(); + return; + } + try { _light = Light(); _startTime = DateTime.now().millisecondsSinceEpoch / 1000.0; - _timeTimer = Timer.periodic(const Duration(seconds: 1), (timer) { - _currentTime = - (DateTime.now().millisecondsSinceEpoch / 1000.0) - _startTime; - _updateData(); - notifyListeners(); - }); + if (_sensorAvailable) { + _timeTimer = Timer.periodic(const Duration(seconds: 1), (timer) { + _currentTime = + (DateTime.now().millisecondsSinceEpoch / 1000.0) - _startTime; + if (_sensorAvailable) { + _updateData(); + } + notifyListeners(); + }); + } + _lightSubscription = _light!.lightSensorStream.listen( (int luxValue) { _currentLux = luxValue.toDouble(); + _sensorAvailable = true; notifyListeners(); }, onError: (error) { - logger.e( - "$lightSensorError $error", - ); + logger.e("$lightSensorError $error"); + _handleSensorError(error); }, - cancelOnError: true, + cancelOnError: false, ); } catch (e) { logger.e("$lightSensorInitialError $e"); + _handleSensorError(e); } } + bool _isPlatformSupported() { + return Platform.isAndroid; + } + + void _handleUnsupportedPlatform() { + _sensorAvailable = false; + if (Platform.isIOS) { + onSensorError?.call(luxMeterIOSError); + } else if (Platform.isWindows || Platform.isMacOS || Platform.isLinux) { + onSensorError?.call(luxMeterDesktopError); + } else if (kIsWeb) { + onSensorError?.call(luxMeterWebError); + } else { + onSensorError?.call(luxMeterPlatformError); + } + } + + void _handleSensorError(dynamic error) { + _sensorAvailable = false; + + if (!_isPlatformSupported()) { + _handleUnsupportedPlatform(); + return; + } + + _handleSensorNotAvailable(); + + logger.e("$lightSensorErrorDetails $error"); + } + + void _handleSensorNotAvailable() { + _sensorAvailable = false; + onSensorError?.call(noLightSensor); + } + void disposeSensors() { _lightSubscription?.cancel(); _timeTimer?.cancel(); @@ -61,6 +113,8 @@ class LuxMeterStateProvider extends ChangeNotifier { } void _updateData() { + if (!_sensorAvailable) return; + final lux = _currentLux; final time = _currentTime; _luxData.add(lux); diff --git a/lib/view/luxmeter_screen.dart b/lib/view/luxmeter_screen.dart index 16288eed1..94ae79e23 100644 --- a/lib/view/luxmeter_screen.dart +++ b/lib/view/luxmeter_screen.dart @@ -13,14 +13,46 @@ class LuxMeterScreen extends StatefulWidget { } class _LuxMeterScreenState extends State { + late LuxMeterStateProvider _provider; + @override - Widget build(BuildContext context) { - return MultiProvider( - providers: [ - ChangeNotifierProvider( - create: (_) => LuxMeterStateProvider()..initializeSensors(), + void initState() { + super.initState(); + _provider = LuxMeterStateProvider(); + WidgetsBinding.instance.addPostFrameCallback((_) { + if (mounted) { + _provider.initializeSensors(onError: _showSensorErrorSnackbar); + } + }); + } + + @override + void dispose() { + _provider.disposeSensors(); + _provider.dispose(); + super.dispose(); + } + + void _showSensorErrorSnackbar(String message) { + if (mounted) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text( + message, + style: const TextStyle(color: Colors.white), + ), + backgroundColor: Colors.grey[700], + duration: const Duration(seconds: 4), + behavior: SnackBarBehavior.floating, ), - ], + ); + } + } + + @override + Widget build(BuildContext context) { + return ChangeNotifierProvider.value( + value: _provider, child: CommonScaffold( title: luxMeterTitle, body: SafeArea( @@ -46,6 +78,7 @@ class _LuxMeterScreenState extends State { builder: (context, provider, child) { final screenWidth = MediaQuery.of(context).size.width; final cardMargin = screenWidth < 400 ? 8.0 : 16.0; + final cardPadding = screenWidth < 400 ? 2.0 : 5.0; List spots = provider.getLuxChartData(); double maxLux = provider.getMaxLux(); double maxTime = provider.getMaxTime(); @@ -54,10 +87,10 @@ class _LuxMeterScreenState extends State { return Container( margin: EdgeInsets.fromLTRB(cardMargin, 0, cardMargin, cardMargin), - padding: EdgeInsets.all(cardMargin), + padding: EdgeInsets.all(cardPadding), decoration: BoxDecoration( color: Colors.black, - borderRadius: BorderRadius.circular(8), + borderRadius: BorderRadius.zero, ), child: _buildChart( screenWidth, maxLux, maxTime, minTime, timeInterval, spots), @@ -108,97 +141,102 @@ class _LuxMeterScreenState extends State { : 10.0; final axisNameFontSize = screenWidth < 400 ? 9.0 : 10.0; final reservedSizeBottom = screenWidth < 400 ? 25.0 : 30.0; - final reservedSizeLeft = screenWidth < 400 ? 20.0 : 25.0; - return LineChart( - LineChartData( - backgroundColor: Colors.black, - titlesData: FlTitlesData( - show: true, - topTitles: AxisTitles( - axisNameWidget: Padding( - padding: EdgeInsets.only(left: screenWidth < 400 ? 15 : 25), - child: Text( - timeAxisLabel, + final reservedSizeLeft = screenWidth < 400 ? 27.0 : 30.0; + final reservedSizeRight = screenWidth < 400 ? 27.0 : 30.0; + return Padding( + padding: const EdgeInsets.only(right: 20.0), + child: LineChart( + LineChartData( + backgroundColor: Colors.black, + titlesData: FlTitlesData( + show: true, + topTitles: AxisTitles( + axisNameWidget: Padding( + padding: EdgeInsets.only(left: screenWidth < 400 ? 15 : 25), + child: Text( + timeAxisLabel, + style: TextStyle( + fontSize: axisNameFontSize, + color: Colors.white, + fontWeight: FontWeight.bold, + ), + ), + ), + axisNameSize: screenWidth < 400 ? 18 : 20, + ), + bottomTitles: AxisTitles( + sideTitles: SideTitles( + showTitles: true, + reservedSize: reservedSizeBottom, + getTitlesWidget: sideTitleWidgets, + interval: timeInterval, + ), + ), + leftTitles: AxisTitles( + axisNameWidget: Text( + lx, style: TextStyle( fontSize: axisNameFontSize, color: Colors.white, fontWeight: FontWeight.bold, ), ), - ), - axisNameSize: screenWidth < 400 ? 18 : 20, - ), - bottomTitles: AxisTitles( - sideTitles: SideTitles( - showTitles: true, - reservedSize: reservedSizeBottom, - getTitlesWidget: sideTitleWidgets, - interval: timeInterval, - ), - ), - leftTitles: AxisTitles( - axisNameWidget: Text( - lx, - style: TextStyle( - fontSize: axisNameFontSize, - color: Colors.white, - fontWeight: FontWeight.bold, + sideTitles: SideTitles( + reservedSize: reservedSizeLeft, + showTitles: true, + getTitlesWidget: (value, meta) { + return SideTitleWidget( + meta: meta, + child: Text( + value.toInt().toString(), + style: TextStyle( + color: Colors.white, + fontSize: chartFontSize, + ), + ), + ); + }, + interval: maxLux > 0 ? (maxLux / 5).ceilToDouble() : 10, ), ), - sideTitles: SideTitles( - reservedSize: reservedSizeLeft, - showTitles: true, - getTitlesWidget: (value, meta) { - return SideTitleWidget( - meta: meta, - child: Text( - value.toInt().toString(), - style: TextStyle( - color: Colors.white, - fontSize: chartFontSize, - ), - ), - ); - }, - interval: maxLux > 0 ? (maxLux / 5).ceilToDouble() : 10, + rightTitles: AxisTitles( + sideTitles: SideTitles( + showTitles: false, reservedSize: reservedSizeRight), ), ), - rightTitles: const AxisTitles( - sideTitles: SideTitles(showTitles: false), + gridData: FlGridData( + show: true, + drawHorizontalLine: true, + drawVerticalLine: true, + horizontalInterval: maxLux > 0 ? (maxLux / 5).ceilToDouble() : 10, + verticalInterval: timeInterval, ), - ), - gridData: FlGridData( - show: true, - drawHorizontalLine: true, - drawVerticalLine: true, - horizontalInterval: maxLux > 0 ? (maxLux / 5).ceilToDouble() : 10, - verticalInterval: timeInterval, - ), - borderData: FlBorderData( - show: true, - border: const Border( - bottom: BorderSide(color: Colors.white38), - left: BorderSide(color: Colors.white38), - top: BorderSide(color: Colors.white38), - right: BorderSide(color: Colors.white38), + borderData: FlBorderData( + show: true, + border: const Border( + bottom: BorderSide(color: Colors.white38), + left: BorderSide(color: Colors.white38), + top: BorderSide(color: Colors.white38), + right: BorderSide(color: Colors.white38), + ), ), + minY: 0, + maxY: maxLux > 0 ? (maxLux * 1.1) : 100, + maxX: maxTime > 0 ? maxTime : 10, + minX: minTime, + clipData: const FlClipData.all(), + lineBarsData: [ + LineChartBarData( + spots: spots, + isCurved: true, + color: Colors.cyan, + barWidth: screenWidth < 400 ? 1.5 : 2.0, + isStrokeCapRound: true, + dotData: const FlDotData(show: false), + belowBarData: BarAreaData(show: false), + ), + ], ), - minY: 0, - maxY: maxLux > 0 ? (maxLux * 1.1) : 100, - maxX: maxTime > 0 ? maxTime : 10, - minX: minTime, - clipData: const FlClipData.all(), - lineBarsData: [ - LineChartBarData( - spots: spots, - isCurved: true, - color: Colors.cyan, - barWidth: screenWidth < 400 ? 1.5 : 2.0, - isStrokeCapRound: true, - dotData: const FlDotData(show: false), - belowBarData: BarAreaData(show: false), - ), - ], ), ); } From ea93d8615116a22ee3d6930f678dfcc1cc019364 Mon Sep 17 00:00:00 2001 From: Yugesh-Kumar-S Date: Wed, 18 Jun 2025 17:31:44 +0530 Subject: [PATCH 2/5] changes --- lib/providers/luxmeter_state_provider.dart | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/providers/luxmeter_state_provider.dart b/lib/providers/luxmeter_state_provider.dart index a33ef93a6..bbbaf839d 100644 --- a/lib/providers/luxmeter_state_provider.dart +++ b/lib/providers/luxmeter_state_provider.dart @@ -41,9 +41,7 @@ class LuxMeterStateProvider extends ChangeNotifier { _timeTimer = Timer.periodic(const Duration(seconds: 1), (timer) { _currentTime = (DateTime.now().millisecondsSinceEpoch / 1000.0) - _startTime; - if (_sensorAvailable) { - _updateData(); - } + _updateData(); notifyListeners(); }); } From f66b5fee886a7408eb32e755e3e39fb261fa9767 Mon Sep 17 00:00:00 2001 From: Yugesh Kumar Date: Wed, 18 Jun 2025 18:50:41 +0530 Subject: [PATCH 3/5] Update lib/constants.dart Co-authored-by: Padmal --- lib/constants.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/constants.dart b/lib/constants.dart index 4f983bb16..ec664b106 100644 --- a/lib/constants.dart +++ b/lib/constants.dart @@ -235,5 +235,5 @@ String luxMeterIOSError = "App doesn't have access to the light sensor"; String luxMeterDesktopError = "Light sensor not supported on desktop"; String luxMeterWebError = "Light sensor not supported on web"; String luxMeterPlatformError = "Light sensor not supported on this platform"; -String noLightSensor = "Device does not have light sensor"; +String noLightSensor = "Device does not have a light sensor"; String lightSensorErrorDetails = "Light sensor error details:"; From 1fefd0d002dc583e04f8faf58c3d9aaf1a365290 Mon Sep 17 00:00:00 2001 From: Yugesh-Kumar-S Date: Thu, 26 Jun 2025 16:26:18 +0530 Subject: [PATCH 4/5] Simplified error handling --- lib/constants.dart | 6 +- lib/providers/luxmeter_state_provider.dart | 65 +++++++--------------- 2 files changed, 21 insertions(+), 50 deletions(-) diff --git a/lib/constants.dart b/lib/constants.dart index c9106dcd1..8f1f308a9 100644 --- a/lib/constants.dart +++ b/lib/constants.dart @@ -256,9 +256,7 @@ String soundMeterError = 'Sound sensor error:'; String soundMeterInitialError = 'Sound sensor initialization error:'; String db = 'dB'; String soundMeterTitle = 'Sound Meter'; -String luxMeterIOSError = "App doesn't have access to the light sensor"; -String luxMeterDesktopError = "Light sensor not supported on desktop"; -String luxMeterWebError = "Light sensor not supported on web"; -String luxMeterPlatformError = "Light sensor not supported on this platform"; String noLightSensor = "Device does not have a light sensor"; String lightSensorErrorDetails = "Light sensor error details:"; +String lightSensorErrorLog = + "No light sensor data received - sensor may not be available"; diff --git a/lib/providers/luxmeter_state_provider.dart b/lib/providers/luxmeter_state_provider.dart index bbbaf839d..1788d6e89 100644 --- a/lib/providers/luxmeter_state_provider.dart +++ b/lib/providers/luxmeter_state_provider.dart @@ -1,6 +1,5 @@ import 'dart:async'; import 'dart:math'; -import 'dart:io'; import 'package:fl_chart/fl_chart.dart'; import 'package:pslab/others/logger_service.dart'; import 'package:light/light.dart'; @@ -22,38 +21,41 @@ class LuxMeterStateProvider extends ChangeNotifier { double _luxMax = 0; double _luxSum = 0; int _dataCount = 0; - bool _sensorAvailable = true; + bool _sensorAvailable = false; Function(String)? onSensorError; void initializeSensors({Function(String)? onError}) { onSensorError = onError; - if (!_isPlatformSupported()) { - _handleUnsupportedPlatform(); - return; - } - try { _light = Light(); _startTime = DateTime.now().millisecondsSinceEpoch / 1000.0; - if (_sensorAvailable) { - _timeTimer = Timer.periodic(const Duration(seconds: 1), (timer) { - _currentTime = - (DateTime.now().millisecondsSinceEpoch / 1000.0) - _startTime; - _updateData(); - notifyListeners(); - }); - } + _timeTimer = Timer.periodic(const Duration(seconds: 1), (timer) { + _currentTime = + (DateTime.now().millisecondsSinceEpoch / 1000.0) - _startTime; + _updateData(); + notifyListeners(); + }); + + Timer sensorTimeout = Timer(const Duration(seconds: 3), () { + if (!_sensorAvailable) { + _handleSensorError(lightSensorErrorLog); + } + }); _lightSubscription = _light!.lightSensorStream.listen( (int luxValue) { _currentLux = luxValue.toDouble(); - _sensorAvailable = true; + if (!_sensorAvailable) { + _sensorAvailable = true; + sensorTimeout.cancel(); + } notifyListeners(); }, onError: (error) { logger.e("$lightSensorError $error"); + sensorTimeout.cancel(); _handleSensorError(error); }, cancelOnError: false, @@ -64,39 +66,10 @@ class LuxMeterStateProvider extends ChangeNotifier { } } - bool _isPlatformSupported() { - return Platform.isAndroid; - } - - void _handleUnsupportedPlatform() { - _sensorAvailable = false; - if (Platform.isIOS) { - onSensorError?.call(luxMeterIOSError); - } else if (Platform.isWindows || Platform.isMacOS || Platform.isLinux) { - onSensorError?.call(luxMeterDesktopError); - } else if (kIsWeb) { - onSensorError?.call(luxMeterWebError); - } else { - onSensorError?.call(luxMeterPlatformError); - } - } - void _handleSensorError(dynamic error) { _sensorAvailable = false; - - if (!_isPlatformSupported()) { - _handleUnsupportedPlatform(); - return; - } - - _handleSensorNotAvailable(); - - logger.e("$lightSensorErrorDetails $error"); - } - - void _handleSensorNotAvailable() { - _sensorAvailable = false; onSensorError?.call(noLightSensor); + logger.e("$lightSensorErrorDetails $error"); } void disposeSensors() { From 31a390e70a75fa4f0cfe846ae4b33a6af55b696a Mon Sep 17 00:00:00 2001 From: Yugesh-Kumar-S Date: Sat, 28 Jun 2025 23:17:10 +0530 Subject: [PATCH 5/5] refactored colors --- lib/theme/colors.dart | 2 ++ lib/view/luxmeter_screen.dart | 6 +++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/theme/colors.dart b/lib/theme/colors.dart index d12969b32..c1c8360fc 100644 --- a/lib/theme/colors.dart +++ b/lib/theme/colors.dart @@ -40,3 +40,5 @@ Color selectedMenuColor = Colors.red; Color menuColor = Colors.grey; Color listTileFocusColor = Colors.grey[350]!; Color oscilloscopeOptionLabelColor = Colors.black; +Color snackBarBackgroundColor = Colors.grey[700]!; +Color snackBarContentColor = Colors.white; diff --git a/lib/view/luxmeter_screen.dart b/lib/view/luxmeter_screen.dart index 4ef1fe256..04638e2c6 100644 --- a/lib/view/luxmeter_screen.dart +++ b/lib/view/luxmeter_screen.dart @@ -41,9 +41,9 @@ class _LuxMeterScreenState extends State { SnackBar( content: Text( message, - style: const TextStyle(color: Colors.white), + style: TextStyle(color: snackBarContentColor), ), - backgroundColor: Colors.grey[700], + backgroundColor: snackBarBackgroundColor, duration: const Duration(seconds: 4), behavior: SnackBarBehavior.floating, ), @@ -231,7 +231,7 @@ class _LuxMeterScreenState extends State { LineChartBarData( spots: spots, isCurved: true, - color: Colors.cyan, + color: chartLineColor, barWidth: screenWidth < 400 ? 1.5 : 2.0, isStrokeCapRound: true, dotData: const FlDotData(show: false),