Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions lib/constants.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Comment thread
Yugesh-Kumar-S marked this conversation as resolved.
Outdated
String lightSensorErrorDetails = "Light sensor error details:";
76 changes: 64 additions & 12 deletions lib/providers/luxmeter_state_provider.dart
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -22,33 +22,83 @@ 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;
_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;
Comment thread
Yugesh-Kumar-S marked this conversation as resolved.
Outdated
}

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();
Expand All @@ -61,6 +111,8 @@ class LuxMeterStateProvider extends ChangeNotifier {
}

void _updateData() {
if (!_sensorAvailable) return;

final lux = _currentLux;
final time = _currentTime;
_luxData.add(lux);
Expand Down
210 changes: 124 additions & 86 deletions lib/view/luxmeter_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,46 @@ class LuxMeterScreen extends StatefulWidget {
}

class _LuxMeterScreenState extends State<LuxMeterScreen> {
late LuxMeterStateProvider _provider;

@override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider<LuxMeterStateProvider>(
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<LuxMeterStateProvider>.value(
value: _provider,
child: CommonScaffold(
title: luxMeterTitle,
body: SafeArea(
Expand All @@ -46,6 +78,7 @@ class _LuxMeterScreenState extends State<LuxMeterScreen> {
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<FlSpot> spots = provider.getLuxChartData();
double maxLux = provider.getMaxLux();
double maxTime = provider.getMaxTime();
Expand All @@ -54,10 +87,10 @@ class _LuxMeterScreenState extends State<LuxMeterScreen> {

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),
Expand Down Expand Up @@ -108,97 +141,102 @@ class _LuxMeterScreenState extends State<LuxMeterScreen> {
: 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(
Comment thread
Yugesh-Kumar-S marked this conversation as resolved.
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),
),
],
),
);
}
Expand Down