-
Notifications
You must be signed in to change notification settings - Fork 816
feat: added configuration screen for luxmeter and stored settings #2769
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: flutter
Are you sure you want to change the base?
Conversation
Reviewer's GuideIntroduces a comprehensive configuration workflow for the LuxMeter instrument by adding a dedicated settings screen with reusable widgets, persisting user preferences via SharedPreferences, integrating a menu-driven navigation in the main LuxMeter UI, and wiring the configuration changes into the existing state provider. Sequence diagram for persisting and applying LuxMeter configuration changessequenceDiagram
actor User
participant LuxMeterConfigScreen
participant LuxMeterConfigProvider
participant SharedPreferences
participant LuxMeterStateProvider
User->LuxMeterConfigScreen: Changes a configuration value
LuxMeterConfigScreen->LuxMeterConfigProvider: Calls update method
LuxMeterConfigProvider->SharedPreferences: Saves config to storage
LuxMeterConfigProvider->LuxMeterStateProvider: Notifies listeners
LuxMeterStateProvider->LuxMeterConfigProvider: Reads updated config
LuxMeterStateProvider->LuxMeterScreen: Applies new configuration
Class diagram for LuxMeter configuration model and providerclassDiagram
class LuxMeterConfig {
+int updatePeriod
+int highLimit
+String activeSensor
+int sensorGain
+bool includeLocationData
+copyWith()
+toJson()
+fromJson()
}
class LuxMeterConfigProvider {
-LuxMeterConfig _config
+LuxMeterConfig get config
+updateConfig()
+updateUpdatePeriod()
+updateHighLimit()
+updateActiveSensor()
+updateSensorGain()
+updateIncludeLocationData()
+resetToDefaults()
}
LuxMeterConfigProvider --> LuxMeterConfig : manages
Class diagram for reusable configuration widgetsclassDiagram
class ConfigInputItem {
+String title
+String value
+TextEditingController controller
+Function(String) onChanged
+String? hint
}
class ConfigDropdownItem {
+String title
+String selectedValue
+List~ConfigOption~ options
+Function(String) onChanged
}
class ConfigCheckboxItem {
+String title
+String subtitle
+bool value
+Function(bool) onChanged
}
class ConfigOption {
+String value
+String displayName
}
ConfigDropdownItem --> ConfigOption : uses
File-Level Changes
Possibly linked issues
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey @Yugesh-Kumar-S - I've reviewed your changes and they look great!
Prompt for AI Agents
Please address the comments from this code review:
## Individual Comments
### Comment 1
<location> `lib/view/luxmeter_config_screen.dart:197` </location>
<code_context>
+ ),
+ TextButton(
+ onPressed: () {
+ onChanged(controller.text);
+ Navigator.of(context).pop();
+ },
</code_context>
<issue_to_address>
Dialog closes even if input is invalid.
Validate the input before closing the dialog so users can correct errors without needing to reopen it.
Suggested implementation:
```
TextButton(
onPressed: () {
final value = controller.text;
final intValue = int.tryParse(value);
if (intValue != null && intValue >= 10 && intValue <= 10000) {
onChanged(value);
Navigator.of(context).pop();
} else {
// Optionally, show an error message to the user
// You may want to setState to display an error below the TextField
// For now, do nothing so the dialog stays open
}
},
```
If you want to display an error message to the user when the input is invalid, you should add a state variable (e.g., `_inputError`) and update the dialog's content to show the error. This will require converting the dialog to a `StatefulBuilder` or extracting it to a `StatefulWidget`.
</issue_to_address>
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
Build successful. APKs to test: https://github.com/fossasia/pslab-android/actions/runs/16100874741/artifacts/3472510153 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hey @Yugesh-Kumar-S - I've reviewed your changes and they look great!
Prompt for AI Agents
Please address the comments from this code review:
## Individual Comments
### Comment 1
<location> `lib/view/luxmeter_screen.dart:56` </location>
<code_context>
}
+ void _showOptionsMenu() {
+ showMenu(
+ context: context,
+ position: RelativeRect.fromLTRB(
+ MediaQuery.of(context).size.width,
+ 0,
+ 0,
+ MediaQuery.of(context).size.height,
+ ),
+ items: [
+ PopupMenuItem(
+ value: 'show_logged_data',
+ child: Text(showLoggedData),
+ ),
+ PopupMenuItem(
+ value: 'lux_meter_config',
+ child: Text(showLuxmeterConfig),
+ ),
+ ],
+ elevation: 8,
+ ).then((value) {
+ if (value != null) {
+ switch (value) {
</code_context>
<issue_to_address>
Options menu positioning may not align with user expectations.
Using the screen's full width and height for `RelativeRect.fromLTRB` may cause the menu to appear in unexpected places on different devices. Anchor the menu to the options button's position for more consistent placement.
Suggested implementation:
```
class _LuxMeterScreenState extends State<LuxMeterScreen> {
late LuxMeterStateProvider _provider;
late LuxMeterConfigProvider _configProvider;
bool _showGuide = false;
static const imagePath = 'assets/images/bh1750_schematic.png';
final GlobalKey _optionsButtonKey = GlobalKey();
void _showInstrumentGuide() {
];
}
void _showOptionsMenu() {
final RenderBox button = _optionsButtonKey.currentContext!.findRenderObject() as RenderBox;
final RenderBox overlay = Overlay.of(context).context.findRenderObject() as RenderBox;
final Offset buttonPosition = button.localToGlobal(Offset.zero, ancestor: overlay);
final Size buttonSize = button.size;
showMenu(
context: context,
position: RelativeRect.fromLTRB(
buttonPosition.dx,
buttonPosition.dy + buttonSize.height,
buttonPosition.dx + buttonSize.width,
buttonPosition.dy,
),
items: [
PopupMenuItem(
value: 'show_logged_data',
child: Text(showLoggedData),
),
PopupMenuItem(
value: 'lux_meter_config',
child: Text(showLuxmeterConfig),
),
],
elevation: 8,
).then((value) {
if (value != null) {
switch (value) {
case 'show_logged_data':
// TODO
break;
case 'lux_meter_config':
_navigateToConfig();
break;
}
}
});
}
```
You must attach `_optionsButtonKey` to the widget (e.g., `IconButton`, `PopupMenuButton`, etc.) that triggers `_showOptionsMenu()`. For example:
```
IconButton(
key: _optionsButtonKey,
icon: Icon(Icons.more_vert),
onPressed: _showOptionsMenu,
)
```
This ensures the menu is anchored to the button's position.
</issue_to_address>
### Comment 2
<location> `lib/providers/luxmeter_state_provider.dart:33` </location>
<code_context>
+ void setConfigProvider(LuxMeterConfigProvider configProvider) {
+ _configProvider = configProvider;
+ _configProvider?.addListener(_onConfigChanged);
+ }
+
+ void _onConfigChanged() {
+ if (_configProvider != null) {
+ // TODO
</code_context>
<issue_to_address>
Config change handler is a placeholder.
Please implement this method to handle configuration changes if they impact sensor behavior or data collection.
</issue_to_address>
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
showMenu( | ||
context: context, | ||
position: RelativeRect.fromLTRB( | ||
MediaQuery.of(context).size.width, | ||
0, | ||
0, | ||
MediaQuery.of(context).size.height, | ||
), | ||
items: [ | ||
PopupMenuItem( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
suggestion: Options menu positioning may not align with user expectations.
Using the screen's full width and height for RelativeRect.fromLTRB
may cause the menu to appear in unexpected places on different devices. Anchor the menu to the options button's position for more consistent placement.
Suggested implementation:
class _LuxMeterScreenState extends State<LuxMeterScreen> {
late LuxMeterStateProvider _provider;
late LuxMeterConfigProvider _configProvider;
bool _showGuide = false;
static const imagePath = 'assets/images/bh1750_schematic.png';
final GlobalKey _optionsButtonKey = GlobalKey();
void _showInstrumentGuide() {
];
}
void _showOptionsMenu() {
final RenderBox button = _optionsButtonKey.currentContext!.findRenderObject() as RenderBox;
final RenderBox overlay = Overlay.of(context).context.findRenderObject() as RenderBox;
final Offset buttonPosition = button.localToGlobal(Offset.zero, ancestor: overlay);
final Size buttonSize = button.size;
showMenu(
context: context,
position: RelativeRect.fromLTRB(
buttonPosition.dx,
buttonPosition.dy + buttonSize.height,
buttonPosition.dx + buttonSize.width,
buttonPosition.dy,
),
items: [
PopupMenuItem(
value: 'show_logged_data',
child: Text(showLoggedData),
),
PopupMenuItem(
value: 'lux_meter_config',
child: Text(showLuxmeterConfig),
),
],
elevation: 8,
).then((value) {
if (value != null) {
switch (value) {
case 'show_logged_data':
// TODO
break;
case 'lux_meter_config':
_navigateToConfig();
break;
}
}
});
}
You must attach _optionsButtonKey
to the widget (e.g., IconButton
, PopupMenuButton
, etc.) that triggers _showOptionsMenu()
. For example:
IconButton(
key: _optionsButtonKey,
icon: Icon(Icons.more_vert),
onPressed: _showOptionsMenu,
)
This ensures the menu is anchored to the button's position.
providers: [ | ||
ChangeNotifierProvider<LuxMeterStateProvider>.value(value: _provider), | ||
ChangeNotifierProvider<LuxMeterConfigProvider>.value( | ||
value: _configProvider), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we need to do the initialization for the providers in three steps ? Can't we initialize them here:
providers: [
ChangeNotifierProvider(
create: (context) => LuxMeterStateProvider(),
),
ChangeNotifierProvider(
create: (context) => LuxMeterConfigProvider(),
),
],
builder: (context) => | ||
ChangeNotifierProvider<LuxMeterConfigProvider>.value( | ||
value: _configProvider, | ||
child: const LuxMeterConfigScreen(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Similar to what I've mentioned. Let's keep the concerns of the providers separated.
Part of #2768
Changes
Screenshots / Recordings
screen-20250705-005818.4.mp4
Checklist:
strings.xml
,dimens.xml
andcolors.xml
without hard coding any value.strings.xml
,dimens.xml
orcolors.xml
.Summary by Sourcery
Introduce a new configuration screen for the LuxMeter instrument with customizable settings persisted via SharedPreferences and integrated into the app navigation.
New Features:
Enhancements:
Build:
Summary by Sourcery
Provide a dedicated configuration screen for the LuxMeter instrument, implement persistence of user-adjustable settings using SharedPreferences, and integrate the configuration provider into the existing LuxMeter workflow and UI
New Features:
Enhancements:
Build: