Commit ba77f80
Changed files (2)
lib
components
screens
lib/components/measurement_list.dart
@@ -97,7 +97,7 @@ class MeasurementList extends StatelessWidget {
initDia: data[index].diastolic ?? -1,
initPul: data[index].pulse ?? -1,
initNote: data[index].notes ?? '',
- isEdit: true,
+ addInitialValuesOnCancel: true,
)),
);
return false;
lib/screens/add_measurement.dart
@@ -8,23 +8,22 @@ import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:intl/intl.dart';
import 'package:provider/provider.dart';
-// TODO: rewrite; this can be far more compact and is way to monolithic
class AddMeasurementPage extends StatefulWidget {
final DateTime? initTime;
- final int initSys;
- final int initDia;
- final int initPul;
- final String initNote;
- final bool isEdit;
+ final int? initSys;
+ final int? initDia;
+ final int? initPul;
+ final String? initNote;
+ final bool addInitialValuesOnCancel;
const AddMeasurementPage(
{super.key,
this.initTime,
- this.initSys = -1,
- this.initDia = -1,
- this.initPul = -1,
- this.initNote = '',
- this.isEdit = false});
+ this.initSys,
+ this.initDia,
+ this.initPul,
+ this.initNote,
+ this.addInitialValuesOnCancel = false});
@override
State<AddMeasurementPage> createState() => _AddMeasurementPageState();
@@ -33,10 +32,10 @@ class AddMeasurementPage extends StatefulWidget {
class _AddMeasurementPageState extends State<AddMeasurementPage> {
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
late DateTime _time;
- late int _systolic;
- late int _diastolic;
- late int _pulse;
- late String _note;
+ late int? _systolic;
+ late int? _diastolic;
+ late int? _pulse;
+ late String? _note;
final _sysFocusNode = FocusNode();
@@ -60,196 +59,135 @@ class _AddMeasurementPageState extends State<AddMeasurementPage> {
child: SingleChildScrollView(
child: Container(
padding: const EdgeInsets.all(60.0),
- child: Column(
- mainAxisAlignment: MainAxisAlignment.center,
- crossAxisAlignment: CrossAxisAlignment.center,
- children: [
- Consumer<Settings>(builder: (context, settings, child) {
- final formatter = DateFormat(settings.dateFormatString);
- if (settings.allowManualTimeInput) {
- return GestureDetector(
- onTap: () async {
- var selectedTime = await showDateTimePicker(
- context: context,
- firstDate: DateTime.fromMillisecondsSinceEpoch(0),
- lastDate: DateTime.now().copyWith(second: DateTime.now().second + 1),
- initialDate: _time);
- if (selectedTime != null) {
- setState(() {
- _time = selectedTime;
- });
- }
- },
- child: Column(
- children: [
- Row(
- children: [Text(formatter.format(_time)), const Spacer(), const Icon(Icons.edit)],
- ),
- const SizedBox(
- height: 3,
+ child: Consumer<Settings>(builder: (context, settings, child) {
+ return Column(
+ mainAxisAlignment: MainAxisAlignment.center,
+ crossAxisAlignment: CrossAxisAlignment.center,
+ children: [
+ (() {
+ final formatter = DateFormat(settings.dateFormatString);
+ if (settings.allowManualTimeInput) {
+ return GestureDetector(
+ onTap: () async {
+ var selectedTime = await showDateTimePicker(
+ context: context,
+ firstDate: DateTime.fromMillisecondsSinceEpoch(0),
+ lastDate: DateTime.now().copyWith(second: DateTime.now().second + 1),
+ initialDate: _time);
+ if (selectedTime != null) {
+ setState(() {
+ _time = selectedTime;
+ });
+ }
+ },
+ child: Column(
+ children: [
+ Row(
+ children: [Text(formatter.format(_time)), const Spacer(), const Icon(Icons.edit)],
+ ),
+ const SizedBox(
+ height: 3,
+ ),
+ Divider(
+ color: Theme.of(context).disabledColor,
+ thickness: 1,
+ )
+ ],
),
- Divider(
- color: Theme.of(context).disabledColor,
- thickness: 1,
- )
- ],
- ),
- );
- } else {
- return const SizedBox.shrink();
- }
- }),
- Consumer<Settings>(builder: (context, settings, child) {
- return TextFormField(
- key: const Key('txtSys'),
- initialValue: widget.isEdit ? _systolic.toString() : '',
- decoration: InputDecoration(hintText: AppLocalizations.of(context)?.sysLong),
- keyboardType: TextInputType.number,
- inputFormatters: <TextInputFormatter>[FilteringTextInputFormatter.digitsOnly],
- focusNode: _sysFocusNode,
- onChanged: (String? value) {
- // to next field
- if (value != null && value.isNotEmpty && (int.tryParse(value) ?? -1) > 40) {
- FocusScope.of(context).nextFocus();
- }
- },
- validator: (String? value) {
- // TMP TODO REMOVE
- _systolic = int.tryParse(value??'') ?? -1;
- return null;
- // TMP END
-
- if (value == null || value.isEmpty || (int.tryParse(value) == null)) {
- return AppLocalizations.of(context)?.errNaN;
- } else if (settings.validateInputs && (int.tryParse(value) ?? -1) <= 30) {
- return AppLocalizations.of(context)?.errLt30;
- } else if (settings.validateInputs && (int.tryParse(value) ?? 1000) >= 400) {
- // exceeding this value is unlikely: https://pubmed.ncbi.nlm.nih.gov/7741618/
- return AppLocalizations.of(context)?.errUnrealistic;
- } else {
- _systolic = int.tryParse(value) ?? -1;
- }
- return null;
- },
- );
- }),
- Consumer<Settings>(builder: (context, settings, child) {
- return TextFormField(
- key: const Key('txtDia'),
- initialValue: widget.isEdit ? _diastolic.toString() : '',
- decoration: InputDecoration(hintText: AppLocalizations.of(context)?.diaLong),
- keyboardType: TextInputType.number,
- inputFormatters: <TextInputFormatter>[FilteringTextInputFormatter.digitsOnly],
- onChanged: (String? value) {
- // to next field
- if (value != null && value.isNotEmpty && (int.tryParse(value) ?? -1) > 40) {
- FocusScope.of(context).nextFocus();
- }
- },
- validator: (String? value) {
- // TMP TODO REMOVE
- _diastolic = int.tryParse(value??'') ?? -1;
- return null;
- // TMP END
- if (value == null || value.isEmpty || (int.tryParse(value) == null)) {
- return AppLocalizations.of(context)?.errNaN;
- } else if (settings.validateInputs && (int.tryParse(value) ?? -1) <= 30) {
- return AppLocalizations.of(context)?.errLt30;
- } else if (settings.validateInputs && (int.tryParse(value) ?? 1000) >= 400) {
- // exceeding this value is unlikely: https://pubmed.ncbi.nlm.nih.gov/7741618/
- return AppLocalizations.of(context)?.errUnrealistic;
- } else if (settings.validateInputs && (int.tryParse(value) ?? -1) >= _systolic) {
- return AppLocalizations.of(context)?.errDiaGtSys;
- } else {
- _diastolic = int.tryParse(value) ?? -1;
- }
- return null;
- },
- );
- }),
- Consumer<Settings>(builder: (context, settings, child) {
- return TextFormField(
- key: const Key('txtPul'),
- initialValue: widget.isEdit ? _pulse.toString() : '',
- decoration: InputDecoration(hintText: AppLocalizations.of(context)?.pulLong),
- keyboardType: TextInputType.number,
- inputFormatters: <TextInputFormatter>[FilteringTextInputFormatter.digitsOnly],
- onChanged: (String? value) {
- // to next field
- if (value != null && value.isNotEmpty && (int.tryParse(value) ?? -1) > 35) {
- FocusScope.of(context).nextFocus();
- }
- },
- validator: (String? value) {
- // TMP TODO REMOVE
- _pulse = int.tryParse(value??'') ?? -1;
- return null;
- // TMP END
-
- if (value == null || value.isEmpty || (int.tryParse(value) == null)) {
- return AppLocalizations.of(context)?.errNaN;
- } else if (settings.validateInputs && (int.tryParse(value) ?? -1) <= 30) {
- return AppLocalizations.of(context)?.errLt30;
- } else if (settings.validateInputs && (int.tryParse(value) ?? 1000) >= 600) {
- // exceeding this value is unlikely: https://www.ncbi.nlm.nih.gov/pmc/articles/PMC3273956/
- return AppLocalizations.of(context)?.errUnrealistic;
+ );
} else {
- _pulse = int.tryParse(value) ?? -1;
+ return const SizedBox.shrink();
}
- return null;
- },
- );
- }),
- TextFormField(
- initialValue: widget.isEdit ? _note.toString() : '',
- decoration: InputDecoration(hintText: AppLocalizations.of(context)?.addNote),
- validator: (String? value) {
- _note = value ?? "";
- return null;
- },
- ),
- const SizedBox(
- height: 24,
- ),
- Row(
- children: [
- ElevatedButton(
- key: const Key('btnCancel'),
- onPressed: () {
- if (widget.isEdit) {
- Provider.of<BloodPressureModel>(context, listen: false).add(BloodPressureRecord(
- widget.initTime ?? DateTime.now(),
- _nullInvalidInt(widget.initSys),
- _nullInvalidInt(widget.initDia),
- _nullInvalidInt(widget.initPul),
- widget.initNote));
+ })(),
+ ValueInput(
+ key: const Key('txtSys'),
+ initialValue: (_systolic ?? '').toString(),
+ hintText: AppLocalizations.of(context)!.sysLong,
+ basicValidation: !settings.allowMissingValues,
+ focusNode: _sysFocusNode,
+ additionalValidator: (String? value) {
+ _systolic = int.tryParse(value ?? '');
+ return null;
+ }
+ ),
+ ValueInput(
+ key: const Key('txtDia'),
+ initialValue: (_diastolic ?? '').toString(),
+ hintText: AppLocalizations.of(context)!.diaLong,
+ basicValidation: !settings.allowMissingValues,
+ additionalValidator: (String? value) {
+ if (settings.validateInputs && (int.tryParse(value ?? '') ?? 0) >= (_systolic ?? 1)) {
+ return AppLocalizations.of(context)?.errDiaGtSys;
}
- Navigator.of(context).pop();
- },
- style: ElevatedButton.styleFrom(backgroundColor: Theme.of(context).unselectedWidgetColor),
- child: Text(AppLocalizations.of(context)!.btnCancel)),
- const Spacer(),
- ElevatedButton(
- key: const Key('btnSave'),
- onPressed: () async {
- if (_formKey.currentState!.validate()) {
- final settings = Provider.of<Settings>(context, listen: false);
- final model = Provider.of<BloodPressureModel>(context, listen: false);
- final exporter = Exporter(context);
- final navigator = Navigator.of(context);
-
- await model.add(BloodPressureRecord(_time, _nullInvalidInt(_systolic), _nullInvalidInt(_diastolic), _nullInvalidInt(_pulse), _note));
- if (settings.exportAfterEveryEntry) {
- exporter.export();
- }
- navigator.pop();
+ _diastolic = int.tryParse(value ?? '');
+ return null;
+ }
+ ),
+ ValueInput(
+ key: const Key('txtPul'),
+ initialValue: (_pulse ?? '').toString(),
+ hintText: AppLocalizations.of(context)!.pulLong,
+ basicValidation: !settings.allowMissingValues,
+ additionalValidator: (String? value) {
+ if (settings.validateInputs && (int.tryParse(value ?? '') ?? 0) >= 600) { // https://www.ncbi.nlm.nih.gov/pmc/articles/PMC3273956/
+ return AppLocalizations.of(context)?.errUnrealistic;
}
- },
- style: ElevatedButton.styleFrom(backgroundColor: Theme.of(context).primaryColor),
- child: Text(AppLocalizations.of(context)!.btnSave))
- ],
- )
- ],
+ _pulse = int.tryParse(value ?? '');
+ return null;
+ }
+ ),
+ TextFormField(
+ initialValue: (_note ?? '').toString(),
+ decoration: InputDecoration(hintText: AppLocalizations.of(context)?.addNote),
+ validator: (String? value) {
+ _note = value;
+ return null;
+ },
+ ),
+ const SizedBox(
+ height: 24,
+ ),
+ Row(
+ children: [
+ ElevatedButton(
+ key: const Key('btnCancel'),
+ onPressed: () {
+ if (widget.addInitialValuesOnCancel) {
+ assert(widget.initTime != null);
+ Provider.of<BloodPressureModel>(context, listen: false).add(BloodPressureRecord(
+ widget.initTime ?? DateTime.now(),
+ widget.initSys,
+ widget.initDia,
+ widget.initPul,
+ widget.initNote));
+ }
+ Navigator.of(context).pop();
+ },
+ style: ElevatedButton.styleFrom(backgroundColor: Theme.of(context).unselectedWidgetColor),
+ child: Text(AppLocalizations.of(context)!.btnCancel)),
+ const Spacer(),
+ ElevatedButton(
+ key: const Key('btnSave'),
+ onPressed: () async {
+ if (_formKey.currentState!.validate()) {
+ final settings = Provider.of<Settings>(context, listen: false);
+ final model = Provider.of<BloodPressureModel>(context, listen: false);
+ final exporter = Exporter(context);
+ final navigator = Navigator.of(context);
+
+ await model.add(BloodPressureRecord(_time, _systolic, _diastolic, _pulse, _note));
+ if (settings.exportAfterEveryEntry) {
+ exporter.export();
+ }
+ navigator.pop();
+ }
+ },
+ style: ElevatedButton.styleFrom(backgroundColor: Theme.of(context).primaryColor),
+ child: Text(AppLocalizations.of(context)!.btnSave))
+ ],
+ )
+ ]);
+ }
),
),
),
@@ -257,8 +195,47 @@ class _AddMeasurementPageState extends State<AddMeasurementPage> {
),
);
}
+}
+
+class ValueInput extends StatelessWidget {
+ final String initialValue;
+ final String hintText;
+ final FocusNode? focusNode;
+ final bool basicValidation;
+ final FormFieldValidator<String> additionalValidator;
+
+ const ValueInput({super.key, required this.initialValue, required this.hintText, this.focusNode, this.basicValidation = true,
+ required this.additionalValidator});
- int? _nullInvalidInt(int i) {
- return (i >= 0) ? i : null;
+ @override
+ Widget build(BuildContext context) {
+ return Consumer<Settings>(builder: (context, settings, child) {
+ return TextFormField(
+ initialValue: initialValue,
+ decoration: InputDecoration(hintText: hintText),
+ keyboardType: TextInputType.number,
+ inputFormatters: <TextInputFormatter>[FilteringTextInputFormatter.digitsOnly],
+ focusNode: focusNode,
+ onChanged: (String? value) {
+ if (value != null && value.isNotEmpty && (int.tryParse(value) ?? -1) > 40) {
+ FocusScope.of(context).nextFocus();
+ }
+ },
+ validator: (String? value) {
+ if (basicValidation) {
+ if (value == null || value.isEmpty || (int.tryParse(value) == null)) {
+ return AppLocalizations.of(context)?.errNaN;
+ } else if (settings.validateInputs && (int.tryParse(value) ?? -1) <= 30) {
+ return AppLocalizations.of(context)?.errLt30;
+ } else if (settings.validateInputs && (int.tryParse(value??'')??0) >= 400) {
+ // https://pubmed.ncbi.nlm.nih.gov/7741618/
+ return AppLocalizations.of(context)?.errUnrealistic;
+ }
+ }
+ return additionalValidator(value);
+ },
+ );
+ });
}
+
}