Commit ba77f80

derdilla <derdilla06@gmail.com>
2023-07-13 01:17:06
AddMeasurementPage rewrite
1 parent 19a20e8
Changed files (2)
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);
+        },
+      );
+    });
   }
+
 }