From ab0839bff3bdd476254ad1cd598f2f48ea8f2e38 Mon Sep 17 00:00:00 2001 From: spinel Date: Tue, 9 Nov 2021 01:17:35 +0100 Subject: [PATCH] fix objectbox meta model, update todo list, add date picker to log entry --- TODO | 36 +- lib/components/data_table.dart | 43 -- lib/components/forms.dart | 142 +++---- lib/components/progress_indicator.dart | 65 --- lib/main.dart | 2 +- lib/models/accuracy.dart | 2 +- lib/models/basal.dart | 2 +- lib/models/basal_profile.dart | 2 +- lib/models/bolus.dart | 2 +- lib/models/bolus_profile.dart | 2 +- lib/models/log_entry.dart | 3 +- lib/models/log_event.dart | 2 +- lib/models/log_event_type.dart | 2 +- lib/models/log_meal.dart | 2 +- lib/models/meal.dart | 2 +- lib/models/meal_category.dart | 2 +- lib/models/meal_portion_type.dart | 2 +- lib/models/meal_source.dart | 2 +- lib/navigation.dart | 2 +- lib/objectbox.g.dart | 4 +- lib/screens/accuracy_detail.dart | 6 +- lib/screens/accuracy_list.dart | 1 + lib/screens/basal/basal_detail.dart | 8 +- lib/screens/basal/basal_list.dart | 1 - lib/screens/basal/basal_profile_detail.dart | 19 +- ...iles_list.dart => basal_profile_list.dart} | 0 lib/screens/bolus/bolus_detail.dart | 10 +- lib/screens/bolus/bolus_list.dart | 2 - lib/screens/bolus/bolus_profile_detail.dart | 28 +- lib/screens/bolus/bolus_profile_list.dart | 1 - lib/screens/log/active_log_event_list.dart | 1 - lib/screens/log/log.dart | 5 +- lib/screens/log/log_entry.dart | 401 +++++++++++++----- lib/screens/log/log_entry_form.dart | 185 -------- lib/screens/log/log_event_detail.dart | 20 +- lib/screens/log/log_event_list.dart | 1 - lib/screens/log/log_event_type_detail.dart | 20 +- lib/screens/log/log_meal_detail.dart | 14 +- lib/screens/meal/meal_category_detail.dart | 2 +- lib/screens/meal/meal_detail.dart | 37 +- .../meal/meal_portion_type_detail.dart | 2 +- lib/screens/meal/meal_source_detail.dart | 10 +- lib/settings.dart | 14 +- 43 files changed, 490 insertions(+), 619 deletions(-) delete mode 100644 lib/components/data_table.dart delete mode 100644 lib/components/progress_indicator.dart rename lib/screens/basal/{basal_profiles_list.dart => basal_profile_list.dart} (100%) delete mode 100644 lib/screens/log/log_entry_form.dart diff --git a/TODO b/TODO index a522ce5..dc50418 100644 --- a/TODO +++ b/TODO @@ -1,7 +1,37 @@ - -Todo: +General/Framework: ☐ add active/deleted flag to all data models ☐ account for deleted/disabled elements in dropdowns ☐ place dropdown items right below their input ✔ use local database instead of back4app @done(21-11-07 18:53) - \ No newline at end of file + ☐ find a general way to deal with duration fields + ☐ add explanations to each section + ☐ use ids instead of passing entities around where possible + +Accuracies: + ☐ implement reordering + +Basal/Bolus: + ☐ create distinct visual mode for picking the active profile + +Meal: + none + +Log Overview: + ☐ add active events view + ☐ adjust/debug active events view + ☐ display more information + ☐ apply target color settings to glucose + ☐ total bolus and carbs per entry + +Log Entry: + ☐ add tab for bolus overview + ☐ calculate bolus suggestions according to active profile + ☐ add time picker for entry date/time + +Event Types: + ☐ add option to change bolus/basal profile for event duration + +Settings: + ☐ add objectbox class and use instead of shared preferences + ☐ add fields for date and time formats + ☐ add fields for glucose target \ No newline at end of file diff --git a/lib/components/data_table.dart b/lib/components/data_table.dart deleted file mode 100644 index 90c2101..0000000 --- a/lib/components/data_table.dart +++ /dev/null @@ -1,43 +0,0 @@ -import 'package:flutter/material.dart'; - -abstract class DataTableContent { - bool selected = false; - List asDataTableCells(List actions) => []; - static List asDataTableColumns() => []; -} - -class DataTableSourceBuilder extends DataTableSource { - final List data; - final BuildContext context; - - DataTableSourceBuilder(this.context, this.data); - - @override - bool get isRowCountApproximate => false; - - @override - int get rowCount => data.length; - - @override - int get selectedRowCount { - int count = 0; - for (var element in data) { - if (element.selected) { - count++; - } - } - return count; - } - - @override - DataRow? getRow(int index) { - assert(index >= 0); - if (index >= data.length) return null; - final rowData = data[index]; - return DataRow.byIndex( - index: index, - selected: rowData.selected, - cells: rowData.asDataTableCells([]), - ); - } -} diff --git a/lib/components/forms.dart b/lib/components/forms.dart index a09f32c..cb9a507 100644 --- a/lib/components/forms.dart +++ b/lib/components/forms.dart @@ -1,19 +1,18 @@ -import 'package:diameter/components/progress_indicator.dart'; import 'package:flutter/material.dart'; -class StyledForm extends StatefulWidget { +class FormWrapper extends StatefulWidget { final List? fields; final List? buttons; final GlobalKey? formState; - const StyledForm({Key? key, this.formState, this.fields, this.buttons}) + const FormWrapper({Key? key, this.formState, this.fields, this.buttons}) : super(key: key); @override - _StyledFormState createState() => _StyledFormState(); + _FormWrapperState createState() => _FormWrapperState(); } -class _StyledFormState extends State { +class _FormWrapperState extends State { @override Widget build(BuildContext context) { return Padding( @@ -45,13 +44,13 @@ class _StyledFormState extends State { } } -class StyledBooleanFormField extends StatefulWidget { +class BooleanFormField extends StatefulWidget { final bool value; final String label; final void Function(bool) onChanged; final bool? enabled; - const StyledBooleanFormField( + const BooleanFormField( {Key? key, required this.value, required this.label, @@ -60,10 +59,10 @@ class StyledBooleanFormField extends StatefulWidget { : super(key: key); @override - _StyledBooleanFormFieldState createState() => _StyledBooleanFormFieldState(); + _BooleanFormFieldState createState() => _BooleanFormFieldState(); } -class _StyledBooleanFormFieldState extends State { +class _BooleanFormFieldState extends State { @override Widget build(BuildContext context) { return FormField(builder: (context) { @@ -80,13 +79,58 @@ class _StyledBooleanFormFieldState extends State { } } -class StyledTimeOfDayFormField extends StatefulWidget { +class DateTimeFormField extends StatefulWidget { + final DateTime date; + final DateTime? minDate; + final DateTime? maxDate; + final TextEditingController controller; + final String label; + final void Function(DateTime?) onChanged; + + const DateTimeFormField( + {Key? key, + required this.date, + this.minDate, + this.maxDate, + required this.controller, + required this.label, + required this.onChanged}) + : super(key: key); + + @override + _DateTimeFormFieldState createState() => + _DateTimeFormFieldState(); +} + +class _DateTimeFormFieldState extends State { + @override + Widget build(BuildContext context) { + return TextFormField( + readOnly: true, + controller: widget.controller, + decoration: InputDecoration( + labelText: widget.label, + ), + onTap: () async { + final newTime = await showDatePicker( + context: context, + initialDate: widget.date, + firstDate: widget.minDate ?? DateTime(2000,1,1), + lastDate: widget.maxDate ?? DateTime.now(), + ); + widget.onChanged(newTime); + }, + ); + } +} + +class TimeOfDayFormField extends StatefulWidget { final TimeOfDay time; final TextEditingController controller; final String label; final void Function(TimeOfDay?) onChanged; - const StyledTimeOfDayFormField( + const TimeOfDayFormField( {Key? key, required this.time, required this.controller, @@ -95,11 +139,11 @@ class StyledTimeOfDayFormField extends StatefulWidget { : super(key: key); @override - _StyledTimeOfDayFormFieldState createState() => - _StyledTimeOfDayFormFieldState(); + _TimeOfDayFormFieldState createState() => + _TimeOfDayFormFieldState(); } -class _StyledTimeOfDayFormFieldState extends State { +class _TimeOfDayFormFieldState extends State { @override Widget build(BuildContext context) { return TextFormField( @@ -119,14 +163,14 @@ class _StyledTimeOfDayFormFieldState extends State { } } -class StyledDropdownButton extends StatefulWidget { +class LabeledDropdownButton extends StatefulWidget { final String label; final T? selectedItem; final List items; final Widget Function(T item) renderItem; final void Function(T? value) onChanged; - const StyledDropdownButton( + const LabeledDropdownButton( {Key? key, this.selectedItem, required this.label, @@ -136,10 +180,10 @@ class StyledDropdownButton extends StatefulWidget { : super(key: key); @override - _StyledDropdownButtonState createState() => _StyledDropdownButtonState(); + _LabeledDropdownButtonState createState() => _LabeledDropdownButtonState(); } -class _StyledDropdownButtonState extends State> { +class _LabeledDropdownButtonState extends State> { @override Widget build(BuildContext context) { return DropdownButtonFormField( @@ -158,65 +202,3 @@ class _StyledDropdownButtonState extends State> { } } -class StyledFutureDropdownButton extends StatefulWidget { - final String label; - final String? selectedItem; - final Future> items; - final String? Function(T item) getItemValue; - final Widget Function(T item) renderItem; - final void Function(String? value) onChanged; - - const StyledFutureDropdownButton( - {Key? key, - this.selectedItem, - required this.label, - required this.items, - required this.getItemValue, - required this.renderItem, - required this.onChanged}) - : super(key: key); - - @override - _StyledFutureDropdownButtonState createState() => - _StyledFutureDropdownButtonState(); -} - -class _StyledFutureDropdownButtonState - extends State> { - @override - Widget build(BuildContext context) { - return FutureBuilder>( - future: widget.items, - builder: (context, snapshot) { - return ViewWithProgressIndicator( - snapshot: snapshot, - padding: const EdgeInsets.all(10.0), - progressIndicatorSize: 44, - child: snapshot.data == null || snapshot.data!.isEmpty - ? Row( - mainAxisAlignment: MainAxisAlignment.center, - children: const [ - Padding( - padding: EdgeInsets.all(10.0), - child: Text('No Meal Sources'), - ) - ], - ) - : DropdownButtonFormField( - decoration: InputDecoration( - labelText: widget.label, - ), - value: widget.selectedItem, - onChanged: widget.onChanged, - items: snapshot.data! - .map((item) => DropdownMenuItem( - value: widget.getItemValue(item), - child: widget.renderItem(item), - )) - .toList(), - ), - ); - }, - ); - } -} diff --git a/lib/components/progress_indicator.dart b/lib/components/progress_indicator.dart deleted file mode 100644 index f9a69e9..0000000 --- a/lib/components/progress_indicator.dart +++ /dev/null @@ -1,65 +0,0 @@ -import 'package:flutter/material.dart'; -// import 'package:flutter/widgets.dart'; - -class ViewWithProgressIndicator extends StatefulWidget { - final AsyncSnapshot snapshot; - final Widget child; - final double progressIndicatorSize; - final EdgeInsets padding; - - const ViewWithProgressIndicator( - {Key? key, - required this.snapshot, - required this.child, - this.progressIndicatorSize = 100, - this.padding = const EdgeInsets.all(0)}) - : super(key: key); - - @override - _ViewWithProgressIndicatorState createState() => - _ViewWithProgressIndicatorState(); -} - -class _ViewWithProgressIndicatorState extends State { - - - @override - Widget build(BuildContext context) { - switch (widget.snapshot.connectionState) { - case ConnectionState.none: - case ConnectionState.waiting: - return Container( - alignment: Alignment.center, - padding: widget.padding, - child: Center( - child: SizedBox( - width: widget.progressIndicatorSize, - height: widget.progressIndicatorSize, - child: FutureBuilder( - future: Future.delayed(const Duration(seconds: 1)), - builder: (context, wait) { - if (wait.connectionState != ConnectionState.waiting) { - return const CircularProgressIndicator(); - } - return Container(); - } - ), - ), - ), - ); - default: - if (widget.snapshot.hasError) { - return Center( - child: Text(widget.snapshot.error.toString()), - ); - } - if (!widget.snapshot.hasData) { - return const Center( - child: Text("No data"), - ); - } else { - return widget.child; - } - } - } -} diff --git a/lib/main.dart b/lib/main.dart index b1b4763..1f7e8e6 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -21,7 +21,7 @@ import 'package:flutter/material.dart'; import 'package:parse_server_sdk_flutter/parse_server_sdk.dart'; import 'package:diameter/screens/accuracy_list.dart'; import 'package:diameter/config.dart'; -import 'package:diameter/screens/basal/basal_profiles_list.dart'; +import 'package:diameter/screens/basal/basal_profile_list.dart'; import 'package:diameter/screens/bolus/bolus_profile_list.dart'; import 'package:diameter/navigation.dart'; diff --git a/lib/models/accuracy.dart b/lib/models/accuracy.dart index 5391c84..571dc34 100644 --- a/lib/models/accuracy.dart +++ b/lib/models/accuracy.dart @@ -1,7 +1,7 @@ import 'package:diameter/main.dart'; import 'package:diameter/objectbox.g.dart'; -@Entity() +@Entity(uid: 3095978685310268382) class Accuracy { static final Box box = objectBox.store.box(); diff --git a/lib/models/basal.dart b/lib/models/basal.dart index cb547be..35d1788 100644 --- a/lib/models/basal.dart +++ b/lib/models/basal.dart @@ -2,7 +2,7 @@ import 'package:diameter/main.dart'; import 'package:diameter/models/basal_profile.dart'; import 'package:diameter/objectbox.g.dart'; -@Entity() +@Entity(uid: 1467758525778521891) class Basal { static final Box box = objectBox.store.box(); diff --git a/lib/models/basal_profile.dart b/lib/models/basal_profile.dart index ceb9bff..f02de8f 100644 --- a/lib/models/basal_profile.dart +++ b/lib/models/basal_profile.dart @@ -1,7 +1,7 @@ import 'package:diameter/main.dart'; import 'package:diameter/objectbox.g.dart'; -@Entity() +@Entity(uid: 3613736032926903785) class BasalProfile { static final Box box = objectBox.store.box(); diff --git a/lib/models/bolus.dart b/lib/models/bolus.dart index 975f461..3147023 100644 --- a/lib/models/bolus.dart +++ b/lib/models/bolus.dart @@ -2,7 +2,7 @@ import 'package:diameter/main.dart'; import 'package:diameter/models/bolus_profile.dart'; import 'package:diameter/objectbox.g.dart'; -@Entity() +@Entity(uid: 3417770529060202389) class Bolus { static final Box box = objectBox.store.box(); diff --git a/lib/models/bolus_profile.dart b/lib/models/bolus_profile.dart index 36966b5..4863826 100644 --- a/lib/models/bolus_profile.dart +++ b/lib/models/bolus_profile.dart @@ -1,7 +1,7 @@ import 'package:diameter/main.dart'; import 'package:diameter/objectbox.g.dart'; -@Entity() +@Entity(uid: 8812452529027052317) class BolusProfile { static final Box box = objectBox.store.box(); diff --git a/lib/models/log_entry.dart b/lib/models/log_entry.dart index eaf4ac2..b6f58eb 100644 --- a/lib/models/log_entry.dart +++ b/lib/models/log_entry.dart @@ -2,9 +2,8 @@ import 'package:diameter/main.dart'; import 'package:diameter/models/log_event.dart'; import 'package:diameter/models/log_meal.dart'; import 'package:diameter/objectbox.g.dart'; -import 'package:objectbox/objectbox.dart'; -@Entity() +@Entity(uid: 752131069307970560) class LogEntry { static final Box box = objectBox.store.box(); diff --git a/lib/models/log_event.dart b/lib/models/log_event.dart index 4db1bec..6122701 100644 --- a/lib/models/log_event.dart +++ b/lib/models/log_event.dart @@ -3,7 +3,7 @@ import 'package:diameter/models/log_entry.dart'; import 'package:diameter/models/log_event_type.dart'; import 'package:diameter/objectbox.g.dart'; -@Entity() +@Entity(uid: 4303325892753185970) class LogEvent { static final Box box = objectBox.store.box(); diff --git a/lib/models/log_event_type.dart b/lib/models/log_event_type.dart index 857813d..2e6c39d 100644 --- a/lib/models/log_event_type.dart +++ b/lib/models/log_event_type.dart @@ -1,7 +1,7 @@ import 'package:diameter/main.dart'; import 'package:objectbox/objectbox.dart'; -@Entity() +@Entity(uid: 8362795406595606110) class LogEventType { static final Box box = objectBox.store.box(); diff --git a/lib/models/log_meal.dart b/lib/models/log_meal.dart index e1fea66..6ee6d02 100644 --- a/lib/models/log_meal.dart +++ b/lib/models/log_meal.dart @@ -7,7 +7,7 @@ import 'package:diameter/models/meal_source.dart'; import 'package:diameter/models/accuracy.dart'; import 'package:objectbox/objectbox.dart'; -@Entity() +@Entity(uid: 411177866700467286) class LogMeal { static final Box box = objectBox.store.box(); diff --git a/lib/models/meal.dart b/lib/models/meal.dart index eb763e2..91e4822 100644 --- a/lib/models/meal.dart +++ b/lib/models/meal.dart @@ -7,7 +7,7 @@ import 'package:objectbox/objectbox.dart'; enum PortionCarbsParameter { carbsRatio, portionSize, carbsPerPortion } -@Entity() +@Entity(uid: 382130101578692012) class Meal { static final Box box = objectBox.store.box(); diff --git a/lib/models/meal_category.dart b/lib/models/meal_category.dart index 298dac0..415636d 100644 --- a/lib/models/meal_category.dart +++ b/lib/models/meal_category.dart @@ -1,7 +1,7 @@ import 'package:diameter/main.dart'; import 'package:objectbox/objectbox.dart'; -@Entity() +@Entity(uid: 3158200688796904913) class MealCategory { static final Box box = objectBox.store.box(); diff --git a/lib/models/meal_portion_type.dart b/lib/models/meal_portion_type.dart index 43d8734..91bddb9 100644 --- a/lib/models/meal_portion_type.dart +++ b/lib/models/meal_portion_type.dart @@ -1,7 +1,7 @@ import 'package:diameter/main.dart'; import 'package:objectbox/objectbox.dart'; -@Entity() +@Entity(uid: 2111511899235985637) class MealPortionType { static final Box box = objectBox.store.box(); diff --git a/lib/models/meal_source.dart b/lib/models/meal_source.dart index 03c1801..0e3e992 100644 --- a/lib/models/meal_source.dart +++ b/lib/models/meal_source.dart @@ -4,7 +4,7 @@ import 'package:diameter/models/meal_category.dart'; import 'package:diameter/models/meal_portion_type.dart'; import 'package:objectbox/objectbox.dart'; -@Entity() +@Entity(uid: 1283034494527412242) class MealSource { static final Box box = objectBox.store.box(); diff --git a/lib/navigation.dart b/lib/navigation.dart index 1b16cc4..ff1f876 100644 --- a/lib/navigation.dart +++ b/lib/navigation.dart @@ -2,7 +2,7 @@ import 'package:diameter/screens/accuracy_detail.dart'; import 'package:diameter/screens/accuracy_list.dart'; import 'package:diameter/screens/basal/basal_detail.dart'; import 'package:diameter/screens/basal/basal_profile_detail.dart'; -import 'package:diameter/screens/basal/basal_profiles_list.dart'; +import 'package:diameter/screens/basal/basal_profile_list.dart'; import 'package:diameter/screens/bolus/bolus_detail.dart'; import 'package:diameter/screens/bolus/bolus_profile_detail.dart'; import 'package:diameter/screens/bolus/bolus_profile_list.dart'; diff --git a/lib/objectbox.g.dart b/lib/objectbox.g.dart index 4517098..c5656d6 100644 --- a/lib/objectbox.g.dart +++ b/lib/objectbox.g.dart @@ -665,8 +665,8 @@ Future openStore( ModelDefinition getObjectBoxModel() { final model = ModelInfo( entities: _entities, - lastEntityId: const IdUid(18, 1283034494527412242), - lastIndexId: const IdUid(25, 1931330716440762729), + lastEntityId: const IdUid(13, 1283034494527412242), + lastIndexId: const IdUid(21, 1931330716440762729), lastRelationId: const IdUid(0, 0), lastSequenceId: const IdUid(0, 0), retiredEntityUids: const [], diff --git a/lib/screens/accuracy_detail.dart b/lib/screens/accuracy_detail.dart index 33cdb48..90f754a 100644 --- a/lib/screens/accuracy_detail.dart +++ b/lib/screens/accuracy_detail.dart @@ -108,7 +108,7 @@ class _AccuracyDetailScreenState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ - StyledForm( + FormWrapper( formState: _accuracyForm, fields: [ TextFormField( @@ -123,7 +123,7 @@ class _AccuracyDetailScreenState extends State { return null; }, ), - StyledBooleanFormField( + BooleanFormField( value: _forCarbsRatio, label: 'for carbs ratio', onChanged: (value) { @@ -132,7 +132,7 @@ class _AccuracyDetailScreenState extends State { }); }, ), - StyledBooleanFormField( + BooleanFormField( value: _forPortionSize, label: 'for portion size', onChanged: (value) { diff --git a/lib/screens/accuracy_list.dart b/lib/screens/accuracy_list.dart index 8e0c4da..60119e7 100644 --- a/lib/screens/accuracy_list.dart +++ b/lib/screens/accuracy_list.dart @@ -105,6 +105,7 @@ class _AccuracyListScreenState extends State { IconButton( icon: const Icon(Icons.reorder), onPressed: () { + // ignore: todo // TODO: implement reordering }, ), diff --git a/lib/screens/basal/basal_detail.dart b/lib/screens/basal/basal_detail.dart index 80f8c9a..1d8c04e 100644 --- a/lib/screens/basal/basal_detail.dart +++ b/lib/screens/basal/basal_detail.dart @@ -85,8 +85,6 @@ class _BasalDetailScreenState extends State { String? error; List basalRates = Basal.getAllForProfile(widget.basalProfileId); - // TODO use a query for the following checks instead? - // check for duplicates if (basalRates .where((other) => (widget.id != other.id) && @@ -187,7 +185,7 @@ class _BasalDetailScreenState extends State { drawer: const Navigation(currentLocation: BasalDetailScreen.routeName), body: Column( children: [ - StyledForm( + FormWrapper( formState: _basalForm, fields: [ Row( @@ -195,7 +193,7 @@ class _BasalDetailScreenState extends State { Expanded( child: Padding( padding: const EdgeInsets.only(right: 5), - child: StyledTimeOfDayFormField( + child: TimeOfDayFormField( label: 'Start Time', controller: _startTimeController, time: _startTime, @@ -213,7 +211,7 @@ class _BasalDetailScreenState extends State { Expanded( child: Padding( padding: const EdgeInsets.only(left: 5), - child: StyledTimeOfDayFormField( + child: TimeOfDayFormField( label: 'End Time', controller: _endTimeController, time: _endTime, diff --git a/lib/screens/basal/basal_list.dart b/lib/screens/basal/basal_list.dart index 96a3c5c..713922a 100644 --- a/lib/screens/basal/basal_list.dart +++ b/lib/screens/basal/basal_list.dart @@ -72,7 +72,6 @@ class _BasalListScreenState extends State { List basalRates = widget.basalRates; Basal basal = basalRates[index]; - // TODO: use queries for all this // check for gaps if (index == 0 && (basal.startTime.hour != 0 || basal.startTime.minute != 0)) { diff --git a/lib/screens/basal/basal_profile_detail.dart b/lib/screens/basal/basal_profile_detail.dart index 13af0bf..9cf0f8a 100644 --- a/lib/screens/basal/basal_profile_detail.dart +++ b/lib/screens/basal/basal_profile_detail.dart @@ -27,7 +27,6 @@ class _BasalProfileDetailScreenState extends State { BasalProfile? _basalProfile; List _basalRates = []; bool _isNew = true; - bool _isSaving = false; final GlobalKey _basalProfileForm = GlobalKey(); @@ -35,6 +34,7 @@ class _BasalProfileDetailScreenState extends State { late IconButton refreshButton; late IconButton closeButton; late DetailBottomRow detailBottomRow; + late DetailBottomRow detailBottomRowWhileSaving; FloatingActionButton? actionButton; List appBarActions = []; @@ -77,7 +77,12 @@ class _BasalProfileDetailScreenState extends State { detailBottomRow = DetailBottomRow( onCancel: handleCancelAction, - onSave: _isSaving ? null : handleSaveAction, + onSave: handleSaveAction, + ); + + detailBottomRowWhileSaving = DetailBottomRow( + onCancel: handleCancelAction, + onSave: null, ); actionButton = null; @@ -146,7 +151,7 @@ class _BasalProfileDetailScreenState extends State { }); } else if (!_active && ((_isNew && _activeCount == 0) || - (_activeCount == 1 && _basalProfile!.active))) { + (!_isNew && _activeCount == 1 && _basalProfile!.active))) { await showDialog( context: context, builder: (BuildContext context) { @@ -214,7 +219,7 @@ class _BasalProfileDetailScreenState extends State { void handleSaveAction() async { setState(() { - _isSaving = true; + bottomNav = detailBottomRowWhileSaving; }); if (_basalProfileForm.currentState!.validate()) { await checkActiveProfiles(); @@ -227,7 +232,7 @@ class _BasalProfileDetailScreenState extends State { Navigator.pop(context, '${_isNew ? 'New' : ''} Basal Profile saved'); } setState(() { - _isSaving = false; + bottomNav = detailBottomRow; }); } @@ -283,7 +288,7 @@ class _BasalProfileDetailScreenState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ - StyledForm( + FormWrapper( formState: _basalProfileForm, fields: [ TextFormField( @@ -307,7 +312,7 @@ class _BasalProfileDetailScreenState extends State { alignLabelWithHint: true, ), ), - StyledBooleanFormField( + BooleanFormField( value: _active, onChanged: (value) { setState(() { diff --git a/lib/screens/basal/basal_profiles_list.dart b/lib/screens/basal/basal_profile_list.dart similarity index 100% rename from lib/screens/basal/basal_profiles_list.dart rename to lib/screens/basal/basal_profile_list.dart diff --git a/lib/screens/bolus/bolus_detail.dart b/lib/screens/bolus/bolus_detail.dart index 917f7e7..7277946 100644 --- a/lib/screens/bolus/bolus_detail.dart +++ b/lib/screens/bolus/bolus_detail.dart @@ -93,9 +93,7 @@ class _BolusDetailScreenState extends State { Future validateTimePeriod() async { String? error; List bolusRates = Bolus.getAllForProfile(widget.bolusProfileId); - // BolusProfile.get(widget.bolusProfileId)?.bolusRates ?? []; - - // TODO use a query for the following checks instead? + // check for duplicates if (bolusRates .where((other) => @@ -238,7 +236,7 @@ class _BolusDetailScreenState extends State { body: SingleChildScrollView( child: Column( children: [ - StyledForm( + FormWrapper( formState: _bolusForm, fields: [ Row( @@ -246,7 +244,7 @@ class _BolusDetailScreenState extends State { Expanded( child: Padding( padding: const EdgeInsets.only(right: 5), - child: StyledTimeOfDayFormField( + child: TimeOfDayFormField( label: 'Start Time', controller: _startTimeController, time: _startTime, @@ -264,7 +262,7 @@ class _BolusDetailScreenState extends State { Expanded( child: Padding( padding: const EdgeInsets.only(left: 5), - child: StyledTimeOfDayFormField( + child: TimeOfDayFormField( label: 'End Time', controller: _endTimeController, time: _endTime, diff --git a/lib/screens/bolus/bolus_list.dart b/lib/screens/bolus/bolus_list.dart index 2c2576e..1c8fbe6 100644 --- a/lib/screens/bolus/bolus_list.dart +++ b/lib/screens/bolus/bolus_list.dart @@ -70,8 +70,6 @@ class _BolusListScreenState extends State { List bolusRates = widget.bolusRates; Bolus bolus = bolusRates[index]; - // TODO: use queries for all this - // check for gaps if (index == 0 && (bolus.startTime.toLocal().hour != 0 || bolus.startTime.minute != 0)) { return 'First Bolus of the day needs to start at 00:00'; diff --git a/lib/screens/bolus/bolus_profile_detail.dart b/lib/screens/bolus/bolus_profile_detail.dart index 4e87dd4..08dc75d 100644 --- a/lib/screens/bolus/bolus_profile_detail.dart +++ b/lib/screens/bolus/bolus_profile_detail.dart @@ -26,7 +26,6 @@ class _BolusProfileDetailScreenState extends State { BolusProfile? _bolusProfile; List _bolusRates = []; bool _isNew = true; - bool _isSaving = false; final GlobalKey _bolusProfileForm = GlobalKey(); @@ -34,6 +33,7 @@ class _BolusProfileDetailScreenState extends State { late IconButton refreshButton; late IconButton closeButton; late DetailBottomRow detailBottomRow; + late DetailBottomRow detailBottomRowWhileSaving; FloatingActionButton? actionButton; List appBarActions = []; @@ -73,10 +73,14 @@ class _BolusProfileDetailScreenState extends State { icon: const Icon(Icons.close), ); - // TODO: fix (saving button doesnt get disabled) detailBottomRow = DetailBottomRow( onCancel: handleCancelAction, - onSave: _isSaving ? null : handleSaveAction, + onSave: handleSaveAction, + ); + + detailBottomRowWhileSaving = DetailBottomRow( + onCancel: handleCancelAction, + onSave: null, ); actionButton = null; @@ -144,7 +148,7 @@ class _BolusProfileDetailScreenState extends State { }); } else if (!_active && ((_isNew && _activeCount == 0) || - (_activeCount == 1 && _bolusProfile!.active))) { + (!_isNew && _activeCount == 1 && _bolusProfile!.active))) { await showDialog( context: context, builder: (BuildContext context) { @@ -212,7 +216,7 @@ class _BolusProfileDetailScreenState extends State { void handleSaveAction() async { setState(() { - _isSaving = true; + bottomNav = detailBottomRowWhileSaving; }); if (_bolusProfileForm.currentState!.validate()) { @@ -229,9 +233,9 @@ class _BolusProfileDetailScreenState extends State { } setState(() { - _isSaving = false; + bottomNav = detailBottomRow; }); - } + } void handleCancelAction() { if (showConfirmationDialogOnCancel && @@ -286,7 +290,7 @@ class _BolusProfileDetailScreenState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ - StyledForm( + FormWrapper( formState: _bolusProfileForm, fields: [ TextFormField( @@ -309,7 +313,7 @@ class _BolusProfileDetailScreenState extends State { controller: _notesController, keyboardType: TextInputType.multiline, ), - StyledBooleanFormField( + BooleanFormField( value: _active, onChanged: (value) { setState(() { @@ -326,8 +330,10 @@ class _BolusProfileDetailScreenState extends State { ]; if (!_isNew) { - tabs.add( - BolusListScreen(bolusProfile: _bolusProfile!, bolusRates: _bolusRates, reload: reload)); + tabs.add(BolusListScreen( + bolusProfile: _bolusProfile!, + bolusRates: _bolusRates, + reload: reload)); } return Scaffold( diff --git a/lib/screens/bolus/bolus_profile_list.dart b/lib/screens/bolus/bolus_profile_list.dart index d6579a9..47f7470 100644 --- a/lib/screens/bolus/bolus_profile_list.dart +++ b/lib/screens/bolus/bolus_profile_list.dart @@ -165,7 +165,6 @@ class _BolusProfileListScreenState extends State { ? Colors.green.shade100 : null, onTap: () { - // TODO: make pick active profile visually distinct pickActiveProfileMode ? onPickActive(bolusProfile) : onEdit(bolusProfile); diff --git a/lib/screens/log/active_log_event_list.dart b/lib/screens/log/active_log_event_list.dart index b16c82d..df08e7a 100644 --- a/lib/screens/log/active_log_event_list.dart +++ b/lib/screens/log/active_log_event_list.dart @@ -96,7 +96,6 @@ class _ActiveLogEventListScreenState extends State { padding: const EdgeInsets.only(top: 10.0), child: Column( children: [ - // TODO: make action button instead of appbar AppBar( title: const Text('Active Events'), primary: false, diff --git a/lib/screens/log/log.dart b/lib/screens/log/log.dart index 910477d..e587ddb 100644 --- a/lib/screens/log/log.dart +++ b/lib/screens/log/log.dart @@ -107,8 +107,8 @@ class _LogScreenState extends State { title: Text( DateTimeUtils.displayTime( logEntry.time)), - // TODO: add additional fields (event icons...) - // TODO: display glucose in colors according to target settings + // ignore: todo + // TODO: add more data (event, glucose color...) subtitle: Text(logEntry .mgPerDl != null @@ -141,6 +141,7 @@ class _LogScreenState extends State { ), ], ), + // ignore: todo // TODO: add button for active events floatingActionButton: FloatingActionButton( onPressed: () { diff --git a/lib/screens/log/log_entry.dart b/lib/screens/log/log_entry.dart index 37ac86e..ff29b4b 100644 --- a/lib/screens/log/log_entry.dart +++ b/lib/screens/log/log_entry.dart @@ -1,13 +1,16 @@ import 'package:diameter/components/detail.dart'; import 'package:diameter/components/dialogs.dart'; +import 'package:diameter/components/forms.dart'; import 'package:diameter/config.dart'; import 'package:diameter/models/log_entry.dart'; import 'package:diameter/navigation.dart'; -import 'package:diameter/screens/log/log_entry_form.dart'; import 'package:diameter/screens/log/log_event_detail.dart'; import 'package:diameter/screens/log/log_event_list.dart'; import 'package:diameter/screens/log/log_meal_detail.dart'; import 'package:diameter/screens/log/log_meal_list.dart'; +import 'package:diameter/settings.dart'; +import 'package:diameter/utils/date_time_utils.dart'; +import 'package:diameter/utils/utils.dart'; import 'package:flutter/material.dart'; class LogEntryScreen extends StatefulWidget { @@ -27,6 +30,17 @@ class _LogEntryScreenState extends State { final GlobalKey logEntryForm = GlobalKey(); + DateTime _time = DateTime.now(); + + final _timeController = TextEditingController(text: ''); + final _dateController = TextEditingController(text: ''); + final _mgPerDlController = TextEditingController(text: ''); + final _mmolPerLController = TextEditingController(text: ''); + final _bolusGlucoseController = TextEditingController(text: ''); + final _delayedBolusRateController = TextEditingController(text: ''); + final _delayedBolusDurationController = TextEditingController(text: ''); + final _notesController = TextEditingController(text: ''); + late FloatingActionButton addMealButton; late FloatingActionButton addEventButton; late IconButton refreshButton; @@ -37,39 +51,12 @@ class _LogEntryScreenState extends State { List appBarActions = []; DetailBottomRow? bottomNav; - final formDataControllers = { - 'time': TextEditingController(text: ''), - 'mgPerDl': TextEditingController(text: ''), - 'mmolPerL': TextEditingController(text: ''), - 'bolusGlucose': TextEditingController(text: ''), - 'delayedBolusRate': TextEditingController(text: ''), - 'delayedBolusDuration': TextEditingController(text: ''), - 'notes': TextEditingController(text: ''), - }; - @override void initState() { super.initState(); reload(); - if (_logEntry != null) { - formDataControllers['time']!.text = _logEntry!.time.toString(); - formDataControllers['mgPerDl']!.text = - (_logEntry!.mgPerDl ?? '').toString(); - formDataControllers['mmolPerL']!.text = - (_logEntry!.mmolPerL ?? '').toString(); - formDataControllers['bolusGlucose']!.text = - (_logEntry!.bolusGlucose ?? '').toString(); - formDataControllers['delayedBolusRate']!.text = - (_logEntry!.delayedBolusRate ?? '').toString(); - formDataControllers['delayedBolusDuration']!.text = - (_logEntry!.delayedBolusDuration ?? '').toString(); - formDataControllers['notes']!.text = _logEntry!.notes ?? ''; - } else { - formDataControllers['time']!.text = DateTime.now().toString(); - } - addMealButton = FloatingActionButton( onPressed: handleAddNewMeal, child: const Icon(Icons.add), @@ -98,6 +85,20 @@ class _LogEntryScreenState extends State { actionButton = null; appBarActions = [closeButton]; bottomNav = detailBottomRow; + + if (_logEntry != null) { + _time = _logEntry!.time; + _mgPerDlController.text = (_logEntry!.mgPerDl ?? '').toString(); + _mmolPerLController.text = (_logEntry!.mmolPerL ?? '').toString(); + _bolusGlucoseController.text = (_logEntry!.bolusGlucose ?? '').toString(); + _delayedBolusRateController.text = + (_logEntry!.delayedBolusRate ?? '').toString(); + _delayedBolusDurationController.text = + (_logEntry!.delayedBolusDuration ?? '').toString(); + _notesController.text = _logEntry!.notes ?? ''; + } + + updateTime(); } void reload({String? message}) { @@ -121,6 +122,95 @@ class _LogEntryScreenState extends State { }); } + void updateTime() { + _timeController.text = DateTimeUtils.displayTime(_time); + _dateController.text = DateTimeUtils.displayDate(_time); + } + + void convertBetweenMgPerDlAndMmolPerL({GlucoseMeasurement? calculateFrom}) { + int? mgPerDl; + double? mmolPerL; + + if (calculateFrom != GlucoseMeasurement.mmolPerL && + _mgPerDlController.text != '') { + mgPerDl = int.tryParse(_mgPerDlController.text); + } + if (calculateFrom != GlucoseMeasurement.mgPerDl && + _mmolPerLController.text != '') { + mmolPerL = double.tryParse(_mmolPerLController.text); + } + + if (mgPerDl != null && mmolPerL == null) { + setState(() { + _mmolPerLController.text = + Utils.convertMgPerDlToMmolPerL(mgPerDl!).toString(); + }); + } + if (mmolPerL != null && mgPerDl == null) { + setState(() { + _mgPerDlController.text = + Utils.convertMmolPerLToMgPerDl(mmolPerL!).toString(); + }); + } + } + + void handleSaveAction() async { + setState(() { + _isSaving = true; + }); + if (logEntryForm.currentState!.validate()) { + LogEntry.put(LogEntry( + id: widget.id, + time: _time, + mgPerDl: int.tryParse(_mgPerDlController.text), + mmolPerL: double.tryParse(_mmolPerLController.text), + bolusGlucose: double.tryParse(_bolusGlucoseController.text), + delayedBolusDuration: + int.tryParse(_delayedBolusDurationController.text), + delayedBolusRate: double.tryParse(_delayedBolusRateController.text), + notes: _notesController.text, + )); + Navigator.pushReplacementNamed(context, '/log', + arguments: '${_isNew ? 'New' : ''} Log Entry Saved'); + } + setState(() { + _isSaving = false; + }); + } + + void handleCancelAction() { + if (showConfirmationDialogOnCancel && + ((_isNew && + (int.tryParse(_mgPerDlController.text) != null || + double.tryParse(_mmolPerLController.text) != null || + double.tryParse(_bolusGlucoseController.text) != null || + int.tryParse(_delayedBolusDurationController.text) != + null || + double.tryParse(_delayedBolusRateController.text) != null || + _notesController.text != '')) || + (!_isNew && + (int.tryParse(_mgPerDlController.text) != _logEntry!.mgPerDl || + double.tryParse(_mmolPerLController.text) != + _logEntry!.mmolPerL || + double.tryParse(_bolusGlucoseController.text) != + _logEntry!.bolusGlucose || + int.tryParse(_delayedBolusDurationController.text) != + _logEntry!.delayedBolusDuration || + double.tryParse(_delayedBolusRateController.text) != + _logEntry!.delayedBolusRate || + _notesController.text != (_logEntry!.notes ?? ''))))) { + Dialogs.showCancelConfirmationDialog( + context: context, + isNew: _isNew, + onSave: handleSaveAction, + onDiscard: (context) => Navigator.pushReplacementNamed(context, '/log'), + ); + } else { + Navigator.pushReplacementNamed(context, '/log', + arguments: '${_isNew ? 'New' : ''} Log Entry Saved'); + } + } + void handleAddNewMeal() async { Navigator.push( context, @@ -143,73 +233,6 @@ class _LogEntryScreenState extends State { ).then((message) => reload(message: message)); } - void handleSaveAction() async { - setState(() { - _isSaving = true; - }); - if (logEntryForm.currentState!.validate()) { - LogEntry.put(LogEntry( - id: widget.id, - time: DateTime.parse(formDataControllers['time']!.text), - mgPerDl: int.tryParse(formDataControllers['mgPerDl']!.text), - mmolPerL: double.tryParse(formDataControllers['mmolPerL']!.text), - bolusGlucose: - double.tryParse(formDataControllers['delayedBolusRate']!.text), - delayedBolusDuration: - int.tryParse(formDataControllers['delayedBolusDuration']!.text), - delayedBolusRate: - double.tryParse(formDataControllers['delayedBolusRate']!.text), - notes: formDataControllers['notes']!.text, - )); - Navigator.pushReplacementNamed(context, '/log', - arguments: '${_isNew ? 'New' : ''} Log Entry Saved'); - } - setState(() { - _isSaving = false; - }); - } - - void handleCancelAction() { - if (showConfirmationDialogOnCancel && - ((_isNew && - (int.tryParse(formDataControllers['mgPerDl']?.text ?? '') != - null || - double.tryParse(formDataControllers['mmolPerL']?.text ?? '') != - null || - double.tryParse(formDataControllers['bolusGlucose']?.text ?? '') != - null || - int.tryParse(formDataControllers['delayedBolusDuration']?.text ?? '') != - null || - double.tryParse(formDataControllers['delayedBolusRate']?.text ?? '') != - null || - formDataControllers['notes']?.text != '')) || - (!_isNew && - (int.tryParse(formDataControllers['mgPerDl']?.text ?? '') != - _logEntry!.mgPerDl || - double.tryParse(formDataControllers['mmolPerL']?.text ?? '') != - _logEntry!.mmolPerL || - double.tryParse(formDataControllers['bolusGlucose']?.text ?? '') != - _logEntry!.bolusGlucose || - int.tryParse( - formDataControllers['delayedBolusDuration']?.text ?? - '') != - _logEntry!.delayedBolusDuration || - double.tryParse(formDataControllers['delayedBolusRate']?.text ?? '') != - _logEntry!.delayedBolusRate || - formDataControllers['notes']?.text != - (_logEntry!.notes ?? ''))))) { - Dialogs.showCancelConfirmationDialog( - context: context, - isNew: _isNew, - onSave: handleSaveAction, - onDiscard: (context) => Navigator.pushReplacementNamed(context, '/log'), - ); - } else { - Navigator.pushReplacementNamed(context, '/log', - arguments: '${_isNew ? 'New' : ''} Log Entry Saved'); - } - } - void renderTabButtons(index) { if (_logEntry != null) { setState(() { @@ -243,8 +266,189 @@ class _LogEntryScreenState extends State { renderTabButtons(tabController.index); }); List tabs = [ - LogEntryForm( - formState: logEntryForm, controllers: formDataControllers), + SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + FormWrapper( + formState: logEntryForm, + fields: [ + Row( + children: [ + Expanded( + child: Padding( + padding: const EdgeInsets.only(right: 5), + child: DateTimeFormField( + date: _time, + label: 'Date', + controller: _dateController, + onChanged: (newTime) { + if (newTime != null) { + setState(() { + _time = DateTime( + newTime.year, + newTime.month, + newTime.day, + _time.hour, + _time.minute); + }); + updateTime(); + } + }, + ), + ), + ), + Expanded( + child: Padding( + padding: const EdgeInsets.only(left: 5), + child: TimeOfDayFormField( + time: TimeOfDay.fromDateTime(_time), + label: 'Time', + controller: _timeController, + onChanged: (newTime) { + if (newTime != null) { + setState(() { + _time = DateTime( + _time.year, + _time.month, + _time.day, + newTime.hour, + newTime.minute); + }); + updateTime(); + } + }, + ), + ), + ), + ], + ), + Row( + children: [ + glucoseMeasurement == GlucoseMeasurement.mgPerDl || + glucoseDisplayMode == + GlucoseDisplayMode.both || + glucoseDisplayMode == + GlucoseDisplayMode.bothForDetail + ? Expanded( + child: TextFormField( + decoration: const InputDecoration( + labelText: 'mg/dl', + suffixText: 'mg/dl', + ), + controller: _mgPerDlController, + onChanged: (_) => + convertBetweenMgPerDlAndMmolPerL( + calculateFrom: + GlucoseMeasurement.mgPerDl), + keyboardType: + const TextInputType.numberWithOptions(), + validator: (value) { + if (value!.trim().isEmpty && + _mmolPerLController.text + .trim() + .isEmpty) { + return 'How many mg/dl or mmol/l does the rate make up for?'; + } + return null; + }, + ), + ) + : Container(), + glucoseDisplayMode == GlucoseDisplayMode.both || + glucoseDisplayMode == + GlucoseDisplayMode.bothForDetail + ? IconButton( + onPressed: () => + convertBetweenMgPerDlAndMmolPerL( + calculateFrom: + GlucoseMeasurement.mmolPerL), + icon: const Icon(Icons.calculate), + ) + : Container(), + glucoseMeasurement == GlucoseMeasurement.mmolPerL || + glucoseDisplayMode == + GlucoseDisplayMode.both || + glucoseDisplayMode == + GlucoseDisplayMode.bothForDetail + ? Expanded( + child: TextFormField( + decoration: const InputDecoration( + labelText: 'mmol/l', + suffixText: 'mmol/l', + ), + controller: _mmolPerLController, + onChanged: (_) => + convertBetweenMgPerDlAndMmolPerL( + calculateFrom: + GlucoseMeasurement.mmolPerL), + keyboardType: + const TextInputType.numberWithOptions( + decimal: true), + validator: (value) { + if (value!.trim().isEmpty && + _mgPerDlController.text + .trim() + .isEmpty) { + return 'How many mg/dl or mmol/l does rhe rate make up for?'; + } + return null; + }, + ), + ) + : Container(), + glucoseDisplayMode == GlucoseDisplayMode.both || + glucoseDisplayMode == + GlucoseDisplayMode.bothForDetail + ? IconButton( + onPressed: () => + convertBetweenMgPerDlAndMmolPerL( + calculateFrom: + GlucoseMeasurement.mgPerDl), + icon: const Icon(Icons.calculate), + ) + : Container(), + ], + ), + TextFormField( + decoration: const InputDecoration( + labelText: 'Bolus Units', + suffixText: 'U', + ), + controller: _bolusGlucoseController, + keyboardType: const TextInputType.numberWithOptions( + decimal: true), + ), + // ignore: todo + // TODO: change field functionality according to time format + TextFormField( + decoration: const InputDecoration( + labelText: 'Delayed Bolus Duration', + suffixText: ' min', + ), + controller: _delayedBolusDurationController, + keyboardType: TextInputType.number, + ), + TextFormField( + decoration: const InputDecoration( + labelText: 'Delayed Bolus Units', + ), + controller: _delayedBolusRateController, + keyboardType: const TextInputType.numberWithOptions( + decimal: true), + ), + TextFormField( + controller: _notesController, + decoration: const InputDecoration( + labelText: 'Notes', + alignLabelWithHint: true, + ), + keyboardType: TextInputType.multiline, + ), + ], + ), + ]), + ), ]; if (!_isNew) { @@ -272,8 +476,7 @@ class _LogEntryScreenState extends State { ), bottomNavigationBar: bottomNav, floatingActionButton: actionButton, - floatingActionButtonLocation: - FloatingActionButtonLocation.endFloat, + floatingActionButtonLocation: FloatingActionButtonLocation.endFloat, ); }), ); diff --git a/lib/screens/log/log_entry_form.dart b/lib/screens/log/log_entry_form.dart deleted file mode 100644 index c969c2b..0000000 --- a/lib/screens/log/log_entry_form.dart +++ /dev/null @@ -1,185 +0,0 @@ -import 'package:diameter/components/forms.dart'; -import 'package:diameter/config.dart'; -import 'package:diameter/settings.dart'; -import 'package:diameter/utils/utils.dart'; -import 'package:flutter/material.dart'; - -class LogEntryForm extends StatefulWidget { - final GlobalKey formState; - final Map controllers; - - const LogEntryForm( - {Key? key, required this.formState, required this.controllers}) - : super(key: key); - - @override - _LogEntryFormState createState() => _LogEntryFormState(); -} - -class _LogEntryFormState extends State { - void convertBetweenMgPerDlAndMmolPerL({GlucoseMeasurement? calculateFrom}) { - int? mgPerDl; - double? mmolPerL; - final _mgPerDlController = widget.controllers['mgPerDl']; - final _mmolPerLController = widget.controllers['mmolPerL']; - - if (calculateFrom != GlucoseMeasurement.mmolPerL && - _mgPerDlController!.text != '') { - mgPerDl = int.tryParse(_mgPerDlController.text); - } - if (calculateFrom != GlucoseMeasurement.mgPerDl && - _mmolPerLController!.text != '') { - mmolPerL = double.tryParse(_mmolPerLController.text); - } - - if (mgPerDl != null && mmolPerL == null) { - setState(() { - _mmolPerLController!.text = - Utils.convertMgPerDlToMmolPerL(mgPerDl!).toString(); - }); - } - if (mmolPerL != null && mgPerDl == null) { - setState(() { - _mgPerDlController!.text = - Utils.convertMmolPerLToMgPerDl(mmolPerL!).toString(); - }); - } - } - - @override - Widget build(BuildContext context) { - // final _timeController = widget.controllers['time']; - final _mgPerDlController = widget.controllers['mgPerDl']; - final _mmolPerLController = widget.controllers['mmolPerL']; - final _bolusGlucoseController = widget.controllers['bolusGlucose']; - final _delayedBolusRateController = widget.controllers['delayedBolusRate']; - final _delayedBolusDurationController = - widget.controllers['delayedBolusDuration']; - final _notesController = widget.controllers['notes']; - - return SingleChildScrollView( - child: Column(crossAxisAlignment: CrossAxisAlignment.stretch, children: < - Widget>[ - StyledForm( - formState: widget.formState, - fields: [ - // TODO: insert time picker - // Expanded( - // child: StyledTimeOfDayFormField( - // label: 'Time', - // controller: _timeController, - // onChanged: (newEndTime) { - // if (newEndTime != null) { - // setState(() { - // _endTime = newEndTime; - // }); - // updateEndTime(); - // } - //), - Row( - children: [ - glucoseMeasurement == GlucoseMeasurement.mgPerDl || - glucoseDisplayMode == GlucoseDisplayMode.both || - glucoseDisplayMode == GlucoseDisplayMode.bothForDetail - ? Expanded( - child: TextFormField( - decoration: const InputDecoration( - labelText: 'mg/dl', - suffixText: 'mg/dl', - ), - controller: _mgPerDlController, - onChanged: (_) => convertBetweenMgPerDlAndMmolPerL( - calculateFrom: GlucoseMeasurement.mgPerDl), - keyboardType: const TextInputType.numberWithOptions(), - validator: (value) { - if (value!.trim().isEmpty && - _mmolPerLController!.text.trim().isEmpty) { - return 'How many mg/dl or mmol/l does the rate make up for?'; - } - return null; - }, - ), - ) - : Container(), - glucoseDisplayMode == GlucoseDisplayMode.both || - glucoseDisplayMode == GlucoseDisplayMode.bothForDetail - ? IconButton( - onPressed: () => convertBetweenMgPerDlAndMmolPerL( - calculateFrom: GlucoseMeasurement.mmolPerL), - icon: const Icon(Icons.calculate), - ) - : Container(), - glucoseMeasurement == GlucoseMeasurement.mmolPerL || - glucoseDisplayMode == GlucoseDisplayMode.both || - glucoseDisplayMode == GlucoseDisplayMode.bothForDetail - ? Expanded( - child: TextFormField( - decoration: const InputDecoration( - labelText: 'mmol/l', - suffixText: 'mmol/l', - ), - controller: _mmolPerLController, - onChanged: (_) => convertBetweenMgPerDlAndMmolPerL( - calculateFrom: GlucoseMeasurement.mmolPerL), - keyboardType: const TextInputType.numberWithOptions( - decimal: true), - validator: (value) { - if (value!.trim().isEmpty && - _mgPerDlController!.text.trim().isEmpty) { - return 'How many mg/dl or mmol/l does rhe rate make up for?'; - } - return null; - }, - ), - ) - : Container(), - glucoseDisplayMode == GlucoseDisplayMode.both || - glucoseDisplayMode == GlucoseDisplayMode.bothForDetail - ? IconButton( - onPressed: () => convertBetweenMgPerDlAndMmolPerL( - calculateFrom: GlucoseMeasurement.mgPerDl), - icon: const Icon(Icons.calculate), - ) - : Container(), - ], - ), - TextFormField( - decoration: const InputDecoration( - labelText: 'Bolus Units', - suffixText: 'U', - ), - controller: _bolusGlucoseController, - keyboardType: - const TextInputType.numberWithOptions(decimal: true), - ), - // TODO: change field functionality according to time format - TextFormField( - decoration: const InputDecoration( - labelText: 'Delayed Bolus Duration', - suffixText: ' min', - ), - controller: _delayedBolusDurationController, - keyboardType: TextInputType.number, - ), - TextFormField( - decoration: const InputDecoration( - labelText: 'Delayed Bolus Units', - ), - controller: _delayedBolusRateController, - keyboardType: - const TextInputType.numberWithOptions(decimal: true), - ), - TextFormField( - controller: _notesController, - decoration: const InputDecoration( - labelText: 'Notes', - alignLabelWithHint: true, - ), - keyboardType: TextInputType.multiline, - ), - ], - ), - ]), - ); - } -} diff --git a/lib/screens/log/log_event_detail.dart b/lib/screens/log/log_event_detail.dart index 61145d9..aea8da1 100644 --- a/lib/screens/log/log_event_detail.dart +++ b/lib/screens/log/log_event_detail.dart @@ -114,10 +114,10 @@ class _LogEventDetailScreenState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ - StyledForm( + FormWrapper( formState: _logEventForm, fields: [ - StyledDropdownButton( + LabeledDropdownButton( selectedItem: _eventType, label: 'Event Type', items: _logEventTypes, @@ -128,26 +128,14 @@ class _LogEventDetailScreenState extends State { }); }, ), - // StyledFutureDropdownButton( - // selectedItem: _eventType, - // label: 'Event Type', - // items: _logEventTypes, - // getItemValue: (item) => item.objectId, - // renderItem: (item) => Text(item.value), - // onChanged: (value) { - // setState(() { - // _eventType = value; - // }); - // }, - // ), - StyledBooleanFormField( + BooleanFormField( value: _hasEndTime, onChanged: (value) { setState(() { _hasEndTime = value; }); }, - label: 'active', + label: 'has end time', ), TextFormField( controller: _notesController, diff --git a/lib/screens/log/log_event_list.dart b/lib/screens/log/log_event_list.dart index bd9503d..46036ef 100644 --- a/lib/screens/log/log_event_list.dart +++ b/lib/screens/log/log_event_list.dart @@ -68,7 +68,6 @@ class _LogEventListScreenState extends State { return Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ - // TODO: add button for active events Expanded( child: (widget.logEntry.events.isNotEmpty || widget.logEntry.endedEvents.isNotEmpty) ? ListView.builder( diff --git a/lib/screens/log/log_event_type_detail.dart b/lib/screens/log/log_event_type_detail.dart index 9bfa4db..bd80755 100644 --- a/lib/screens/log/log_event_type_detail.dart +++ b/lib/screens/log/log_event_type_detail.dart @@ -45,22 +45,6 @@ class _LogEventTypeDetailScreenState extends State { }); if (_logEventTypeForm.currentState!.validate()) { bool isNew = widget.logEventType == null; - // isNew - // ? await LogEventType.save( - // value: _valueController.text, - // notes: _notesController.text, - // defaultReminderDuration: - // int.tryParse(_defaultReminderDurationController.text), - // hasEndTime: _hasEndTime, - // ) - // : await LogEventType.update( - // widget.logEventType!.objectId!, - // value: _valueController.text, - // notes: _notesController.text, - // defaultReminderDuration: - // int.tryParse(_defaultReminderDurationController.text), - // hasEndTime: _hasEndTime, - // ); LogEventType.put(LogEventType( id: widget.logEventType?.id ?? 0, value: _valueController.text, @@ -115,7 +99,7 @@ class _LogEventTypeDetailScreenState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ - StyledForm( + FormWrapper( formState: _logEventTypeForm, fields: [ TextFormField( @@ -131,7 +115,7 @@ class _LogEventTypeDetailScreenState extends State { return null; }, ), - StyledBooleanFormField( + BooleanFormField( value: _hasEndTime, label: 'has end time', onChanged: (value) { diff --git a/lib/screens/log/log_meal_detail.dart b/lib/screens/log/log_meal_detail.dart index 540e302..b188ad8 100644 --- a/lib/screens/log/log_meal_detail.dart +++ b/lib/screens/log/log_meal_detail.dart @@ -315,7 +315,7 @@ class _LogMealDetailScreenState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ - StyledForm( + FormWrapper( formState: _logMealForm, fields: [ TextFormField( @@ -330,7 +330,7 @@ class _LogMealDetailScreenState extends State { return null; }, ), - StyledDropdownButton( + LabeledDropdownButton( selectedItem: _meal, label: 'Meal', items: _meals, @@ -342,7 +342,7 @@ class _LogMealDetailScreenState extends State { } }, ), - StyledDropdownButton( + LabeledDropdownButton( selectedItem: _mealSource, label: 'Meal Source', items: _mealSources, @@ -354,7 +354,7 @@ class _LogMealDetailScreenState extends State { }); }, ), - StyledDropdownButton( + LabeledDropdownButton( selectedItem: _mealCategory, label: 'Meal Category', items: _mealCategories, @@ -366,7 +366,7 @@ class _LogMealDetailScreenState extends State { }); }, ), - StyledDropdownButton( + LabeledDropdownButton( selectedItem: _mealPortionType, label: 'Meal Portion Type', items: _mealPortionTypes, @@ -434,7 +434,7 @@ class _LogMealDetailScreenState extends State { ), ], ), - StyledDropdownButton( + LabeledDropdownButton( selectedItem: _portionSizeAccuracy, label: 'Portion Size Accuracy', items: _portionSizeAccuracies, @@ -488,7 +488,7 @@ class _LogMealDetailScreenState extends State { ), ], ), - StyledDropdownButton( + LabeledDropdownButton( selectedItem: _carbsRatioAccuracy, label: 'Carbs Ratio Accuracy', items: _carbsRatioAccuracies, diff --git a/lib/screens/meal/meal_category_detail.dart b/lib/screens/meal/meal_category_detail.dart index 0414f45..9967b78 100644 --- a/lib/screens/meal/meal_category_detail.dart +++ b/lib/screens/meal/meal_category_detail.dart @@ -78,7 +78,7 @@ class _MealCategoryDetailScreenState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ - StyledForm( + FormWrapper( formState: _mealCategoryForm, fields: [ TextFormField( diff --git a/lib/screens/meal/meal_detail.dart b/lib/screens/meal/meal_detail.dart index c7ca8b3..ac09991 100644 --- a/lib/screens/meal/meal_detail.dart +++ b/lib/screens/meal/meal_detail.dart @@ -266,7 +266,7 @@ class _MealDetailScreenState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ - StyledForm( + FormWrapper( formState: _mealForm, fields: [ TextFormField( @@ -281,7 +281,7 @@ class _MealDetailScreenState extends State { return null; }, ), - StyledDropdownButton( + LabeledDropdownButton( selectedItem: _mealSource, label: 'Meal Source', items: _mealSources, @@ -293,7 +293,7 @@ class _MealDetailScreenState extends State { } }, ), - StyledDropdownButton( + LabeledDropdownButton( selectedItem: _mealCategory, label: 'Meal Category', items: _mealCategories, @@ -305,7 +305,7 @@ class _MealDetailScreenState extends State { }); }, ), - StyledDropdownButton( + LabeledDropdownButton( selectedItem: _mealPortionType, label: 'Meal Portion Type', items: _mealPortionTypes, @@ -373,7 +373,7 @@ class _MealDetailScreenState extends State { ), ], ), - StyledDropdownButton( + LabeledDropdownButton( selectedItem: _portionSizeAccuracy, label: 'Portion Size Accuracy', items: _portionSizeAccuracies, @@ -385,18 +385,6 @@ class _MealDetailScreenState extends State { }); }, ), - // StyledFutureDropdownButton( - // selectedItem: _portionSizeAccuracy, - // label: 'Portion Size Accuracy', - // items: _portionSizeAccuracies, - // getItemValue: (item) => item.objectId, - // renderItem: (item) => Text(item.value), - // onChanged: (value) { - // setState(() { - // _portionSizeAccuracy = value; - // }); - // }, - // ), Row( children: [ Expanded( @@ -427,7 +415,7 @@ class _MealDetailScreenState extends State { ), ], ), - StyledDropdownButton( + LabeledDropdownButton( selectedItem: _carbsRatioAccuracy, label: 'Carbs Ratio Accuracy', items: _carbsRatioAccuracies, @@ -439,18 +427,7 @@ class _MealDetailScreenState extends State { }); }, ), - // StyledFutureDropdownButton( - // selectedItem: _carbsRatioAccuracy, - // label: 'Carbs Ratio Accuracy', - // items: _carbsRatioAccuracies, - // getItemValue: (item) => item.objectId, - // renderItem: (item) => Text(item.value), - // onChanged: (value) { - // setState(() { - // _carbsRatioAccuracy = value; - // }); - // }, - // ), + // ignore: todo // TODO: display according to time format TextFormField( decoration: const InputDecoration( diff --git a/lib/screens/meal/meal_portion_type_detail.dart b/lib/screens/meal/meal_portion_type_detail.dart index 1b6ef56..fb53e07 100644 --- a/lib/screens/meal/meal_portion_type_detail.dart +++ b/lib/screens/meal/meal_portion_type_detail.dart @@ -89,7 +89,7 @@ class _MealPortionTypeDetailScreenState child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ - StyledForm( + FormWrapper( formState: _mealPortionTypeForm, fields: [ TextFormField( diff --git a/lib/screens/meal/meal_source_detail.dart b/lib/screens/meal/meal_source_detail.dart index 595286c..5a512f7 100644 --- a/lib/screens/meal/meal_source_detail.dart +++ b/lib/screens/meal/meal_source_detail.dart @@ -145,7 +145,7 @@ class _MealSourceDetailScreenState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ - StyledForm( + FormWrapper( formState: _mealSourceForm, fields: [ TextFormField( @@ -160,7 +160,7 @@ class _MealSourceDetailScreenState extends State { return null; }, ), - StyledDropdownButton( + LabeledDropdownButton( selectedItem: _defaultCarbsRatioAccuracy, label: 'Default Carbs Ratio Accuracy', items: _carbsRatioAccuracies, @@ -171,7 +171,7 @@ class _MealSourceDetailScreenState extends State { }); }, ), - StyledDropdownButton( + LabeledDropdownButton( selectedItem: _defaultPortionSizeAccuracy, label: 'Default Portion Size Accuracy', items: _portionSizeAccuracies, @@ -206,7 +206,7 @@ class _MealSourceDetailScreenState extends State { // }); // }, // ), - StyledDropdownButton( + LabeledDropdownButton( selectedItem: _defaultMealCategory, label: 'Default Meal Category', items: _mealCategories, @@ -218,7 +218,7 @@ class _MealSourceDetailScreenState extends State { }); }, ), - StyledDropdownButton( + LabeledDropdownButton( selectedItem: _defaultMealPortionType, label: 'Default Meal Portion Type', items: _mealPortionTypes, diff --git a/lib/settings.dart b/lib/settings.dart index f17dd65..33dce90 100644 --- a/lib/settings.dart +++ b/lib/settings.dart @@ -127,10 +127,10 @@ class _SettingsScreenState extends State { child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ - StyledForm( + FormWrapper( formState: _settingsForm, fields: [ - StyledDropdownButton( + LabeledDropdownButton( selectedItem: nutritionMeasurement, label: 'Preferred Nutrition Measurement', items: NutritionMeasurement.values, @@ -144,7 +144,7 @@ class _SettingsScreenState extends State { } }, ), - StyledDropdownButton( + LabeledDropdownButton( selectedItem: glucoseMeasurement, label: 'Preferred Glucose Measurement', items: GlucoseMeasurement.values, @@ -158,7 +158,7 @@ class _SettingsScreenState extends State { } }, ), - StyledBooleanFormField( + BooleanFormField( value: glucoseDisplayMode == GlucoseDisplayMode.activeOnly, label: 'only display active glucose measurement', onChanged: (_) { @@ -172,7 +172,7 @@ class _SettingsScreenState extends State { }); }, ), - StyledBooleanFormField( + BooleanFormField( value: glucoseDisplayMode == GlucoseDisplayMode.both || glucoseDisplayMode == GlucoseDisplayMode.bothForDetail, enabled: glucoseDisplayMode != GlucoseDisplayMode.activeOnly, @@ -190,7 +190,7 @@ class _SettingsScreenState extends State { }); }, ), - StyledBooleanFormField( + BooleanFormField( value: glucoseDisplayMode == GlucoseDisplayMode.both || glucoseDisplayMode == GlucoseDisplayMode.bothForList, enabled: glucoseDisplayMode != GlucoseDisplayMode.activeOnly, @@ -208,8 +208,6 @@ class _SettingsScreenState extends State { }); }, ), - // TODO: add fields for date and time formats - // TODO: add fields for glucose target ], ), ],