diameter/lib/settings.dart

575 lines
26 KiB
Dart

import 'package:diameter/components/forms/boolean_form_field.dart';
import 'package:diameter/components/forms/number_form_field.dart';
import 'package:diameter/localization_keys.dart';
import 'package:diameter/utils/dialog_utils.dart';
import 'package:diameter/components/forms/auto_complete_dropdown_button.dart';
import 'package:diameter/models/settings.dart';
import 'package:diameter/navigation.dart';
import 'package:diameter/utils/utils.dart';
import 'package:flutter/material.dart';
import 'package:flutter_translate/flutter_translate.dart';
import 'package:intl/intl.dart';
class SettingsScreen extends StatefulWidget {
static const String routeName = '/settings';
const SettingsScreen({Key? key}) : super(key: key);
@override
_SettingsScreenState createState() => _SettingsScreenState();
}
class _SettingsScreenState extends State<SettingsScreen> {
late Settings _settings;
final ScrollController _scrollController = ScrollController();
bool _measurementsIsExpanded = true;
bool _promptsIsExpanded = true;
bool _formatIsExpanded = true;
final _nutritionMeasurementLabelController = TextEditingController(text: '');
final _glucoseMeasurementLabelController = TextEditingController(text: '');
final _dateFormatController = TextEditingController(text: '');
final _longDateFormatController = TextEditingController(text: '');
final _timeFormatController = TextEditingController(text: '');
final _longTimeFormatController = TextEditingController(text: '');
final _insulinIncrementsController = TextEditingController(text: '');
final _nutritionIncrementsController = TextEditingController(text: '');
final _mmolPerLIncrementsController = TextEditingController(text: '');
final _targetGlucoseMgPerDlController = TextEditingController(text: '');
final _targetGlucoseMmolPerLController = TextEditingController(text: '');
late bool _onlyDisplayActiveGlucoseMeasurement;
late bool _displayBothGlucoseMeasurementsInDetailView;
late bool _displayBothGlucoseMeasurementsInListView;
late bool _showConfirmationDialogOnCancel;
late bool _showConfirmationDialogOnDelete;
late bool _showConfirmationDialogOnStopEvent;
@override
void initState() {
super.initState();
_settings = Settings.get();
_nutritionMeasurementLabelController.text =
nutritionMeasurementLabels[_settings.nutritionMeasurementIndex];
_glucoseMeasurementLabelController.text =
glucoseMeasurementLabels[_settings.glucoseMeasurementIndex];
_insulinIncrementsController.text = _settings.insulinIncrements.toString();
_nutritionIncrementsController.text =
_settings.nutritionIncrements.toString();
_mmolPerLIncrementsController.text =
_settings.mmolPerLIncrements.toString();
_targetGlucoseMgPerDlController.text =
_settings.targetGlucoseMgPerDl.toInt().toString();
_targetGlucoseMmolPerLController.text =
_settings.targetGlucoseMmolPerL.toString();
_onlyDisplayActiveGlucoseMeasurement = _settings.glucoseDisplayModeIndex ==
GlucoseDisplayMode.activeOnly.index;
_displayBothGlucoseMeasurementsInDetailView =
_settings.glucoseDisplayModeIndex == GlucoseDisplayMode.both.index ||
_settings.glucoseDisplayModeIndex ==
GlucoseDisplayMode.bothForDetail.index;
_displayBothGlucoseMeasurementsInListView =
_settings.glucoseDisplayModeIndex == GlucoseDisplayMode.both.index ||
_settings.glucoseDisplayModeIndex ==
GlucoseDisplayMode.bothForList.index;
_dateFormatController.text = _settings.dateFormat;
_longDateFormatController.text = _settings.longDateFormat ?? '';
_timeFormatController.text = _settings.timeFormat;
_longTimeFormatController.text = _settings.longTimeFormat ?? '';
_showConfirmationDialogOnCancel = _settings.showConfirmationDialogOnCancel;
_showConfirmationDialogOnDelete = _settings.showConfirmationDialogOnDelete;
_showConfirmationDialogOnStopEvent =
_settings.showConfirmationDialogOnStopEvent;
}
@override
void dispose() {
_scrollController.dispose();
_nutritionMeasurementLabelController.dispose();
_glucoseMeasurementLabelController.dispose();
_dateFormatController.dispose();
_longDateFormatController.dispose();
_timeFormatController.dispose();
_longTimeFormatController.dispose();
_insulinIncrementsController.dispose();
_nutritionIncrementsController.dispose();
_mmolPerLIncrementsController.dispose();
_targetGlucoseMgPerDlController.dispose();
_targetGlucoseMmolPerLController.dispose();
super.dispose();
}
void reload({String? message}) {
setState(() {
_settings = Settings.get();
});
setState(() {
if (message != null) {
var snackBar = SnackBar(
content: Text(message),
duration: const Duration(seconds: 2),
);
ScaffoldMessenger.of(context)
..removeCurrentSnackBar()
..showSnackBar(snackBar);
}
});
}
void saveSettings() {
Settings.put(Settings(
id: _settings.id,
nutritionMeasurementIndex: _nutritionMeasurementLabelController.text != '' ? nutritionMeasurementLabels
.indexOf(_nutritionMeasurementLabelController.text) : 0,
glucoseMeasurementIndex: _glucoseMeasurementLabelController.text != '' ? glucoseMeasurementLabels
.indexOf(_glucoseMeasurementLabelController.text) : 0,
glucoseDisplayModeIndex: _onlyDisplayActiveGlucoseMeasurement
? GlucoseDisplayMode.activeOnly.index
: _displayBothGlucoseMeasurementsInDetailView &&
_displayBothGlucoseMeasurementsInListView
? GlucoseDisplayMode.both.index
: _displayBothGlucoseMeasurementsInDetailView
? GlucoseDisplayMode.bothForDetail.index
: GlucoseDisplayMode.bothForList.index,
targetGlucoseMgPerDl:
int.tryParse(_targetGlucoseMgPerDlController.text) ?? _settings.targetGlucoseMgPerDl,
targetGlucoseMmolPerL:
double.tryParse(_targetGlucoseMmolPerLController.text) ?? _settings.targetGlucoseMmolPerL,
insulinIncrements:
double.tryParse(_insulinIncrementsController.text) ?? _settings.insulinIncrements,
nutritionIncrements:
double.tryParse(_nutritionIncrementsController.text) ?? _settings.nutritionIncrements,
mmolPerLIncrements:
double.tryParse(_mmolPerLIncrementsController.text) ?? _settings.mmolPerLIncrements,
dateFormat: _dateFormatController.text,
longDateFormat: _longDateFormatController.text,
timeFormat: _timeFormatController.text,
longTimeFormat: _longTimeFormatController.text,
showConfirmationDialogOnCancel: _showConfirmationDialogOnCancel,
showConfirmationDialogOnDelete: _showConfirmationDialogOnDelete,
showConfirmationDialogOnStopEvent: _showConfirmationDialogOnStopEvent,
));
reload(message: translate(LocalizationKeys.settings_updated));
}
void onReset() {
Settings.reset();
reload(message: translate(LocalizationKeys.settings_reset));
}
void handleResetAction() async {
DialogUtils.showConfirmationDialog(
context: context,
onConfirm: onReset,
message: translate(LocalizationKeys.settings_confirmReset),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(translate(LocalizationKeys.settings_title)),
),
drawer: const Navigation(currentLocation: SettingsScreen.routeName),
body: SingleChildScrollView(
controller: _scrollController,
padding: const EdgeInsets.all(10.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Padding(
padding: const EdgeInsets.only(bottom: 10.0),
child: GestureDetector(
onTap: () => setState(() {
_measurementsIsExpanded = !_measurementsIsExpanded;
}),
child: Row(
children: [
Expanded(
child: Text(
translate(LocalizationKeys.settings_sections_measurements).toUpperCase(),
style: Theme.of(context).textTheme.subtitle2,
),
),
Icon(_measurementsIsExpanded
? Icons.expand_less
: Icons.expand_more),
],
),
),
),
Column(
children: _measurementsIsExpanded
? [
AutoCompleteDropdownButton<String>(
controller: _nutritionMeasurementLabelController,
selectedItem: _nutritionMeasurementLabelController.text,
label: translate(LocalizationKeys.settings_fields_nutritionMeasurement),
items: nutritionMeasurementLabels,
onChanged: (value) {
_nutritionMeasurementLabelController.text =
value ?? '';
saveSettings();
},
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 10.0),
child: AutoCompleteDropdownButton<String>(
controller: _glucoseMeasurementLabelController,
selectedItem: _glucoseMeasurementLabelController.text,
label: translate(LocalizationKeys.settings_fields_glucoseMeasurement),
items: glucoseMeasurementLabels,
onChanged: (value) {
_glucoseMeasurementLabelController.text =
value ?? '';
saveSettings();
},
),
),
Settings.glucoseMeasurement == GlucoseMeasurement.mgPerDl
? NumberFormField(
label: translate(LocalizationKeys.settings_fields_targetGlucose),
suffix: Settings.glucoseMeasurementSuffix,
controller: _targetGlucoseMgPerDlController,
showSteppers: false,
onChanged: (_) async {
await Future.delayed(
const Duration(seconds: 1));
if (Settings.glucoseMeasurement ==
GlucoseMeasurement.mgPerDl) {
final value = int.tryParse(
_targetGlucoseMgPerDlController.text);
_targetGlucoseMmolPerLController.text = Utils
.toStringMatchingTemplateFractionPrecision(
Utils.convertMgPerDlToMmolPerL(value ?? 0),
Settings.mmolPerLSteps);
await Future.delayed(
const Duration(seconds: 1));
saveSettings();
}
},
)
: NumberFormField(
label: translate(LocalizationKeys.settings_fields_targetGlucose),
suffix: Settings.glucoseMeasurementSuffix,
controller: _targetGlucoseMmolPerLController,
showSteppers: false,
onChanged: (_) async {
await Future.delayed(
const Duration(seconds: 1));
if (Settings.glucoseMeasurement ==
GlucoseMeasurement.mmolPerL) {
final value = double.tryParse(
_targetGlucoseMmolPerLController.text);
_targetGlucoseMgPerDlController.text =
Utils.convertMmolPerLToMgPerDl(value ?? 0)
.toString();
saveSettings();
}
},
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 10.0),
child: NumberFormField(
controller: _insulinIncrementsController,
showSteppers: false,
label: translate(LocalizationKeys.settings_fields_insulinIncrement),
onChanged: (value) {
_insulinIncrementsController.text =
(value ?? 0).toString();
saveSettings();
}),
),
NumberFormField(
controller: _nutritionIncrementsController,
showSteppers: false,
label: translate(LocalizationKeys.settings_fields_nutritionIncrement),
onChanged: (value) {
_nutritionIncrementsController.text =
(value ?? 0).toString();
saveSettings();
}),
Padding(
padding: const EdgeInsets.symmetric(vertical: 10.0),
child: NumberFormField(
controller: _mmolPerLIncrementsController,
showSteppers: false,
label: translate(LocalizationKeys.settings_fields_mmolLIncrement),
onChanged: (value) {
_mmolPerLIncrementsController.text =
(value ?? 0).toString();
saveSettings();
}),
),
BooleanFormField(
value: _onlyDisplayActiveGlucoseMeasurement,
label: translate(LocalizationKeys.settings_fields_onlyDisplayActive),
onChanged: (value) {
_onlyDisplayActiveGlucoseMeasurement = value;
saveSettings();
},
),
BooleanFormField(
value: _displayBothGlucoseMeasurementsInDetailView,
enabled: !_onlyDisplayActiveGlucoseMeasurement,
label:
translate(LocalizationKeys.settings_fields_displayBothDetail),
onChanged: (value) {
_displayBothGlucoseMeasurementsInDetailView = value;
saveSettings();
},
),
BooleanFormField(
value: _displayBothGlucoseMeasurementsInListView,
enabled: !_onlyDisplayActiveGlucoseMeasurement,
label: translate(LocalizationKeys.settings_fields_displayBothList),
onChanged: (value) {
_displayBothGlucoseMeasurementsInListView = value;
saveSettings();
},
),
]
: [],
),
const Divider(),
Padding(
padding: const EdgeInsets.only(bottom: 10.0),
child: GestureDetector(
onTap: () => setState(() {
_promptsIsExpanded = !_promptsIsExpanded;
}),
child: Row(
mainAxisSize: MainAxisSize.max,
children: [
Expanded(
child: Text(
translate(LocalizationKeys.settings_sections_confirmation).toUpperCase(),
style: Theme.of(context).textTheme.subtitle2,
),
),
Icon(_promptsIsExpanded
? Icons.expand_less
: Icons.expand_more),
],
),
),
),
Column(
children: _promptsIsExpanded
? [
BooleanFormField(
value: _showConfirmationDialogOnCancel,
label:
translate(LocalizationKeys.settings_fields_confirmOnCancel),
onChanged: (value) {
_showConfirmationDialogOnCancel = value;
saveSettings();
},
),
BooleanFormField(
value: _showConfirmationDialogOnDelete,
label: translate(LocalizationKeys.settings_fields_confirmOnDelete),
onChanged: (value) {
_showConfirmationDialogOnDelete = value;
saveSettings();
},
),
BooleanFormField(
value: _showConfirmationDialogOnStopEvent,
label: translate(LocalizationKeys.settings_fields_confirmOnEndEvent),
onChanged: (value) {
_showConfirmationDialogOnStopEvent = value;
saveSettings();
},
),
]
: [],
),
const Divider(),
Padding(
padding: const EdgeInsets.only(bottom: 10.0),
child: GestureDetector(
onTap: () => setState(() {
_formatIsExpanded = !_formatIsExpanded;
}),
child: Row(
mainAxisSize: MainAxisSize.max,
children: [
Expanded(
child: Text(
translate(LocalizationKeys.settings_sections_dateTimeFormat).toUpperCase(),
style: Theme.of(context).textTheme.subtitle2,
),
),
Icon(_formatIsExpanded
? Icons.expand_less
: Icons.expand_more),
],
),
),
),
Column(
children: _formatIsExpanded
? [
Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Expanded(
child: TextFormField(
controller: _dateFormatController,
decoration: InputDecoration(
labelText: translate(LocalizationKeys.settings_fields_dateFormat),
),
),
),
Expanded(
child: Padding(
padding: const EdgeInsets.only(
left: 5.0, bottom: 10.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(translate(LocalizationKeys.general_example), textScaleFactor: 0.75),
Text(
DateFormat(_dateFormatController.text)
.format(DateTime.now()),
textScaleFactor: 1.25,
),
],
),
),
),
],
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 10.0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Expanded(
child: TextFormField(
controller: _longDateFormatController,
decoration: InputDecoration(
labelText: translate(LocalizationKeys.settings_fields_longDateFormat),
),
),
),
Expanded(
child: Padding(
padding: const EdgeInsets.only(
left: 5.0, bottom: 10.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(translate(LocalizationKeys.general_example),
textScaleFactor: 0.75),
Text(
DateFormat(_longDateFormatController.text)
.format(DateTime.now()),
textScaleFactor: 1.25,
),
],
),
),
),
],
),
),
Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Expanded(
child: TextFormField(
controller: _timeFormatController,
decoration: InputDecoration(
labelText: translate(LocalizationKeys.settings_fields_timeFormat),
),
),
),
Expanded(
child: Padding(
padding: const EdgeInsets.only(
left: 5.0, bottom: 10.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(translate(LocalizationKeys.general_example), textScaleFactor: 0.75),
Text(
DateFormat(_timeFormatController.text)
.format(DateTime.now()),
textScaleFactor: 1.25,
),
],
),
),
),
],
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 10.0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Expanded(
child: TextFormField(
controller: _longTimeFormatController,
decoration: InputDecoration(
labelText: translate(LocalizationKeys.settings_fields_longTimeFormat),
),
),
),
Expanded(
child: Padding(
padding: const EdgeInsets.only(
left: 5.0, bottom: 10.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(translate(LocalizationKeys.general_example),
textScaleFactor: 0.75),
Text(
DateFormat(_longTimeFormatController.text)
.format(DateTime.now()),
textScaleFactor: 1.25,
),
],
),
),
),
],
),
),
]
: [],
),
],
),
),
bottomNavigationBar: BottomAppBar(
child: Padding(
padding: const EdgeInsets.all(10.0),
child: Row(
children: [
ElevatedButton.icon(
onPressed: handleResetAction,
icon: const Icon(
Icons.settings_backup_restore,
size: 18.0,
),
label: Text(translate(LocalizationKeys.settings_resetAll).toUpperCase()),
),
const Spacer(),
],
),
),
),
);
}
}