sync server support, additional settings, number field component, usability improvements
This commit is contained in:
parent
6b4f588d5d
commit
09c5230caf
114
TODO
114
TODO
@ -1,53 +1,95 @@
|
|||||||
MAIN TASKS:
|
MAIN TASKS:
|
||||||
General/Framework:
|
Components/Framework:
|
||||||
☐ create app icon
|
☐ update number fields to use corresponding components
|
||||||
☐ show indicator and make all fields readonly if user somehow gets to a deleted record detail view
|
☐ meal detail (carbs ratio, portion size, carbs per portion)
|
||||||
☐ clean up controllers (dispose method of each stateful widget)
|
☐ log meal detail (amount, carbs ratio, portion size, carbs per portion)
|
||||||
☐ account for deleted/disabled elements in dropdowns
|
☐ add "set manually" switch (like in log bolus detail) wherever parameters can be calculated from others
|
||||||
☐ check through all detail forms and set required fields/according messages
|
☐ meal detail
|
||||||
|
☐ log meal detail
|
||||||
|
☐ come up with new concept for duration component
|
||||||
|
☐ update duration fields to use corresponding component
|
||||||
|
☐ log event type detail (reminder duration)
|
||||||
|
☐ log event detail (reminder duration)
|
||||||
|
☐ meal (bolus delay)
|
||||||
|
☐ log bolus (delay)
|
||||||
|
☐ put dropdowns first if they override name field
|
||||||
☐ set name properties as unique (and add checks to forms)
|
☐ set name properties as unique (and add checks to forms)
|
||||||
☐ implement component for durations
|
☐ check through all detail forms and set required fields/according messages
|
||||||
☐ change placement of delete and floating button because its very easy to accidentally hit delete
|
☐ change placement of delete and floating button because its very easy to accidentally hit delete
|
||||||
|
☐ implement deletion by swiping left on item instead?
|
||||||
|
☐ check for changes before navigating as well (not just on cancel)
|
||||||
|
|
||||||
|
Log Overview:
|
||||||
|
☐ only show current day
|
||||||
|
☐ add calendar field on top to navigate
|
||||||
|
☐ use currently selected day when adding a log entry
|
||||||
|
Event Types:
|
||||||
|
☐ add pagination
|
||||||
|
Reports:
|
||||||
|
☐ evaluate what type of reports there should be
|
||||||
|
☐ try out graph/diagram components
|
||||||
|
|
||||||
|
FUTURE TASKS:
|
||||||
|
Features:
|
||||||
|
☐ app icon
|
||||||
|
☐ desktop version
|
||||||
|
☐ add explanations to each section
|
||||||
|
☐ alternate languages
|
||||||
|
☐ log hba1c
|
||||||
|
☐ indicate nested creation process (creating from dropdown etc)
|
||||||
|
☐ enable restoring data from sync
|
||||||
|
☐ indicate read only fields
|
||||||
|
Components/Framework:
|
||||||
|
☐ show indicator and make all fields readonly if user somehow gets to a deleted record detail view
|
||||||
|
☐ dropdown tweaks
|
||||||
|
☐ edit item -> cancel: shouldn't clear dropdwon
|
||||||
|
☐ keep focus on textfield when typing
|
||||||
|
☐ account for deleted/disabled elements
|
||||||
|
Accuracy:
|
||||||
|
☐ same icons in detail as in overview to indicate what's what
|
||||||
Recipe:
|
Recipe:
|
||||||
✔ recipe list screen @done(21-12-11 22:01)
|
☐ update to use correct components, init/dispose etc
|
||||||
✔ recipe detail screen @done(21-12-11 22:01)
|
☐ change the entire concept of ingredients
|
||||||
☐ add functionality to create a meal from a recipe
|
☐ add functionality to create a meal from a recipe
|
||||||
|
Reports:
|
||||||
|
☐ meal tweaking
|
||||||
|
☐ bolus tweaking
|
||||||
|
☐ basal test
|
||||||
|
☐ daily graph (showing glucose curve, events, boli and meals)
|
||||||
|
Log Overview:
|
||||||
|
☐ add filters
|
||||||
|
Log Entry:
|
||||||
|
☐ check if there is still an active bolus when suggesting glucose bolus
|
||||||
Event Types:
|
Event Types:
|
||||||
☐ add colors as indicators for log entries (and later graphs in reports)
|
☐ add colors as indicators for log entries (and later graphs in reports)
|
||||||
|
☐ implement reminders as push notifications
|
||||||
Settings:
|
Settings:
|
||||||
☐ add setting for decimal places/unit steps
|
☐ add option to hide extra customization options (ie. changing pre calculated values)?
|
||||||
☐ add fields for preferred date and time formats
|
☐ option to switch theme
|
||||||
☐ add fields for glucose target (as map of cutoff glucose and colors)
|
☐ add fields for glucose target tiers (as map of cutoff glucose and colors)
|
||||||
☐ add field for active insulin duration
|
☐ add field for active insulin duration
|
||||||
☐ add setting for carb units/bread units
|
☐ add setting for carb units/bread units
|
||||||
☐ add option to switch 'save' and 'save & close' buttons
|
☐ add option to switch 'save' and 'save & close' buttons
|
||||||
☐ add functionality to delete dead records (meaning: set deleted flag and no relations to undeleted records)
|
☐ add functionality to delete dead records (meaning: set deleted flag and no relations to undeleted records)
|
||||||
|
|
||||||
FUTURE TASKS:
|
|
||||||
General/Framework:
|
|
||||||
☐ setup objectbox sync server
|
|
||||||
☐ add explanations to each section
|
|
||||||
☐ evaluate if some fields should be readonly instead of completely hidden
|
|
||||||
☐ alternate languages
|
|
||||||
☐ log hba1c
|
|
||||||
Reports:
|
|
||||||
☐ evaluate what type of reports there should be
|
|
||||||
☐ meal tweaking
|
|
||||||
☐ bolus tweaking
|
|
||||||
☐ daily graph (showing glucose curve, events, boli and meals)
|
|
||||||
Log Overview:
|
|
||||||
☐ add pagination
|
|
||||||
☐ add filters
|
|
||||||
Log Entry:
|
|
||||||
☐ check if there is still an active bolus when suggesting glucose bolus
|
|
||||||
Event Types:
|
|
||||||
☐ add pagination
|
|
||||||
☐ implement reminders as push notifications
|
|
||||||
Settings:
|
|
||||||
☐ add option to hide extra customization options (ie. changing pre calculated values)?
|
|
||||||
☐ option to switch theme
|
|
||||||
|
|
||||||
Archive:
|
Archive:
|
||||||
|
✔ settings (target glucose, increments) @done(22-01-22 01:48) @project(MAIN TASKS.Components/Framework)
|
||||||
|
✔ accuracy detail (confidence rating) @done(22-01-21 16:51) @project(MAIN TASKS.Components/Framework)
|
||||||
|
✔ basal detail (units) @done(22-01-21 18:14) @project(MAIN TASKS.Components/Framework)
|
||||||
|
✔ bolus detail (units, per carbs, per glucose) @done(22-01-21 20:35) @project(MAIN TASKS.Components/Framework)
|
||||||
|
✔ log entry (glucose) @done(22-01-22 15:13) @project(MAIN TASKS.Components/Framework)
|
||||||
|
✔ log bolus detail (units, current, target, correction, carbs) @done(22-01-22 22:59) @project(MAIN TASKS.Components/Framework)
|
||||||
|
✔ add dispose methods everywhere and clean up controllers @done(22-01-21 17:55) @project(MAIN TASKS.Components/Framework)
|
||||||
|
✔ fix spacing @done(22-01-21 17:20) @project(MAIN TASKS.Event Types)
|
||||||
|
✔ calculation log meal carbs @done(22-01-08 22:21) @project(BUG FIXES.Log Entry)
|
||||||
|
✔ implement component for durations @done(22-01-08 19:00) @project(MAIN TASKS.General/Framework)
|
||||||
|
✔ make glucose optional @done(22-01-08 19:00) @project(MAIN TASKS.Log Entry)
|
||||||
|
✔ add setting for decimal places/unit steps @done(22-01-08 22:18) @project(MAIN TASKS.Settings)
|
||||||
|
✔ add fields for preferred date and time formats @done(22-01-07 21:06) @project(MAIN TASKS.Settings)
|
||||||
|
✔ add field for glucose target @done(22-01-08 19:00) @project(MAIN TASKS.Settings)
|
||||||
|
✔ setup objectbox sync server @done(21-12-22 15:21) @project(FUTURE TASKS.General/Framework)
|
||||||
|
✔ recipe list screen @done(21-12-11 22:01) @project(MAIN TASKS.Recipe)
|
||||||
|
✔ recipe detail screen @done(21-12-11 22:01) @project(MAIN TASKS.Recipe)
|
||||||
✔ add model for recipe @done(21-12-11 02:23) @project(MAIN TASKS.Recipe)
|
✔ add model for recipe @done(21-12-11 02:23) @project(MAIN TASKS.Recipe)
|
||||||
✔ add model for ingredient (relation betweeen recipe and meal) @done(21-12-11 02:23) @project(MAIN TASKS.Recipe)
|
✔ add model for ingredient (relation betweeen recipe and meal) @done(21-12-11 02:23) @project(MAIN TASKS.Recipe)
|
||||||
✔ give option to specify quantity @done(21-12-11 01:28) @project(MAIN TASKS.Log Entry)
|
✔ give option to specify quantity @done(21-12-11 01:28) @project(MAIN TASKS.Log Entry)
|
||||||
|
@ -26,7 +26,7 @@ apply plugin: 'kotlin-android'
|
|||||||
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
|
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 30
|
compileSdkVersion 31
|
||||||
|
|
||||||
compileOptions {
|
compileOptions {
|
||||||
sourceCompatibility JavaVersion.VERSION_1_8
|
sourceCompatibility JavaVersion.VERSION_1_8
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
buildscript {
|
buildscript {
|
||||||
ext.kotlin_version = '1.3.50'
|
ext.kotlin_version = '1.6.10'
|
||||||
repositories {
|
repositories {
|
||||||
google()
|
google()
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
|
@ -1,239 +0,0 @@
|
|||||||
import 'package:flutter/material.dart';
|
|
||||||
|
|
||||||
class FormWrapper extends StatefulWidget {
|
|
||||||
final List<Widget>? fields;
|
|
||||||
final List<Widget>? buttons;
|
|
||||||
final GlobalKey<FormState>? formState;
|
|
||||||
|
|
||||||
const FormWrapper({Key? key, this.formState, this.fields, this.buttons})
|
|
||||||
: super(key: key);
|
|
||||||
|
|
||||||
@override
|
|
||||||
_FormWrapperState createState() => _FormWrapperState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _FormWrapperState extends State<FormWrapper> {
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Padding(
|
|
||||||
padding: const EdgeInsets.all(10.0),
|
|
||||||
child: Form(
|
|
||||||
key: widget.formState,
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
Column(
|
|
||||||
children: widget.fields
|
|
||||||
?.map((e) => Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(vertical: 5.0),
|
|
||||||
child: e))
|
|
||||||
.toList() ??
|
|
||||||
[],
|
|
||||||
),
|
|
||||||
Container(
|
|
||||||
padding: const EdgeInsets.only(top: 10.0),
|
|
||||||
child: Row(
|
|
||||||
mainAxisSize: MainAxisSize.max,
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: widget.buttons ?? [],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class BooleanFormField extends StatefulWidget {
|
|
||||||
final bool value;
|
|
||||||
final String label;
|
|
||||||
final void Function(bool) onChanged;
|
|
||||||
final bool? enabled;
|
|
||||||
final EdgeInsets? contentPadding;
|
|
||||||
|
|
||||||
const BooleanFormField(
|
|
||||||
{Key? key,
|
|
||||||
required this.value,
|
|
||||||
required this.label,
|
|
||||||
required this.onChanged,
|
|
||||||
this.enabled,
|
|
||||||
this.contentPadding})
|
|
||||||
: super(key: key);
|
|
||||||
|
|
||||||
@override
|
|
||||||
_BooleanFormFieldState createState() => _BooleanFormFieldState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _BooleanFormFieldState extends State<BooleanFormField> {
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return FormField<bool>(builder: (state) {
|
|
||||||
return ListTile(
|
|
||||||
contentPadding: widget.contentPadding,
|
|
||||||
onTap: () => widget.onChanged(!widget.value),
|
|
||||||
trailing: Switch(
|
|
||||||
value: widget.value,
|
|
||||||
onChanged: widget.onChanged,
|
|
||||||
),
|
|
||||||
title: Text(widget.label),
|
|
||||||
enabled: widget.enabled ?? true,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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<DateTimeFormField> {
|
|
||||||
@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().add(const Duration(days: 365)),
|
|
||||||
);
|
|
||||||
widget.onChanged(newTime);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class TimeOfDayFormField extends StatefulWidget {
|
|
||||||
final TimeOfDay time;
|
|
||||||
final TextEditingController controller;
|
|
||||||
final String label;
|
|
||||||
final void Function(TimeOfDay?) onChanged;
|
|
||||||
|
|
||||||
const TimeOfDayFormField(
|
|
||||||
{Key? key,
|
|
||||||
required this.time,
|
|
||||||
required this.controller,
|
|
||||||
required this.label,
|
|
||||||
required this.onChanged})
|
|
||||||
: super(key: key);
|
|
||||||
|
|
||||||
@override
|
|
||||||
_TimeOfDayFormFieldState createState() => _TimeOfDayFormFieldState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _TimeOfDayFormFieldState extends State<TimeOfDayFormField> {
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return TextFormField(
|
|
||||||
readOnly: true,
|
|
||||||
controller: widget.controller,
|
|
||||||
decoration: InputDecoration(
|
|
||||||
labelText: widget.label,
|
|
||||||
),
|
|
||||||
onTap: () async {
|
|
||||||
final newTime = await showTimePicker(
|
|
||||||
context: context,
|
|
||||||
initialTime: widget.time,
|
|
||||||
);
|
|
||||||
widget.onChanged(newTime);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class NumberFormField extends StatefulWidget {
|
|
||||||
final TextEditingController controller;
|
|
||||||
final String label;
|
|
||||||
final String? suffix;
|
|
||||||
final void Function(double?) onChanged;
|
|
||||||
final double? min;
|
|
||||||
final double? max;
|
|
||||||
final double step;
|
|
||||||
|
|
||||||
const NumberFormField(
|
|
||||||
{Key? key,
|
|
||||||
required this.controller,
|
|
||||||
required this.label,
|
|
||||||
this.suffix,
|
|
||||||
required this.onChanged,
|
|
||||||
this.min,
|
|
||||||
this.max,
|
|
||||||
this.step = 1})
|
|
||||||
: super(key: key);
|
|
||||||
|
|
||||||
@override
|
|
||||||
_NumberFormFieldState createState() => _NumberFormFieldState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _NumberFormFieldState extends State<NumberFormField> {
|
|
||||||
void onIncrease() {
|
|
||||||
double value = double.tryParse(widget.controller.text) ?? 0;
|
|
||||||
if (widget.max == null || value + widget.step <= widget.max!) {
|
|
||||||
value += widget.step;
|
|
||||||
widget.onChanged(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void onDecrease() {
|
|
||||||
double value = double.tryParse(widget.controller.text) ?? 0;
|
|
||||||
if (widget.min == null || value - widget.step >= widget.min!) {
|
|
||||||
value -= widget.step;
|
|
||||||
widget.onChanged(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Row(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
IconButton(
|
|
||||||
onPressed: onDecrease,
|
|
||||||
icon: const Icon(Icons.remove),
|
|
||||||
),
|
|
||||||
Expanded(
|
|
||||||
child: TextFormField(
|
|
||||||
controller: widget.controller,
|
|
||||||
decoration: InputDecoration(
|
|
||||||
labelText: widget.label,
|
|
||||||
suffixText: widget.suffix,
|
|
||||||
),
|
|
||||||
keyboardType: const TextInputType.numberWithOptions(decimal: true),
|
|
||||||
onChanged: (value) async {
|
|
||||||
await Future.delayed(const Duration(seconds: 1));
|
|
||||||
widget.onChanged(double.tryParse(value));
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
IconButton(
|
|
||||||
onPressed: onIncrease,
|
|
||||||
icon: const Icon(Icons.add),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
39
lib/components/forms/boolean_form_field.dart
Normal file
39
lib/components/forms/boolean_form_field.dart
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class BooleanFormField extends StatefulWidget {
|
||||||
|
final bool value;
|
||||||
|
final String label;
|
||||||
|
final void Function(bool) onChanged;
|
||||||
|
final bool? enabled;
|
||||||
|
final EdgeInsets? contentPadding;
|
||||||
|
|
||||||
|
const BooleanFormField(
|
||||||
|
{Key? key,
|
||||||
|
required this.value,
|
||||||
|
required this.label,
|
||||||
|
required this.onChanged,
|
||||||
|
this.enabled,
|
||||||
|
this.contentPadding})
|
||||||
|
: super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
_BooleanFormFieldState createState() => _BooleanFormFieldState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _BooleanFormFieldState extends State<BooleanFormField> {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return FormField<bool>(builder: (state) {
|
||||||
|
return ListTile(
|
||||||
|
contentPadding: widget.contentPadding,
|
||||||
|
onTap: () => widget.onChanged(!widget.value),
|
||||||
|
trailing: Switch(
|
||||||
|
value: widget.value,
|
||||||
|
onChanged: widget.onChanged,
|
||||||
|
),
|
||||||
|
title: Text(widget.label),
|
||||||
|
enabled: widget.enabled ?? true,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
47
lib/components/forms/date_time_form_field.dart
Normal file
47
lib/components/forms/date_time_form_field.dart
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
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<DateTimeFormField> {
|
||||||
|
@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().add(const Duration(days: 365)),
|
||||||
|
);
|
||||||
|
widget.onChanged(newTime);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
123
lib/components/forms/duration_form_field.dart
Normal file
123
lib/components/forms/duration_form_field.dart
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class DurationFormField extends StatefulWidget {
|
||||||
|
final String label;
|
||||||
|
final int minutes;
|
||||||
|
final void Function(int?) onChanged;
|
||||||
|
final bool showSteppers;
|
||||||
|
final bool readOnly;
|
||||||
|
final int min;
|
||||||
|
final int? max;
|
||||||
|
final int step;
|
||||||
|
|
||||||
|
const DurationFormField(
|
||||||
|
{Key? key,
|
||||||
|
required this.label,
|
||||||
|
this.minutes = 0,
|
||||||
|
required this.onChanged,
|
||||||
|
this.showSteppers = false,
|
||||||
|
this.readOnly = false,
|
||||||
|
this.min = 0,
|
||||||
|
this.max,
|
||||||
|
this.step = 5})
|
||||||
|
: super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
_DurationFormFieldState createState() => _DurationFormFieldState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _DurationFormFieldState extends State<DurationFormField> {
|
||||||
|
late Duration duration;
|
||||||
|
final TextEditingController controller = TextEditingController(text: '');
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
updateDuration();
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateDuration() {
|
||||||
|
duration = Duration(minutes: widget.minutes);
|
||||||
|
|
||||||
|
int days = duration.inDays;
|
||||||
|
int hours = duration.inHours - days * 24;
|
||||||
|
int minutes = duration.inMinutes - hours * 60;
|
||||||
|
int seconds = duration.inSeconds - minutes * 60;
|
||||||
|
|
||||||
|
String daysString = days > 9 ? '$days d' : days > 0 ? '0$days d' : '00 d';
|
||||||
|
String hoursString = hours > 9 ? ' $hours h' : hours > 0 ? ' 0$hours h' : ' 00 h';
|
||||||
|
String minutesString = minutes > 9 ? ' $minutes m' : minutes > 0 ? ' 0$minutes m' : ' 00 m';
|
||||||
|
String secondsString = seconds > 9 ? ' $seconds s' : seconds > 0 ? ' 0$seconds s' : ' 00 s';
|
||||||
|
controller.text = '$daysString $hoursString $minutesString $secondsString'.trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleChange(String value) async {
|
||||||
|
await Future.delayed(const Duration(seconds: 1));
|
||||||
|
|
||||||
|
int days = int.tryParse(value.split(' d')[0]) ?? 0;
|
||||||
|
int hours = int.tryParse(value.split('d')[1].split(' h')[0]) ?? 0;
|
||||||
|
int minutes = int.tryParse(value.split('h')[1].split(' m')[0]) ?? 0;
|
||||||
|
int seconds = int.tryParse(value.split('m')[1].split(' s')[0]) ?? 0;
|
||||||
|
int updatedMinutes =
|
||||||
|
Duration(days: days, hours: hours, minutes: minutes, seconds: seconds)
|
||||||
|
.inMinutes;
|
||||||
|
|
||||||
|
widget.onChanged(updatedMinutes);
|
||||||
|
setState(() {
|
||||||
|
updateDuration();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void onIncrease() {
|
||||||
|
if (widget.max == null || widget.minutes + widget.step <= widget.max!) {
|
||||||
|
int value = widget.minutes + widget.step;
|
||||||
|
widget.onChanged(value);
|
||||||
|
setState(() {
|
||||||
|
updateDuration();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void onDecrease() {
|
||||||
|
if (widget.minutes - widget.step >= widget.min) {
|
||||||
|
int value = widget.minutes - widget.step;
|
||||||
|
widget.onChanged(value);
|
||||||
|
setState(() {
|
||||||
|
updateDuration();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
widget.showSteppers
|
||||||
|
? IconButton(
|
||||||
|
onPressed: onDecrease,
|
||||||
|
icon: const Icon(Icons.remove),
|
||||||
|
)
|
||||||
|
: Container(),
|
||||||
|
Expanded(
|
||||||
|
child: TextFormField(
|
||||||
|
controller: controller,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: widget.label,
|
||||||
|
),
|
||||||
|
keyboardType: TextInputType.numberWithOptions(
|
||||||
|
decimal: true, signed: widget.min.isNegative),
|
||||||
|
onChanged: handleChange,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
widget.showSteppers
|
||||||
|
? IconButton(
|
||||||
|
onPressed: onIncrease,
|
||||||
|
icon: const Icon(Icons.add),
|
||||||
|
)
|
||||||
|
: Container(),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
45
lib/components/forms/form_wrapper.dart
Normal file
45
lib/components/forms/form_wrapper.dart
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class FormWrapper extends StatefulWidget {
|
||||||
|
final List<Widget>? fields;
|
||||||
|
final List<Widget>? buttons;
|
||||||
|
final GlobalKey<FormState>? formState;
|
||||||
|
|
||||||
|
const FormWrapper({Key? key, this.formState, this.fields, this.buttons})
|
||||||
|
: super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
_FormWrapperState createState() => _FormWrapperState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _FormWrapperState extends State<FormWrapper> {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.all(10.0),
|
||||||
|
child: Form(
|
||||||
|
key: widget.formState,
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Column(
|
||||||
|
children: widget.fields
|
||||||
|
?.map((e) => Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 5.0),
|
||||||
|
child: e))
|
||||||
|
.toList() ??
|
||||||
|
[],
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
padding: const EdgeInsets.only(top: 10.0),
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.max,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: widget.buttons ?? [],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
137
lib/components/forms/number_form_field.dart
Normal file
137
lib/components/forms/number_form_field.dart
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
import 'package:diameter/components/repeat_on_hold_button.dart';
|
||||||
|
import 'package:diameter/utils/utils.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class NumberFormField extends StatefulWidget {
|
||||||
|
final TextEditingController controller;
|
||||||
|
final double min;
|
||||||
|
final double? max;
|
||||||
|
final double step;
|
||||||
|
final String label;
|
||||||
|
final String? suffix;
|
||||||
|
final void Function(double?) onChanged;
|
||||||
|
final bool readOnly;
|
||||||
|
final bool showSteppers;
|
||||||
|
final bool autoRoundToMultipleOfStep;
|
||||||
|
final String? Function(String?)? validator;
|
||||||
|
|
||||||
|
const NumberFormField({
|
||||||
|
Key? key,
|
||||||
|
required this.controller,
|
||||||
|
required this.label,
|
||||||
|
required this.onChanged,
|
||||||
|
this.suffix,
|
||||||
|
this.min = 0,
|
||||||
|
this.max,
|
||||||
|
this.step = 1,
|
||||||
|
this.readOnly = false,
|
||||||
|
this.showSteppers = true,
|
||||||
|
this.autoRoundToMultipleOfStep = false,
|
||||||
|
this.validator,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
_NumberFormFieldState createState() => _NumberFormFieldState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _NumberFormFieldState extends State<NumberFormField> {
|
||||||
|
int precision = 1;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
precision = Utils.getFractionDigitsLength(widget.step) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool onIncrease() {
|
||||||
|
double? currentValue = double.tryParse(widget.controller.text);
|
||||||
|
|
||||||
|
if (currentValue != null &&
|
||||||
|
(widget.max == null || currentValue + widget.step <= widget.max!)) {
|
||||||
|
widget.onChanged(
|
||||||
|
Utils.addDoublesWithPrecision(currentValue, widget.step, precision));
|
||||||
|
setState(() {});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool onDecrease() {
|
||||||
|
double? currentValue = double.tryParse(widget.controller.text);
|
||||||
|
|
||||||
|
if (currentValue != null && (currentValue - widget.step >= widget.min)) {
|
||||||
|
widget.onChanged(
|
||||||
|
Utils.addDoublesWithPrecision(currentValue, -widget.step, precision));
|
||||||
|
setState(() {});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
widget.showSteppers
|
||||||
|
? RepeatOnHoldButton(
|
||||||
|
onTap: onDecrease,
|
||||||
|
child: IconButton(
|
||||||
|
onPressed: double.tryParse(widget.controller.text) != null &&
|
||||||
|
(double.parse(widget.controller.text) - widget.step >=
|
||||||
|
widget.min)
|
||||||
|
? onDecrease
|
||||||
|
: null,
|
||||||
|
icon: const Icon(Icons.remove),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: Container(),
|
||||||
|
Expanded(
|
||||||
|
child: TextFormField(
|
||||||
|
readOnly: widget.readOnly,
|
||||||
|
controller: widget.controller,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: widget.label,
|
||||||
|
suffixText: widget.suffix,
|
||||||
|
),
|
||||||
|
keyboardType: TextInputType.numberWithOptions(
|
||||||
|
decimal: widget.step > 0 && widget.step < 1,
|
||||||
|
signed: widget.min.isNegative),
|
||||||
|
onChanged: (input) async {
|
||||||
|
await Future.delayed(const Duration(seconds: 1));
|
||||||
|
double? value = double.tryParse(input);
|
||||||
|
if (value != null &&
|
||||||
|
widget.autoRoundToMultipleOfStep &&
|
||||||
|
(value % widget.step != 0)) {
|
||||||
|
double remainder = value % widget.step;
|
||||||
|
value =
|
||||||
|
Utils.addDoublesWithPrecision(value, -remainder, precision);
|
||||||
|
if (remainder > widget.step / 2) {
|
||||||
|
value = Utils.addDoublesWithPrecision(
|
||||||
|
value, widget.step, precision);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
widget.onChanged(value);
|
||||||
|
},
|
||||||
|
validator: widget.validator,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
widget.showSteppers
|
||||||
|
? RepeatOnHoldButton(
|
||||||
|
onTap: onIncrease,
|
||||||
|
child: IconButton(
|
||||||
|
onPressed: double.tryParse(widget.controller.text) != null &&
|
||||||
|
(widget.max == null ||
|
||||||
|
double.parse(widget.controller.text) +
|
||||||
|
widget.step <=
|
||||||
|
widget.max!)
|
||||||
|
? onIncrease
|
||||||
|
: null,
|
||||||
|
icon: const Icon(Icons.add),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: Container(),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
40
lib/components/forms/time_of_day_form_field.dart
Normal file
40
lib/components/forms/time_of_day_form_field.dart
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class TimeOfDayFormField extends StatefulWidget {
|
||||||
|
final TimeOfDay time;
|
||||||
|
final TextEditingController controller;
|
||||||
|
final String label;
|
||||||
|
final void Function(TimeOfDay?) onChanged;
|
||||||
|
|
||||||
|
const TimeOfDayFormField(
|
||||||
|
{Key? key,
|
||||||
|
required this.time,
|
||||||
|
required this.controller,
|
||||||
|
required this.label,
|
||||||
|
required this.onChanged})
|
||||||
|
: super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
_TimeOfDayFormFieldState createState() => _TimeOfDayFormFieldState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _TimeOfDayFormFieldState extends State<TimeOfDayFormField> {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return TextFormField(
|
||||||
|
readOnly: true,
|
||||||
|
controller: widget.controller,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: widget.label,
|
||||||
|
),
|
||||||
|
onTap: () async {
|
||||||
|
final newTime = await showTimePicker(
|
||||||
|
context: context,
|
||||||
|
initialTime: widget.time,
|
||||||
|
);
|
||||||
|
widget.onChanged(newTime);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
74
lib/components/repeat_on_hold_button.dart
Normal file
74
lib/components/repeat_on_hold_button.dart
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
import 'dart:math';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class RepeatOnHoldButton extends StatefulWidget {
|
||||||
|
/// Function to be called on tap and on long press.
|
||||||
|
/// Return [false] to signify that the loop should be broken after execution.
|
||||||
|
final bool? Function() onTap;
|
||||||
|
|
||||||
|
/// Specifies whether repetition speeds up when the user keeps holding the button.
|
||||||
|
final bool increaseSpeed;
|
||||||
|
|
||||||
|
/// Specifies how many ms should pass before action is repeated.
|
||||||
|
final int initialRepetitionIntervalMs;
|
||||||
|
|
||||||
|
/// Specifies by how much the interval between actions should be divided after [speedUpAfterTimes] times.
|
||||||
|
final int speedUpFactor;
|
||||||
|
|
||||||
|
/// Specifies how many times [onTap] will be called before increasing the speed.
|
||||||
|
final int speedUpAfterTimes;
|
||||||
|
|
||||||
|
final Widget child;
|
||||||
|
|
||||||
|
const RepeatOnHoldButton({
|
||||||
|
Key? key,
|
||||||
|
required this.onTap,
|
||||||
|
this.increaseSpeed = true,
|
||||||
|
this.initialRepetitionIntervalMs = 250,
|
||||||
|
this.speedUpFactor = 2,
|
||||||
|
this.speedUpAfterTimes = 5,
|
||||||
|
required this.child,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
_RepeatOnHoldButtonState createState() => _RepeatOnHoldButtonState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _RepeatOnHoldButtonState extends State<RepeatOnHoldButton> {
|
||||||
|
bool _isHeld = false;
|
||||||
|
|
||||||
|
void onLongPress() async {
|
||||||
|
setState(() {
|
||||||
|
_isHeld = true;
|
||||||
|
});
|
||||||
|
int holdCycle = 0;
|
||||||
|
int speed = widget.initialRepetitionIntervalMs;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
final result = widget.onTap() ?? true;
|
||||||
|
if (!_isHeld || !result) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
holdCycle++;
|
||||||
|
if (speed > 1 && holdCycle % widget.speedUpAfterTimes == 0) {
|
||||||
|
speed = max(1, (speed ~/ widget.speedUpFactor));
|
||||||
|
}
|
||||||
|
|
||||||
|
await Future.delayed(
|
||||||
|
Duration(
|
||||||
|
milliseconds: speed,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return GestureDetector(
|
||||||
|
onLongPress: onLongPress,
|
||||||
|
onLongPressEnd: (_) => _isHeld = false,
|
||||||
|
child: widget.child,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -37,7 +37,7 @@ Future<void> main() async {
|
|||||||
Sync.isAvailable();
|
Sync.isAvailable();
|
||||||
SyncClient syncClient = Sync.client(
|
SyncClient syncClient = Sync.client(
|
||||||
objectBox.store,
|
objectBox.store,
|
||||||
'wss://127.0.0.1:9999',
|
'ws://192.168.1.184:9999',
|
||||||
SyncCredentials.sharedSecretString(secret)
|
SyncCredentials.sharedSecretString(secret)
|
||||||
);
|
);
|
||||||
syncClient.start();
|
syncClient.start();
|
||||||
|
@ -53,6 +53,11 @@ class Settings {
|
|||||||
int targetGlucoseMgPerDl;
|
int targetGlucoseMgPerDl;
|
||||||
double targetGlucoseMmolPerL;
|
double targetGlucoseMmolPerL;
|
||||||
|
|
||||||
|
double insulinIncrements;
|
||||||
|
double nutritionIncrements;
|
||||||
|
double mmolPerLIncrements;
|
||||||
|
double amountIncrements;
|
||||||
|
|
||||||
String dateFormat;
|
String dateFormat;
|
||||||
String? longDateFormat;
|
String? longDateFormat;
|
||||||
String timeFormat;
|
String timeFormat;
|
||||||
@ -70,6 +75,10 @@ class Settings {
|
|||||||
this.nutritionMeasurementIndex = 0,
|
this.nutritionMeasurementIndex = 0,
|
||||||
this.glucoseDisplayModeIndex = 0,
|
this.glucoseDisplayModeIndex = 0,
|
||||||
this.glucoseMeasurementIndex = 0,
|
this.glucoseMeasurementIndex = 0,
|
||||||
|
this.insulinIncrements = 0.05,
|
||||||
|
this.nutritionIncrements = 0.01,
|
||||||
|
this.mmolPerLIncrements = 0.1,
|
||||||
|
this.amountIncrements = 0.05,
|
||||||
this.dateFormat = 'MM/dd/yy',
|
this.dateFormat = 'MM/dd/yy',
|
||||||
this.longDateFormat = 'MMMM dd, yyyy',
|
this.longDateFormat = 'MMMM dd, yyyy',
|
||||||
this.timeFormat = 'HH:mm',
|
this.timeFormat = 'HH:mm',
|
||||||
@ -78,7 +87,7 @@ class Settings {
|
|||||||
this.showConfirmationDialogOnDelete = true,
|
this.showConfirmationDialogOnDelete = true,
|
||||||
this.showConfirmationDialogOnStopEvent = true,
|
this.showConfirmationDialogOnStopEvent = true,
|
||||||
this.targetGlucoseMgPerDl = 100,
|
this.targetGlucoseMgPerDl = 100,
|
||||||
this.targetGlucoseMmolPerL = 5.49,
|
this.targetGlucoseMmolPerL = 5.5,
|
||||||
this.useDarkTheme = false,
|
this.useDarkTheme = false,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -105,6 +114,10 @@ class Settings {
|
|||||||
static int get targetMgPerDl => get().targetGlucoseMgPerDl;
|
static int get targetMgPerDl => get().targetGlucoseMgPerDl;
|
||||||
static double get targetMmolPerL => get().targetGlucoseMmolPerL;
|
static double get targetMmolPerL => get().targetGlucoseMmolPerL;
|
||||||
|
|
||||||
|
static double get insulinSteps => get().insulinIncrements;
|
||||||
|
static double get nutritionSteps => get().nutritionIncrements;
|
||||||
|
static double get mmolPerLSteps => get().mmolPerLIncrements;
|
||||||
|
|
||||||
static ThemeMode get themeMode =>
|
static ThemeMode get themeMode =>
|
||||||
get().useDarkTheme ? ThemeMode.dark : ThemeMode.light;
|
get().useDarkTheme ? ThemeMode.dark : ThemeMode.light;
|
||||||
|
|
||||||
|
@ -113,14 +113,14 @@ class _NavigationState extends State<Navigation> {
|
|||||||
},
|
},
|
||||||
selected: widget.currentLocation == Routes.events,
|
selected: widget.currentLocation == Routes.events,
|
||||||
),
|
),
|
||||||
ListTile(
|
// ListTile(
|
||||||
title: const Text('Recipes'),
|
// title: const Text('Recipes'),
|
||||||
leading: const Icon(Icons.local_dining),
|
// leading: const Icon(Icons.local_dining),
|
||||||
onTap: () {
|
// onTap: () {
|
||||||
selectDestination(Routes.recipes);
|
// selectDestination(Routes.recipes);
|
||||||
},
|
// },
|
||||||
selected: Routes.recipeRoutes.contains(widget.currentLocation),
|
// selected: Routes.recipeRoutes.contains(widget.currentLocation),
|
||||||
),
|
// ),
|
||||||
ListTile(
|
ListTile(
|
||||||
title: const Text('Meals'),
|
title: const Text('Meals'),
|
||||||
leading: const Icon(Icons.dinner_dining),
|
leading: const Icon(Icons.dinner_dining),
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
import 'package:diameter/components/detail.dart';
|
import 'package:diameter/components/detail.dart';
|
||||||
import 'package:diameter/components/dialogs.dart';
|
import 'package:diameter/components/forms/boolean_form_field.dart';
|
||||||
|
import 'package:diameter/components/forms/number_form_field.dart';
|
||||||
|
import 'package:diameter/utils/dialog_utils.dart';
|
||||||
import 'package:diameter/models/settings.dart';
|
import 'package:diameter/models/settings.dart';
|
||||||
import 'package:diameter/navigation.dart';
|
import 'package:diameter/navigation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:diameter/components/forms.dart';
|
import 'package:diameter/components/forms/form_wrapper.dart';
|
||||||
import 'package:diameter/models/accuracy.dart';
|
import 'package:diameter/models/accuracy.dart';
|
||||||
|
|
||||||
class AccuracyDetailScreen extends StatefulWidget {
|
class AccuracyDetailScreen extends StatefulWidget {
|
||||||
@ -25,8 +27,9 @@ class _AccuracyDetailScreenState extends State<AccuracyDetailScreen> {
|
|||||||
final ScrollController _scrollController = ScrollController();
|
final ScrollController _scrollController = ScrollController();
|
||||||
|
|
||||||
final _valueController = TextEditingController(text: '');
|
final _valueController = TextEditingController(text: '');
|
||||||
final _confidenceRatingController = TextEditingController(text: '');
|
|
||||||
final _notesController = TextEditingController(text: '');
|
final _notesController = TextEditingController(text: '');
|
||||||
|
final _confidenceRatingController =
|
||||||
|
TextEditingController(text: Accuracy.getAll().length.toString());
|
||||||
bool _forCarbsRatio = true;
|
bool _forCarbsRatio = true;
|
||||||
bool _forPortionSize = true;
|
bool _forPortionSize = true;
|
||||||
|
|
||||||
@ -39,11 +42,20 @@ class _AccuracyDetailScreenState extends State<AccuracyDetailScreen> {
|
|||||||
_forCarbsRatio = _accuracy!.forCarbsRatio;
|
_forCarbsRatio = _accuracy!.forCarbsRatio;
|
||||||
_forPortionSize = _accuracy!.forPortionSize;
|
_forPortionSize = _accuracy!.forPortionSize;
|
||||||
_confidenceRatingController.text =
|
_confidenceRatingController.text =
|
||||||
(_accuracy!.confidenceRating ?? '').toString();
|
(_accuracy!.confidenceRating ?? Accuracy.getAll().length).toString();
|
||||||
_notesController.text = _accuracy!.notes ?? '';
|
_notesController.text = _accuracy!.notes ?? '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_scrollController.dispose();
|
||||||
|
_valueController.dispose();
|
||||||
|
_notesController.dispose();
|
||||||
|
_confidenceRatingController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
void reload({String? message}) {
|
void reload({String? message}) {
|
||||||
if (widget.id != 0) {
|
if (widget.id != 0) {
|
||||||
setState(() {
|
setState(() {
|
||||||
@ -77,10 +89,11 @@ class _AccuracyDetailScreenState extends State<AccuracyDetailScreen> {
|
|||||||
forPortionSize: _forPortionSize,
|
forPortionSize: _forPortionSize,
|
||||||
notes: _notesController.text,
|
notes: _notesController.text,
|
||||||
);
|
);
|
||||||
Accuracy.box.put(accuracy);
|
Accuracy.put(accuracy);
|
||||||
Accuracy.reorder(
|
Accuracy.reorder(
|
||||||
accuracy, int.tryParse(_confidenceRatingController.text));
|
accuracy, int.tryParse(_confidenceRatingController.text));
|
||||||
Navigator.pop(context, ['${_isNew ? 'New' : ''} Accuracy saved', accuracy]);
|
Navigator.pop(
|
||||||
|
context, ['${_isNew ? 'New' : ''} Accuracy saved', accuracy]);
|
||||||
}
|
}
|
||||||
setState(() {
|
setState(() {
|
||||||
_isSaving = false;
|
_isSaving = false;
|
||||||
@ -93,7 +106,8 @@ class _AccuracyDetailScreenState extends State<AccuracyDetailScreen> {
|
|||||||
(!_forCarbsRatio ||
|
(!_forCarbsRatio ||
|
||||||
!_forPortionSize ||
|
!_forPortionSize ||
|
||||||
_valueController.text != '' ||
|
_valueController.text != '' ||
|
||||||
int.tryParse(_confidenceRatingController.text) != null ||
|
int.tryParse(_confidenceRatingController.text) !=
|
||||||
|
Accuracy.getAll().length ||
|
||||||
_notesController.text != '')) ||
|
_notesController.text != '')) ||
|
||||||
(!_isNew &&
|
(!_isNew &&
|
||||||
(_forCarbsRatio != _accuracy!.forCarbsRatio ||
|
(_forCarbsRatio != _accuracy!.forCarbsRatio ||
|
||||||
@ -102,7 +116,7 @@ class _AccuracyDetailScreenState extends State<AccuracyDetailScreen> {
|
|||||||
int.tryParse(_confidenceRatingController.text) !=
|
int.tryParse(_confidenceRatingController.text) !=
|
||||||
_accuracy!.confidenceRating ||
|
_accuracy!.confidenceRating ||
|
||||||
(_accuracy!.notes ?? '') != _notesController.text))) {
|
(_accuracy!.notes ?? '') != _notesController.text))) {
|
||||||
Dialogs.showCancelConfirmationDialog(
|
DialogUtils.showCancelConfirmationDialog(
|
||||||
context: context,
|
context: context,
|
||||||
isNew: _isNew,
|
isNew: _isNew,
|
||||||
onSave: handleSaveAction,
|
onSave: handleSaveAction,
|
||||||
@ -159,12 +173,15 @@ class _AccuracyDetailScreenState extends State<AccuracyDetailScreen> {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
TextFormField(
|
NumberFormField(
|
||||||
controller: _confidenceRatingController,
|
controller: _confidenceRatingController,
|
||||||
keyboardType: TextInputType.number,
|
label: 'Confidence Rating',
|
||||||
decoration: const InputDecoration(
|
onChanged: (value) {
|
||||||
labelText: 'Confidence Rating',
|
setState(() {
|
||||||
),
|
_confidenceRatingController.text =
|
||||||
|
(value ?? 0).toInt().toString();
|
||||||
|
});
|
||||||
|
},
|
||||||
),
|
),
|
||||||
TextFormField(
|
TextFormField(
|
||||||
controller: _notesController,
|
controller: _notesController,
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import 'package:diameter/components/dialogs.dart';
|
import 'package:diameter/utils/dialog_utils.dart';
|
||||||
import 'package:diameter/models/settings.dart';
|
import 'package:diameter/models/settings.dart';
|
||||||
import 'package:diameter/navigation.dart';
|
import 'package:diameter/navigation.dart';
|
||||||
import 'package:diameter/screens/accuracy_detail.dart';
|
import 'package:diameter/screens/accuracy_detail.dart';
|
||||||
@ -24,6 +24,12 @@ class _AccuracyListScreenState extends State<AccuracyListScreen> {
|
|||||||
reload();
|
reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_scrollController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
void reload({String? message}) {
|
void reload({String? message}) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_accuracies = Accuracy.getAll();
|
_accuracies = Accuracy.getAll();
|
||||||
@ -49,7 +55,7 @@ class _AccuracyListScreenState extends State<AccuracyListScreen> {
|
|||||||
|
|
||||||
void handleDeleteAction(Accuracy accuracy) async {
|
void handleDeleteAction(Accuracy accuracy) async {
|
||||||
if (Settings.get().showConfirmationDialogOnDelete) {
|
if (Settings.get().showConfirmationDialogOnDelete) {
|
||||||
Dialogs.showConfirmationDialog(
|
DialogUtils.showConfirmationDialog(
|
||||||
context: context,
|
context: context,
|
||||||
onConfirm: () => onDelete(accuracy),
|
onConfirm: () => onDelete(accuracy),
|
||||||
message: 'Are you sure you want to delete this Accuracy?',
|
message: 'Are you sure you want to delete this Accuracy?',
|
||||||
|
@ -1,10 +1,13 @@
|
|||||||
import 'package:diameter/components/detail.dart';
|
import 'package:diameter/components/detail.dart';
|
||||||
import 'package:diameter/components/dialogs.dart';
|
import 'package:diameter/components/forms/number_form_field.dart';
|
||||||
|
import 'package:diameter/components/forms/time_of_day_form_field.dart';
|
||||||
|
import 'package:diameter/utils/dialog_utils.dart';
|
||||||
import 'package:diameter/models/settings.dart';
|
import 'package:diameter/models/settings.dart';
|
||||||
import 'package:diameter/navigation.dart';
|
import 'package:diameter/navigation.dart';
|
||||||
import 'package:diameter/utils/date_time_utils.dart';
|
import 'package:diameter/utils/date_time_utils.dart';
|
||||||
|
import 'package:diameter/utils/utils.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:diameter/components/forms.dart';
|
import 'package:diameter/components/forms/form_wrapper.dart';
|
||||||
import 'package:diameter/models/basal.dart';
|
import 'package:diameter/models/basal.dart';
|
||||||
import 'package:diameter/models/basal_profile.dart';
|
import 'package:diameter/models/basal_profile.dart';
|
||||||
|
|
||||||
@ -42,7 +45,8 @@ class _BasalDetailScreenState extends State<BasalDetailScreen> {
|
|||||||
|
|
||||||
final _startTimeController = TextEditingController(text: '');
|
final _startTimeController = TextEditingController(text: '');
|
||||||
final _endTimeController = TextEditingController(text: '');
|
final _endTimeController = TextEditingController(text: '');
|
||||||
final _unitsController = TextEditingController(text: '');
|
final _unitsController =
|
||||||
|
TextEditingController(text: 0.toStringAsPrecision(3));
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@ -59,13 +63,22 @@ class _BasalDetailScreenState extends State<BasalDetailScreen> {
|
|||||||
if (_basal != null) {
|
if (_basal != null) {
|
||||||
_startTime = TimeOfDay.fromDateTime(_basal!.startTime);
|
_startTime = TimeOfDay.fromDateTime(_basal!.startTime);
|
||||||
_endTime = TimeOfDay.fromDateTime(_basal!.endTime);
|
_endTime = TimeOfDay.fromDateTime(_basal!.endTime);
|
||||||
_unitsController.text = _basal!.units.toString();
|
_unitsController.text = _basal!.units.toStringAsPrecision(3);
|
||||||
}
|
}
|
||||||
|
|
||||||
_startTimeController.text = DateTimeUtils.displayTimeOfDay(_startTime);
|
_startTimeController.text = DateTimeUtils.displayTimeOfDay(_startTime);
|
||||||
_endTimeController.text = DateTimeUtils.displayTimeOfDay(_endTime);
|
_endTimeController.text = DateTimeUtils.displayTimeOfDay(_endTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_scrollController.dispose();
|
||||||
|
_startTimeController.dispose();
|
||||||
|
_endTimeController.dispose();
|
||||||
|
_unitsController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
void reload({String? message}) {
|
void reload({String? message}) {
|
||||||
if (widget.id != 0) {
|
if (widget.id != 0) {
|
||||||
setState(() {
|
setState(() {
|
||||||
@ -207,13 +220,13 @@ class _BasalDetailScreenState extends State<BasalDetailScreen> {
|
|||||||
_startTime.minute !=
|
_startTime.minute !=
|
||||||
(widget.suggestedStartTime?.minute ?? 0) ||
|
(widget.suggestedStartTime?.minute ?? 0) ||
|
||||||
_endTime.minute != (widget.suggestedEndTime?.minute ?? 0) ||
|
_endTime.minute != (widget.suggestedEndTime?.minute ?? 0) ||
|
||||||
double.tryParse(_unitsController.text) != null)) ||
|
double.tryParse(_unitsController.text) != 0)) ||
|
||||||
(!_isNew &&
|
(!_isNew &&
|
||||||
(TimeOfDay.fromDateTime(_basal!.startTime) != _startTime ||
|
(TimeOfDay.fromDateTime(_basal!.startTime) != _startTime ||
|
||||||
TimeOfDay.fromDateTime(_basal!.endTime) != _endTime ||
|
TimeOfDay.fromDateTime(_basal!.endTime) != _endTime ||
|
||||||
(double.tryParse(_unitsController.text) ?? 0) !=
|
double.tryParse(_unitsController.text) !=
|
||||||
_basal!.units)))) {
|
_basal!.units)))) {
|
||||||
Dialogs.showCancelConfirmationDialog(
|
DialogUtils.showCancelConfirmationDialog(
|
||||||
context: context,
|
context: context,
|
||||||
isNew: _isNew,
|
isNew: _isNew,
|
||||||
onSave: handleSaveAction,
|
onSave: handleSaveAction,
|
||||||
@ -266,21 +279,19 @@ class _BasalDetailScreenState extends State<BasalDetailScreen> {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
TextFormField(
|
NumberFormField(
|
||||||
controller: _unitsController,
|
controller: _unitsController,
|
||||||
keyboardType:
|
label: 'Units',
|
||||||
const TextInputType.numberWithOptions(decimal: true),
|
suffix: 'U',
|
||||||
decoration: const InputDecoration(
|
autoRoundToMultipleOfStep: true,
|
||||||
labelText: 'Units',
|
step: Settings.insulinSteps,
|
||||||
suffixText: 'U',
|
onChanged: (value) {
|
||||||
),
|
if (value != null) {
|
||||||
validator: (value) {
|
_unitsController.text =
|
||||||
if (value!.trim().isEmpty) {
|
Utils.toStringMatchingTemplateFractionPrecision(
|
||||||
return 'Empty amount of units';
|
value, Settings.insulinSteps);
|
||||||
}
|
}
|
||||||
return null;
|
}),
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import 'package:diameter/components/dialogs.dart';
|
import 'package:diameter/utils/dialog_utils.dart';
|
||||||
import 'package:diameter/models/settings.dart';
|
import 'package:diameter/models/settings.dart';
|
||||||
import 'package:diameter/utils/date_time_utils.dart';
|
import 'package:diameter/utils/date_time_utils.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
@ -25,6 +25,12 @@ class BasalListScreen extends StatefulWidget {
|
|||||||
class _BasalListScreenState extends State<BasalListScreen> {
|
class _BasalListScreenState extends State<BasalListScreen> {
|
||||||
final ScrollController _scrollController = ScrollController();
|
final ScrollController _scrollController = ScrollController();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_scrollController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
void reload({String? message}) {
|
void reload({String? message}) {
|
||||||
widget.reload();
|
widget.reload();
|
||||||
|
|
||||||
@ -60,7 +66,7 @@ class _BasalListScreenState extends State<BasalListScreen> {
|
|||||||
|
|
||||||
void handleDeleteAction(Basal basal) async {
|
void handleDeleteAction(Basal basal) async {
|
||||||
if (Settings.get().showConfirmationDialogOnDelete) {
|
if (Settings.get().showConfirmationDialogOnDelete) {
|
||||||
Dialogs.showConfirmationDialog(
|
DialogUtils.showConfirmationDialog(
|
||||||
context: context,
|
context: context,
|
||||||
onConfirm: () => onDelete(basal),
|
onConfirm: () => onDelete(basal),
|
||||||
message: 'Are you sure you want to delete this Basal Rate?',
|
message: 'Are you sure you want to delete this Basal Rate?',
|
||||||
@ -106,11 +112,14 @@ class _BasalListScreenState extends State<BasalListScreen> {
|
|||||||
.isNotEmpty) {
|
.isNotEmpty) {
|
||||||
return 'This rate\'s time period overlaps with another one';
|
return 'This rate\'s time period overlaps with another one';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return widget.basalRates.isNotEmpty ? Scrollbar(
|
return widget.basalRates.isNotEmpty
|
||||||
|
? Scrollbar(
|
||||||
controller: _scrollController,
|
controller: _scrollController,
|
||||||
child: ListView.builder(
|
child: ListView.builder(
|
||||||
padding: const EdgeInsets.all(10.0),
|
padding: const EdgeInsets.all(10.0),
|
||||||
@ -129,13 +138,15 @@ class _BasalListScreenState extends State<BasalListScreen> {
|
|||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
Icon(Icons.warning, color: Theme.of(context).errorColor),
|
Icon(Icons.warning,
|
||||||
Text(
|
color: Theme.of(context).errorColor),
|
||||||
error, style: TextStyle(color: Theme.of(context).errorColor)
|
Text(error,
|
||||||
),
|
style: TextStyle(
|
||||||
|
color: Theme.of(context).errorColor)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
) : Container(),
|
)
|
||||||
|
: Container(),
|
||||||
ListTile(
|
ListTile(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
handleEditAction(basal);
|
handleEditAction(basal);
|
||||||
@ -168,7 +179,8 @@ class _BasalListScreenState extends State<BasalListScreen> {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
) : const Center(
|
)
|
||||||
|
: const Center(
|
||||||
child: Text('You have not created any Basal Rates yet!'),
|
child: Text('You have not created any Basal Rates yet!'),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
import 'package:diameter/components/detail.dart';
|
import 'package:diameter/components/detail.dart';
|
||||||
import 'package:diameter/components/dialogs.dart';
|
import 'package:diameter/components/forms/boolean_form_field.dart';
|
||||||
|
import 'package:diameter/utils/dialog_utils.dart';
|
||||||
import 'package:diameter/models/basal.dart';
|
import 'package:diameter/models/basal.dart';
|
||||||
import 'package:diameter/models/settings.dart';
|
import 'package:diameter/models/settings.dart';
|
||||||
import 'package:diameter/navigation.dart';
|
import 'package:diameter/navigation.dart';
|
||||||
import 'package:diameter/screens/basal/basal_detail.dart';
|
import 'package:diameter/screens/basal/basal_detail.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:diameter/components/forms.dart';
|
import 'package:diameter/components/forms/form_wrapper.dart';
|
||||||
import 'package:diameter/models/basal_profile.dart';
|
import 'package:diameter/models/basal_profile.dart';
|
||||||
import 'package:diameter/screens/basal/basal_list.dart';
|
import 'package:diameter/screens/basal/basal_list.dart';
|
||||||
|
|
||||||
@ -92,6 +93,14 @@ class _BasalProfileDetailScreenState extends State<BasalProfileDetailScreen> {
|
|||||||
bottomNav = detailBottomRow;
|
bottomNav = detailBottomRow;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_scrollController.dispose();
|
||||||
|
_nameController.dispose();
|
||||||
|
_notesController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
void reload({String? message}) {
|
void reload({String? message}) {
|
||||||
if (widget.id != 0) {
|
if (widget.id != 0) {
|
||||||
setState(() {
|
setState(() {
|
||||||
@ -270,7 +279,7 @@ class _BasalProfileDetailScreenState extends State<BasalProfileDetailScreen> {
|
|||||||
(_basalProfile!.active != _active ||
|
(_basalProfile!.active != _active ||
|
||||||
_basalProfile!.name != _nameController.text ||
|
_basalProfile!.name != _nameController.text ||
|
||||||
(_basalProfile!.notes ?? '') != _notesController.text))) {
|
(_basalProfile!.notes ?? '') != _notesController.text))) {
|
||||||
Dialogs.showCancelConfirmationDialog(
|
DialogUtils.showCancelConfirmationDialog(
|
||||||
context: context,
|
context: context,
|
||||||
isNew: _isNew,
|
isNew: _isNew,
|
||||||
onSave: handleSaveAction,
|
onSave: handleSaveAction,
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import 'package:diameter/components/dialogs.dart';
|
import 'package:diameter/utils/dialog_utils.dart';
|
||||||
import 'package:diameter/components/dropdown.dart';
|
import 'package:diameter/components/forms/auto_complete_dropdown_button.dart';
|
||||||
import 'package:diameter/models/basal.dart';
|
import 'package:diameter/models/basal.dart';
|
||||||
import 'package:diameter/models/settings.dart';
|
import 'package:diameter/models/settings.dart';
|
||||||
import 'package:diameter/navigation.dart';
|
import 'package:diameter/navigation.dart';
|
||||||
@ -23,7 +23,19 @@ class _BasalProfileListScreenState extends State<BasalProfileListScreen> {
|
|||||||
|
|
||||||
final BasalProfile? _activeProfile = BasalProfile.getActive(DateTime.now());
|
final BasalProfile? _activeProfile = BasalProfile.getActive(DateTime.now());
|
||||||
|
|
||||||
void refresh({String? message}) {
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_scrollController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
void reload({String? message}) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_basalProfiles = BasalProfile.getAll();
|
_basalProfiles = BasalProfile.getAll();
|
||||||
});
|
});
|
||||||
@ -75,14 +87,35 @@ class _BasalProfileListScreenState extends State<BasalProfileListScreen> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void handleDuplicateAction(BasalProfile basalProfile) async {
|
||||||
|
final copy = BasalProfile(
|
||||||
|
active: false,
|
||||||
|
name: 'Copy of ${basalProfile.name}',
|
||||||
|
);
|
||||||
|
BasalProfile.put(copy);
|
||||||
|
|
||||||
|
final rates = Basal.getAllForProfile(basalProfile.id);
|
||||||
|
for (Basal rate in rates) {
|
||||||
|
final basal = Basal(
|
||||||
|
endTime: rate.endTime,
|
||||||
|
startTime: rate.startTime,
|
||||||
|
units: rate.units,
|
||||||
|
);
|
||||||
|
basal.basalProfile.target = copy;
|
||||||
|
Basal.put(basal);
|
||||||
|
}
|
||||||
|
|
||||||
|
reload(message: 'Added copy of ${basalProfile.name}');
|
||||||
|
}
|
||||||
|
|
||||||
void onDelete(BasalProfile basalProfile) {
|
void onDelete(BasalProfile basalProfile) {
|
||||||
BasalProfile.remove(basalProfile.id);
|
BasalProfile.remove(basalProfile.id);
|
||||||
refresh(message: 'Basal Profile deleted');
|
reload(message: 'Basal Profile deleted');
|
||||||
}
|
}
|
||||||
|
|
||||||
void handleDeleteAction(BasalProfile basalProfile) async {
|
void handleDeleteAction(BasalProfile basalProfile) async {
|
||||||
if (Settings.get().showConfirmationDialogOnDelete) {
|
if (Settings.get().showConfirmationDialogOnDelete) {
|
||||||
Dialogs.showConfirmationDialog(
|
DialogUtils.showConfirmationDialog(
|
||||||
context: context,
|
context: context,
|
||||||
onConfirm: () => onDelete(basalProfile),
|
onConfirm: () => onDelete(basalProfile),
|
||||||
message: 'Are you sure you want to delete this Basal Profile?',
|
message: 'Are you sure you want to delete this Basal Profile?',
|
||||||
@ -97,7 +130,7 @@ class _BasalProfileListScreenState extends State<BasalProfileListScreen> {
|
|||||||
BasalProfile.setAllInactive;
|
BasalProfile.setAllInactive;
|
||||||
basalProfile.active = true;
|
basalProfile.active = true;
|
||||||
BasalProfile.put(basalProfile);
|
BasalProfile.put(basalProfile);
|
||||||
refresh(
|
reload(
|
||||||
message: '${basalProfile.name} has been set as your active Profile');
|
message: '${basalProfile.name} has been set as your active Profile');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -130,7 +163,7 @@ class _BasalProfileListScreenState extends State<BasalProfileListScreen> {
|
|||||||
builder: (context) =>
|
builder: (context) =>
|
||||||
BasalProfileDetailScreen(id: basalProfile?.id ?? 0, active: active),
|
BasalProfileDetailScreen(id: basalProfile?.id ?? 0, active: active),
|
||||||
),
|
),
|
||||||
).then((result) => refresh(message: result?[0]));
|
).then((result) => reload(message: result?[0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
void onNew(bool active) {
|
void onNew(bool active) {
|
||||||
@ -141,19 +174,13 @@ class _BasalProfileListScreenState extends State<BasalProfileListScreen> {
|
|||||||
showDetailScreen(basalProfile: basalProfile);
|
showDetailScreen(basalProfile: basalProfile);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
refresh();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: const Text('Basal Profiles'),
|
title: const Text('Basal Profiles'),
|
||||||
actions: <Widget>[
|
actions: <Widget>[
|
||||||
IconButton(onPressed: refresh, icon: const Icon(Icons.refresh))
|
IconButton(onPressed: reload, icon: const Icon(Icons.refresh))
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
drawer:
|
drawer:
|
||||||
@ -186,22 +213,23 @@ class _BasalProfileListScreenState extends State<BasalProfileListScreen> {
|
|||||||
basalProfile.id == _activeProfile?.id,
|
basalProfile.id == _activeProfile?.id,
|
||||||
onTap: () => onEdit(basalProfile),
|
onTap: () => onEdit(basalProfile),
|
||||||
title: Text(
|
title: Text(
|
||||||
basalProfile.name.toUpperCase() + activeProfileText,
|
basalProfile.name.toUpperCase() +
|
||||||
|
activeProfileText,
|
||||||
style: Theme.of(context).textTheme.subtitle2,
|
style: Theme.of(context).textTheme.subtitle2,
|
||||||
),
|
),
|
||||||
subtitle: Padding(
|
subtitle: Padding(
|
||||||
padding: const EdgeInsets.only(top: 10.0),
|
padding: const EdgeInsets.only(top: 10.0),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Text(basalProfile.notes ?? ''),
|
||||||
basalProfile.notes ?? ''
|
|
||||||
),
|
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Column(
|
child: Column(
|
||||||
children: dailyTotal > 0
|
children: dailyTotal > 0
|
||||||
? [
|
? [
|
||||||
Text(dailyTotal.toStringAsPrecision(3)),
|
Text(dailyTotal
|
||||||
const Text('U/day', textScaleFactor: 0.75),
|
.toStringAsPrecision(3)),
|
||||||
|
const Text('U/day',
|
||||||
|
textScaleFactor: 0.75),
|
||||||
]
|
]
|
||||||
: [],
|
: [],
|
||||||
),
|
),
|
||||||
@ -212,12 +240,21 @@ class _BasalProfileListScreenState extends State<BasalProfileListScreen> {
|
|||||||
trailing: Row(
|
trailing: Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(
|
||||||
|
Icons.copy,
|
||||||
|
color: Colors.blue,
|
||||||
|
),
|
||||||
|
onPressed: () =>
|
||||||
|
handleDuplicateAction(basalProfile),
|
||||||
|
),
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: const Icon(
|
icon: const Icon(
|
||||||
Icons.delete,
|
Icons.delete,
|
||||||
color: Colors.blue,
|
color: Colors.blue,
|
||||||
),
|
),
|
||||||
onPressed: () => handleDeleteAction(basalProfile),
|
onPressed: () =>
|
||||||
|
handleDeleteAction(basalProfile),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
import 'package:diameter/components/detail.dart';
|
import 'package:diameter/components/detail.dart';
|
||||||
import 'package:diameter/components/dialogs.dart';
|
import 'package:diameter/components/forms/number_form_field.dart';
|
||||||
|
import 'package:diameter/components/forms/time_of_day_form_field.dart';
|
||||||
|
import 'package:diameter/utils/dialog_utils.dart';
|
||||||
import 'package:diameter/models/settings.dart';
|
import 'package:diameter/models/settings.dart';
|
||||||
import 'package:diameter/navigation.dart';
|
import 'package:diameter/navigation.dart';
|
||||||
import 'package:diameter/utils/date_time_utils.dart';
|
import 'package:diameter/utils/date_time_utils.dart';
|
||||||
import 'package:diameter/utils/utils.dart';
|
import 'package:diameter/utils/utils.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:diameter/components/forms.dart';
|
import 'package:diameter/components/forms/form_wrapper.dart';
|
||||||
import 'package:diameter/models/bolus.dart';
|
import 'package:diameter/models/bolus.dart';
|
||||||
import 'package:diameter/models/bolus_profile.dart';
|
import 'package:diameter/models/bolus_profile.dart';
|
||||||
|
|
||||||
@ -43,10 +45,10 @@ class _BolusDetailScreenState extends State<BolusDetailScreen> {
|
|||||||
|
|
||||||
final _startTimeController = TextEditingController(text: '');
|
final _startTimeController = TextEditingController(text: '');
|
||||||
final _endTimeController = TextEditingController(text: '');
|
final _endTimeController = TextEditingController(text: '');
|
||||||
final _unitsController = TextEditingController(text: '');
|
final _unitsController = TextEditingController(text: Utils.toStringMatchingTemplateFractionPrecision(0, Settings.insulinSteps));
|
||||||
final _carbsController = TextEditingController(text: '');
|
final _carbsController = TextEditingController(text: Utils.toStringMatchingTemplateFractionPrecision(0, Settings.nutritionSteps));
|
||||||
final _mgPerDlController = TextEditingController(text: '');
|
final _mgPerDlController = TextEditingController(text: '0');
|
||||||
final _mmolPerLController = TextEditingController(text: '');
|
final _mmolPerLController = TextEditingController(text: Utils.toStringMatchingTemplateFractionPrecision(0, Settings.mmolPerLSteps));
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@ -74,6 +76,18 @@ class _BolusDetailScreenState extends State<BolusDetailScreen> {
|
|||||||
_endTimeController.text = DateTimeUtils.displayTimeOfDay(_endTime);
|
_endTimeController.text = DateTimeUtils.displayTimeOfDay(_endTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_scrollController.dispose();
|
||||||
|
_startTimeController.dispose();
|
||||||
|
_endTimeController.dispose();
|
||||||
|
_unitsController.dispose();
|
||||||
|
_carbsController.dispose();
|
||||||
|
_mgPerDlController.dispose();
|
||||||
|
_mmolPerLController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
void reload({String? message}) {
|
void reload({String? message}) {
|
||||||
if (widget.id != 0) {
|
if (widget.id != 0) {
|
||||||
setState(() {
|
setState(() {
|
||||||
@ -196,7 +210,8 @@ class _BolusDetailScreenState extends State<BolusDetailScreen> {
|
|||||||
).then((result) {
|
).then((result) {
|
||||||
Navigator.pop(
|
Navigator.pop(
|
||||||
context,
|
context,
|
||||||
['New Bolus Rate${result[1] != null ? 's' : ''} saved', bolus] + [result[1]],
|
['New Bolus Rate${result[1] != null ? 's' : ''} saved', bolus] +
|
||||||
|
[result[1]],
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
@ -235,7 +250,7 @@ class _BolusDetailScreenState extends State<BolusDetailScreen> {
|
|||||||
_bolus!.mgPerDl ||
|
_bolus!.mgPerDl ||
|
||||||
(double.tryParse(_mmolPerLController.text) ?? 0) !=
|
(double.tryParse(_mmolPerLController.text) ?? 0) !=
|
||||||
_bolus!.mmolPerL)))) {
|
_bolus!.mmolPerL)))) {
|
||||||
Dialogs.showCancelConfirmationDialog(
|
DialogUtils.showCancelConfirmationDialog(
|
||||||
context: context,
|
context: context,
|
||||||
isNew: _isNew,
|
isNew: _isNew,
|
||||||
onSave: handleSaveAction,
|
onSave: handleSaveAction,
|
||||||
@ -245,32 +260,28 @@ class _BolusDetailScreenState extends State<BolusDetailScreen> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void convertBetweenMgPerDlAndMmolPerL({GlucoseMeasurement? calculateFrom}) {
|
void convertBetweenMgPerDlAndMmolPerL(double? value) async {
|
||||||
int? mgPerDl;
|
if (value != null) {
|
||||||
double? mmolPerL;
|
if (Settings.glucoseMeasurement == GlucoseMeasurement.mgPerDl &&
|
||||||
|
|
||||||
if (calculateFrom != GlucoseMeasurement.mmolPerL &&
|
|
||||||
_mgPerDlController.text != '') {
|
_mgPerDlController.text != '') {
|
||||||
mgPerDl = int.tryParse(_mgPerDlController.text);
|
_mgPerDlController.text = value.toInt().toString();
|
||||||
}
|
|
||||||
if (calculateFrom != GlucoseMeasurement.mgPerDl &&
|
|
||||||
_mmolPerLController.text != '') {
|
|
||||||
mmolPerL = double.tryParse(_mmolPerLController.text);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mgPerDl != null && mmolPerL == null) {
|
|
||||||
setState(() {
|
setState(() {
|
||||||
_mmolPerLController.text =
|
_mmolPerLController.text =
|
||||||
Utils.convertMgPerDlToMmolPerL(mgPerDl!).toString();
|
Utils.convertMgPerDlToMmolPerL(value.toInt()).toString();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (mmolPerL != null && mgPerDl == null) {
|
if (Settings.glucoseMeasurement == GlucoseMeasurement.mmolPerL &&
|
||||||
|
_mmolPerLController.text != '') {
|
||||||
|
_mmolPerLController.text =
|
||||||
|
Utils.toStringMatchingTemplateFractionPrecision(
|
||||||
|
value, Settings.mmolPerLSteps);
|
||||||
setState(() {
|
setState(() {
|
||||||
_mgPerDlController.text =
|
_mgPerDlController.text =
|
||||||
Utils.convertMmolPerLToMgPerDl(mmolPerL!).toString();
|
Utils.convertMmolPerLToMgPerDl(value.toDouble()).toString();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -315,34 +326,32 @@ class _BolusDetailScreenState extends State<BolusDetailScreen> {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
TextFormField(
|
NumberFormField(
|
||||||
decoration: const InputDecoration(
|
|
||||||
labelText: 'Units',
|
|
||||||
suffixText: 'U',
|
|
||||||
),
|
|
||||||
controller: _unitsController,
|
controller: _unitsController,
|
||||||
keyboardType:
|
label: 'Units',
|
||||||
const TextInputType.numberWithOptions(decimal: true),
|
suffix: 'U',
|
||||||
validator: (value) {
|
autoRoundToMultipleOfStep: true,
|
||||||
if (value!.trim().isEmpty) {
|
step: Settings.insulinSteps,
|
||||||
return 'Empty amount of units';
|
onChanged: (value) {
|
||||||
|
if (value != null) {
|
||||||
|
_unitsController.text =
|
||||||
|
Utils.toStringMatchingTemplateFractionPrecision(
|
||||||
|
value, Settings.insulinSteps);
|
||||||
}
|
}
|
||||||
return null;
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
TextFormField(
|
NumberFormField(
|
||||||
decoration: InputDecoration(
|
|
||||||
labelText: 'per carbs',
|
|
||||||
suffixText: Settings.nutritionMeasurementSuffix,
|
|
||||||
),
|
|
||||||
controller: _carbsController,
|
controller: _carbsController,
|
||||||
keyboardType:
|
label: 'per carbs',
|
||||||
const TextInputType.numberWithOptions(decimal: true),
|
suffix: Settings.nutritionMeasurementSuffix,
|
||||||
validator: (value) {
|
autoRoundToMultipleOfStep: true,
|
||||||
if (value!.trim().isEmpty) {
|
step: Settings.nutritionSteps,
|
||||||
return 'How many carbs does the rate make up for?';
|
onChanged: (value) {
|
||||||
|
if (value != null) {
|
||||||
|
_carbsController.text =
|
||||||
|
Utils.toStringMatchingTemplateFractionPrecision(
|
||||||
|
value, Settings.nutritionSteps);
|
||||||
}
|
}
|
||||||
return null;
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
Row(
|
Row(
|
||||||
@ -354,42 +363,18 @@ class _BolusDetailScreenState extends State<BolusDetailScreen> {
|
|||||||
Settings.glucoseDisplayMode ==
|
Settings.glucoseDisplayMode ==
|
||||||
GlucoseDisplayMode.bothForDetail
|
GlucoseDisplayMode.bothForDetail
|
||||||
? Expanded(
|
? Expanded(
|
||||||
child: TextFormField(
|
flex: Settings.glucoseMeasurement == GlucoseMeasurement.mgPerDl ? 2 : 1,
|
||||||
decoration: const InputDecoration(
|
child: NumberFormField(
|
||||||
labelText: 'per mg/dl',
|
label: 'per mg/dl',
|
||||||
suffixText: 'mg/dl',
|
suffix: 'mg/dl',
|
||||||
),
|
|
||||||
readOnly: Settings.glucoseMeasurement ==
|
readOnly: Settings.glucoseMeasurement ==
|
||||||
GlucoseMeasurement.mmolPerL,
|
GlucoseMeasurement.mmolPerL,
|
||||||
|
showSteppers: Settings.glucoseMeasurement == GlucoseMeasurement.mgPerDl,
|
||||||
controller: _mgPerDlController,
|
controller: _mgPerDlController,
|
||||||
onChanged: (_) async {
|
onChanged: convertBetweenMgPerDlAndMmolPerL,
|
||||||
await Future.delayed(
|
|
||||||
const Duration(seconds: 1));
|
|
||||||
convertBetweenMgPerDlAndMmolPerL(
|
|
||||||
calculateFrom:
|
|
||||||
GlucoseMeasurement.mgPerDl);
|
|
||||||
},
|
|
||||||
keyboardType:
|
|
||||||
const TextInputType.numberWithOptions(),
|
|
||||||
validator: (value) {
|
|
||||||
if (value!.trim().isEmpty &&
|
|
||||||
_mmolPerLController.text.trim().isEmpty) {
|
|
||||||
return 'How many mg/dl does the rate make up for?';
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
: Container(),
|
: Container(),
|
||||||
Settings.glucoseDisplayMode == GlucoseDisplayMode.both ||
|
|
||||||
Settings.glucoseDisplayMode ==
|
|
||||||
GlucoseDisplayMode.bothForDetail
|
|
||||||
? IconButton(
|
|
||||||
onPressed: () => convertBetweenMgPerDlAndMmolPerL(
|
|
||||||
calculateFrom: GlucoseMeasurement.mmolPerL),
|
|
||||||
icon: const Icon(Icons.calculate),
|
|
||||||
)
|
|
||||||
: Container(),
|
|
||||||
Settings.glucoseMeasurement ==
|
Settings.glucoseMeasurement ==
|
||||||
GlucoseMeasurement.mmolPerL ||
|
GlucoseMeasurement.mmolPerL ||
|
||||||
[
|
[
|
||||||
@ -397,44 +382,19 @@ class _BolusDetailScreenState extends State<BolusDetailScreen> {
|
|||||||
GlucoseDisplayMode.bothForDetail
|
GlucoseDisplayMode.bothForDetail
|
||||||
].contains(Settings.glucoseDisplayMode)
|
].contains(Settings.glucoseDisplayMode)
|
||||||
? Expanded(
|
? Expanded(
|
||||||
child: TextFormField(
|
flex: Settings.glucoseMeasurement == GlucoseMeasurement.mmolPerL ? 2 : 1,
|
||||||
decoration: const InputDecoration(
|
child: NumberFormField(
|
||||||
labelText: 'per mmol/l',
|
label: 'per mmol/l',
|
||||||
suffixText: 'mmol/l',
|
suffix: 'mmol/l',
|
||||||
),
|
|
||||||
readOnly: Settings.glucoseMeasurement ==
|
readOnly: Settings.glucoseMeasurement ==
|
||||||
GlucoseMeasurement.mgPerDl,
|
GlucoseMeasurement.mgPerDl,
|
||||||
|
showSteppers: Settings.glucoseMeasurement == GlucoseMeasurement.mmolPerL,
|
||||||
controller: _mmolPerLController,
|
controller: _mmolPerLController,
|
||||||
onChanged: (_) async {
|
step: Settings.mmolPerLSteps,
|
||||||
await Future.delayed(
|
onChanged: convertBetweenMgPerDlAndMmolPerL,
|
||||||
const Duration(seconds: 1));
|
|
||||||
convertBetweenMgPerDlAndMmolPerL(
|
|
||||||
calculateFrom:
|
|
||||||
GlucoseMeasurement.mmolPerL);
|
|
||||||
},
|
|
||||||
keyboardType:
|
|
||||||
const TextInputType.numberWithOptions(
|
|
||||||
decimal: true),
|
|
||||||
validator: (value) {
|
|
||||||
if (value!.trim().isEmpty &&
|
|
||||||
_mgPerDlController.text.trim().isEmpty) {
|
|
||||||
return 'How many mmol/l does rhe rate make up for?';
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
: Container(),
|
: Container(),
|
||||||
[
|
|
||||||
GlucoseDisplayMode.both,
|
|
||||||
GlucoseDisplayMode.bothForDetail
|
|
||||||
].contains(Settings.glucoseDisplayMode)
|
|
||||||
? IconButton(
|
|
||||||
onPressed: () => convertBetweenMgPerDlAndMmolPerL(
|
|
||||||
calculateFrom: GlucoseMeasurement.mgPerDl),
|
|
||||||
icon: const Icon(Icons.calculate),
|
|
||||||
)
|
|
||||||
: Container(),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import 'package:diameter/components/dialogs.dart';
|
import 'package:diameter/utils/dialog_utils.dart';
|
||||||
import 'package:diameter/models/settings.dart';
|
import 'package:diameter/models/settings.dart';
|
||||||
import 'package:diameter/utils/date_time_utils.dart';
|
import 'package:diameter/utils/date_time_utils.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
@ -12,7 +12,10 @@ class BolusListScreen extends StatefulWidget {
|
|||||||
final Function() reload;
|
final Function() reload;
|
||||||
|
|
||||||
const BolusListScreen(
|
const BolusListScreen(
|
||||||
{Key? key, required this.bolusProfile, this.bolusRates = const [], required this.reload})
|
{Key? key,
|
||||||
|
required this.bolusProfile,
|
||||||
|
this.bolusRates = const [],
|
||||||
|
required this.reload})
|
||||||
: super(key: key);
|
: super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -22,6 +25,12 @@ class BolusListScreen extends StatefulWidget {
|
|||||||
class _BolusListScreenState extends State<BolusListScreen> {
|
class _BolusListScreenState extends State<BolusListScreen> {
|
||||||
final ScrollController _scrollController = ScrollController();
|
final ScrollController _scrollController = ScrollController();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_scrollController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
void reload({String? message}) {
|
void reload({String? message}) {
|
||||||
widget.reload();
|
widget.reload();
|
||||||
|
|
||||||
@ -57,7 +66,7 @@ class _BolusListScreenState extends State<BolusListScreen> {
|
|||||||
|
|
||||||
void handleDeleteAction(Bolus bolus) async {
|
void handleDeleteAction(Bolus bolus) async {
|
||||||
if (Settings.get().showConfirmationDialogOnDelete) {
|
if (Settings.get().showConfirmationDialogOnDelete) {
|
||||||
Dialogs.showConfirmationDialog(
|
DialogUtils.showConfirmationDialog(
|
||||||
context: context,
|
context: context,
|
||||||
onConfirm: () => onDelete(bolus),
|
onConfirm: () => onDelete(bolus),
|
||||||
message: 'Are you sure you want to delete this Bolus Rate?',
|
message: 'Are you sure you want to delete this Bolus Rate?',
|
||||||
@ -102,11 +111,14 @@ class _BolusListScreenState extends State<BolusListScreen> {
|
|||||||
.isNotEmpty) {
|
.isNotEmpty) {
|
||||||
return 'This rate\'s time period overlaps with another one';
|
return 'This rate\'s time period overlaps with another one';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return widget.bolusRates.isNotEmpty ? Scrollbar(
|
return widget.bolusRates.isNotEmpty
|
||||||
|
? Scrollbar(
|
||||||
controller: _scrollController,
|
controller: _scrollController,
|
||||||
child: ListView.builder(
|
child: ListView.builder(
|
||||||
padding: const EdgeInsets.all(10.0),
|
padding: const EdgeInsets.all(10.0),
|
||||||
@ -125,19 +137,22 @@ class _BolusListScreenState extends State<BolusListScreen> {
|
|||||||
child: Row(
|
child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
Icon(Icons.warning, color: Theme.of(context).errorColor),
|
Icon(Icons.warning,
|
||||||
Text(
|
color: Theme.of(context).errorColor),
|
||||||
error, style: TextStyle(color: Theme.of(context).errorColor)
|
Text(error,
|
||||||
),
|
style: TextStyle(
|
||||||
|
color: Theme.of(context).errorColor)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
) : Container(),
|
)
|
||||||
|
: Container(),
|
||||||
ListTile(
|
ListTile(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
handleEditAction(bolus);
|
handleEditAction(bolus);
|
||||||
},
|
},
|
||||||
isThreeLine: true,
|
isThreeLine: true,
|
||||||
title: Text('${DateTimeUtils.displayTime(bolus.startTime)} - ${DateTimeUtils.displayTime(bolus.endTime)}'),
|
title: Text(
|
||||||
|
'${DateTimeUtils.displayTime(bolus.startTime)} - ${DateTimeUtils.displayTime(bolus.endTime)}'),
|
||||||
subtitle: Row(
|
subtitle: Row(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
@ -145,9 +160,12 @@ class _BolusListScreenState extends State<BolusListScreen> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
children: (bolus.units > 0 && bolus.carbs > 0)
|
children: (bolus.units > 0 && bolus.carbs > 0)
|
||||||
? [
|
? [
|
||||||
Text((bolus.carbs / bolus.units).toStringAsPrecision(2)),
|
Text((bolus.carbs / bolus.units)
|
||||||
Text('${Settings.nutritionMeasurementSuffix} carbs per U',
|
.toStringAsPrecision(2)),
|
||||||
textAlign: TextAlign.center, textScaleFactor: 0.75),
|
Text(
|
||||||
|
'${Settings.nutritionMeasurementSuffix} carbs per U',
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
textScaleFactor: 0.75),
|
||||||
]
|
]
|
||||||
: [],
|
: [],
|
||||||
),
|
),
|
||||||
@ -156,20 +174,32 @@ class _BolusListScreenState extends State<BolusListScreen> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
children: (bolus.units > 0 && bolus.carbs > 0)
|
children: (bolus.units > 0 && bolus.carbs > 0)
|
||||||
? [
|
? [
|
||||||
Text((bolus.units / bolus.carbs * 12).toStringAsPrecision(2)),
|
Text((bolus.units / bolus.carbs * 12)
|
||||||
|
.toStringAsPrecision(2)),
|
||||||
const Text('U per bread unit',
|
const Text('U per bread unit',
|
||||||
textAlign: TextAlign.center, textScaleFactor: 0.75),
|
textAlign: TextAlign.center,
|
||||||
|
textScaleFactor: 0.75),
|
||||||
]
|
]
|
||||||
: [],
|
: [],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Column(
|
child: Column(
|
||||||
children: (bolus.units > 0 && (bolus.mgPerDl ?? bolus.mmolPerL ?? 0) > 0)
|
children: (bolus.units > 0 &&
|
||||||
|
(bolus.mgPerDl ?? bolus.mmolPerL ?? 0) >
|
||||||
|
0)
|
||||||
? [
|
? [
|
||||||
Text((((Settings.glucoseMeasurement == GlucoseMeasurement.mgPerDl ? bolus.mgPerDl : bolus.mmolPerL ?? 0)! / bolus.units)).toString()),
|
Text((((Settings.glucoseMeasurement ==
|
||||||
Text('${Settings.glucoseMeasurementSuffix} per unit',
|
GlucoseMeasurement
|
||||||
textAlign: TextAlign.center, textScaleFactor: 0.75),
|
.mgPerDl
|
||||||
|
? bolus.mgPerDl
|
||||||
|
: bolus.mmolPerL ?? 0)! /
|
||||||
|
bolus.units))
|
||||||
|
.toString()),
|
||||||
|
Text(
|
||||||
|
'${Settings.glucoseMeasurementSuffix} per unit',
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
textScaleFactor: 0.75),
|
||||||
]
|
]
|
||||||
: [],
|
: [],
|
||||||
),
|
),
|
||||||
@ -194,7 +224,8 @@ class _BolusListScreenState extends State<BolusListScreen> {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
) : const Center(
|
)
|
||||||
|
: const Center(
|
||||||
child: Text('You have not created any Bolus Rates yet!'),
|
child: Text('You have not created any Bolus Rates yet!'),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
import 'package:diameter/components/detail.dart';
|
import 'package:diameter/components/detail.dart';
|
||||||
import 'package:diameter/components/dialogs.dart';
|
import 'package:diameter/components/forms/boolean_form_field.dart';
|
||||||
|
import 'package:diameter/utils/dialog_utils.dart';
|
||||||
import 'package:diameter/models/bolus.dart';
|
import 'package:diameter/models/bolus.dart';
|
||||||
import 'package:diameter/models/settings.dart';
|
import 'package:diameter/models/settings.dart';
|
||||||
import 'package:diameter/navigation.dart';
|
import 'package:diameter/navigation.dart';
|
||||||
import 'package:diameter/screens/bolus/bolus_detail.dart';
|
import 'package:diameter/screens/bolus/bolus_detail.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:diameter/components/forms.dart';
|
import 'package:diameter/components/forms/form_wrapper.dart';
|
||||||
import 'package:diameter/models/bolus_profile.dart';
|
import 'package:diameter/models/bolus_profile.dart';
|
||||||
import 'package:diameter/screens/bolus/bolus_list.dart';
|
import 'package:diameter/screens/bolus/bolus_list.dart';
|
||||||
|
|
||||||
@ -90,6 +91,14 @@ class _BolusProfileDetailScreenState extends State<BolusProfileDetailScreen> {
|
|||||||
bottomNav = detailBottomRow;
|
bottomNav = detailBottomRow;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_scrollController.dispose();
|
||||||
|
_nameController.dispose();
|
||||||
|
_notesController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
void reload({String? message}) {
|
void reload({String? message}) {
|
||||||
if (widget.id != 0) {
|
if (widget.id != 0) {
|
||||||
setState(() {
|
setState(() {
|
||||||
@ -269,7 +278,7 @@ class _BolusProfileDetailScreenState extends State<BolusProfileDetailScreen> {
|
|||||||
(_bolusProfile!.active != _active ||
|
(_bolusProfile!.active != _active ||
|
||||||
_bolusProfile!.name != _nameController.text ||
|
_bolusProfile!.name != _nameController.text ||
|
||||||
(_bolusProfile!.notes ?? '') != _notesController.text))) {
|
(_bolusProfile!.notes ?? '') != _notesController.text))) {
|
||||||
Dialogs.showCancelConfirmationDialog(
|
DialogUtils.showCancelConfirmationDialog(
|
||||||
context: context,
|
context: context,
|
||||||
isNew: _isNew,
|
isNew: _isNew,
|
||||||
onSave: handleSaveAction,
|
onSave: handleSaveAction,
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import 'package:diameter/components/dialogs.dart';
|
import 'package:diameter/utils/dialog_utils.dart';
|
||||||
import 'package:diameter/components/dropdown.dart';
|
import 'package:diameter/components/forms/auto_complete_dropdown_button.dart';
|
||||||
|
import 'package:diameter/models/bolus.dart';
|
||||||
import 'package:diameter/models/settings.dart';
|
import 'package:diameter/models/settings.dart';
|
||||||
import 'package:diameter/navigation.dart';
|
import 'package:diameter/navigation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
@ -22,6 +23,18 @@ class _BolusProfileListScreenState extends State<BolusProfileListScreen> {
|
|||||||
|
|
||||||
final BolusProfile? _activeProfile = BolusProfile.getActive(DateTime.now());
|
final BolusProfile? _activeProfile = BolusProfile.getActive(DateTime.now());
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_scrollController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
void reload({String? message}) {
|
void reload({String? message}) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_bolusProfiles = BolusProfile.getAll();
|
_bolusProfiles = BolusProfile.getAll();
|
||||||
@ -77,6 +90,30 @@ class _BolusProfileListScreenState extends State<BolusProfileListScreen> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void handleDuplicateAction(BolusProfile bolusProfile) async {
|
||||||
|
final copy = BolusProfile(
|
||||||
|
active: false,
|
||||||
|
name: 'Copy of ${bolusProfile.name}',
|
||||||
|
);
|
||||||
|
BolusProfile.put(copy);
|
||||||
|
|
||||||
|
final rates = Bolus.getAllForProfile(bolusProfile.id);
|
||||||
|
for (Bolus rate in rates) {
|
||||||
|
final bolus = Bolus(
|
||||||
|
endTime: rate.endTime,
|
||||||
|
startTime: rate.startTime,
|
||||||
|
units: rate.units,
|
||||||
|
carbs: rate.carbs,
|
||||||
|
mgPerDl: rate.mgPerDl,
|
||||||
|
mmolPerL: rate.mmolPerL,
|
||||||
|
);
|
||||||
|
bolus.bolusProfile.target = copy;
|
||||||
|
Bolus.put(bolus);
|
||||||
|
}
|
||||||
|
|
||||||
|
reload(message: 'Added copy of ${bolusProfile.name}');
|
||||||
|
}
|
||||||
|
|
||||||
void onDelete(BolusProfile bolusProfile) {
|
void onDelete(BolusProfile bolusProfile) {
|
||||||
BolusProfile.remove(bolusProfile.id);
|
BolusProfile.remove(bolusProfile.id);
|
||||||
reload(message: 'Bolus Profile deleted');
|
reload(message: 'Bolus Profile deleted');
|
||||||
@ -84,7 +121,7 @@ class _BolusProfileListScreenState extends State<BolusProfileListScreen> {
|
|||||||
|
|
||||||
void handleDeleteAction(BolusProfile bolusProfile) async {
|
void handleDeleteAction(BolusProfile bolusProfile) async {
|
||||||
if (Settings.get().showConfirmationDialogOnDelete) {
|
if (Settings.get().showConfirmationDialogOnDelete) {
|
||||||
Dialogs.showConfirmationDialog(
|
DialogUtils.showConfirmationDialog(
|
||||||
context: context,
|
context: context,
|
||||||
onConfirm: () => onDelete(bolusProfile),
|
onConfirm: () => onDelete(bolusProfile),
|
||||||
message: 'Are you sure you want to delete this Bolus Profile?',
|
message: 'Are you sure you want to delete this Bolus Profile?',
|
||||||
@ -143,12 +180,6 @@ class _BolusProfileListScreenState extends State<BolusProfileListScreen> {
|
|||||||
showDetailScreen(bolusProfile: bolusProfile);
|
showDetailScreen(bolusProfile: bolusProfile);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
reload();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
@ -196,6 +227,14 @@ class _BolusProfileListScreenState extends State<BolusProfileListScreen> {
|
|||||||
trailing: Row(
|
trailing: Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(
|
||||||
|
Icons.copy,
|
||||||
|
color: Colors.blue,
|
||||||
|
),
|
||||||
|
onPressed: () =>
|
||||||
|
handleDuplicateAction(bolusProfile),
|
||||||
|
),
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: const Icon(
|
icon: const Icon(
|
||||||
Icons.delete,
|
Icons.delete,
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import 'package:diameter/components/dialogs.dart';
|
import 'package:diameter/utils/dialog_utils.dart';
|
||||||
import 'package:diameter/models/glucose_target.dart';
|
import 'package:diameter/models/glucose_target.dart';
|
||||||
import 'package:diameter/models/log_bolus.dart';
|
import 'package:diameter/models/log_bolus.dart';
|
||||||
import 'package:diameter/models/log_entry.dart';
|
import 'package:diameter/models/log_entry.dart';
|
||||||
@ -29,6 +29,12 @@ class _LogScreenState extends State<LogScreen> {
|
|||||||
reload();
|
reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_scrollController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
void reload({String? message}) {
|
void reload({String? message}) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_logEntryDailyMap = LogEntry.getDailyEntryMap();
|
_logEntryDailyMap = LogEntry.getDailyEntryMap();
|
||||||
@ -53,7 +59,7 @@ class _LogScreenState extends State<LogScreen> {
|
|||||||
|
|
||||||
void handleDeleteAction(LogEntry logEntry) async {
|
void handleDeleteAction(LogEntry logEntry) async {
|
||||||
if (Settings.get().showConfirmationDialogOnDelete) {
|
if (Settings.get().showConfirmationDialogOnDelete) {
|
||||||
Dialogs.showConfirmationDialog(
|
DialogUtils.showConfirmationDialog(
|
||||||
context: context,
|
context: context,
|
||||||
onConfirm: () => onDelete(logEntry),
|
onConfirm: () => onDelete(logEntry),
|
||||||
message: 'Are you sure you want to delete this Log Entry?',
|
message: 'Are you sure you want to delete this Log Entry?',
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
|
||||||
import 'package:diameter/components/detail.dart';
|
import 'package:diameter/components/detail.dart';
|
||||||
import 'package:diameter/components/dialogs.dart';
|
import 'package:diameter/components/forms/boolean_form_field.dart';
|
||||||
import 'package:diameter/components/dropdown.dart';
|
import 'package:diameter/components/forms/number_form_field.dart';
|
||||||
import 'package:diameter/components/forms.dart';
|
import 'package:diameter/utils/dialog_utils.dart';
|
||||||
|
import 'package:diameter/components/forms/auto_complete_dropdown_button.dart';
|
||||||
|
import 'package:diameter/components/forms/form_wrapper.dart';
|
||||||
import 'package:diameter/models/bolus.dart';
|
import 'package:diameter/models/bolus.dart';
|
||||||
import 'package:diameter/models/log_bolus.dart';
|
import 'package:diameter/models/log_bolus.dart';
|
||||||
import 'package:diameter/models/log_entry.dart';
|
import 'package:diameter/models/log_entry.dart';
|
||||||
@ -136,6 +138,25 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
|
|||||||
updateDelayedRatio();
|
updateDelayedRatio();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_scrollController.dispose();
|
||||||
|
_unitsController.dispose();
|
||||||
|
_carbsController.dispose();
|
||||||
|
_mgPerDlCurrentController.dispose();
|
||||||
|
_mgPerDlTargetController.dispose();
|
||||||
|
_mgPerDlCorrectionController.dispose();
|
||||||
|
_mmolPerLCurrentController.dispose();
|
||||||
|
_mmolPerLTargetController.dispose();
|
||||||
|
_mmolPerLCorrectionController.dispose();
|
||||||
|
_delayController.dispose();
|
||||||
|
_notesController.dispose();
|
||||||
|
_delayedUnitsController.dispose();
|
||||||
|
_immediateUnitsController.dispose();
|
||||||
|
_mealController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
void reload({String? message}) {
|
void reload({String? message}) {
|
||||||
if (widget.id != 0) {
|
if (widget.id != 0) {
|
||||||
setState(() {
|
setState(() {
|
||||||
@ -179,20 +200,73 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void updateDelayedRatio() {
|
void updateDelayedRatio(
|
||||||
if (_unitsController.text != '') {
|
{double? totalUnitsUpdate,
|
||||||
|
double? delayedUnitsUpdate,
|
||||||
|
double? immediateUnitsUpdate,
|
||||||
|
double? percentageUpdate}) {
|
||||||
|
int precision = Utils.getFractionDigitsLength(Settings.insulinSteps);
|
||||||
|
double? totalUnits =
|
||||||
|
totalUnitsUpdate ?? double.tryParse(_unitsController.text);
|
||||||
|
double? delayedUnits;
|
||||||
|
double? immediateUnits;
|
||||||
|
|
||||||
|
if (totalUnits == null) {
|
||||||
|
delayedUnits =
|
||||||
|
delayedUnitsUpdate ?? double.tryParse(_delayedUnitsController.text);
|
||||||
|
immediateUnits = immediateUnitsUpdate ??
|
||||||
|
double.tryParse(_immediateUnitsController.text);
|
||||||
|
|
||||||
|
if (percentageUpdate != null) {
|
||||||
|
if (delayedUnits != null) {
|
||||||
|
totalUnits = delayedUnits / percentageUpdate * 100;
|
||||||
|
} else if (immediateUnits != null) {
|
||||||
|
totalUnits = immediateUnits / percentageUpdate * 100;
|
||||||
|
}
|
||||||
|
} else if (delayedUnits != null && immediateUnits != null) {
|
||||||
|
totalUnits = Utils.addDoublesWithPrecision(
|
||||||
|
delayedUnits, immediateUnits, precision);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
setState(() {
|
setState(() {
|
||||||
_delayedUnitsController.text =
|
_unitsController.text = (totalUnits ?? 0).toString();
|
||||||
((double.tryParse(_unitsController.text) ?? 0) *
|
|
||||||
_delayPercentage /
|
|
||||||
100)
|
|
||||||
.toString();
|
|
||||||
_immediateUnitsController.text =
|
|
||||||
((double.tryParse(_unitsController.text) ?? 0) *
|
|
||||||
(100 - _delayPercentage) /
|
|
||||||
100)
|
|
||||||
.toString();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (totalUnits != null) {
|
||||||
|
double percentage = percentageUpdate ?? _delayPercentage;
|
||||||
|
|
||||||
|
if (totalUnitsUpdate != null || percentageUpdate != null) {
|
||||||
|
delayedUnits = totalUnits * percentage / 100;
|
||||||
|
} else if (delayedUnitsUpdate != null) {
|
||||||
|
delayedUnits = delayedUnitsUpdate;
|
||||||
|
} else if (immediateUnitsUpdate != null) {
|
||||||
|
delayedUnits = totalUnits - immediateUnitsUpdate;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (delayedUnits != null) {
|
||||||
|
double remainder = delayedUnits % Settings.insulinSteps;
|
||||||
|
int precision = Utils.getFractionDigitsLength(Settings.insulinSteps);
|
||||||
|
|
||||||
|
if (remainder != 0) {
|
||||||
|
delayedUnits = Utils.addDoublesWithPrecision(
|
||||||
|
delayedUnits, -remainder, precision);
|
||||||
|
if (remainder > Settings.insulinSteps / 2) {
|
||||||
|
delayedUnits = Utils.addDoublesWithPrecision(
|
||||||
|
delayedUnits, Settings.insulinSteps, precision);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setState(() {
|
||||||
|
_delayedUnitsController.text = delayedUnits.toString();
|
||||||
|
_immediateUnitsController.text = Utils.addDoublesWithPrecision(
|
||||||
|
totalUnits!, -delayedUnits!, precision)
|
||||||
|
.toString();
|
||||||
|
if (totalUnits != 0) {
|
||||||
|
_delayPercentage = delayedUnits * 100 / totalUnits;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -207,28 +281,30 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void calculateBolus() {
|
void calculateBolus() {
|
||||||
setState(() {
|
|
||||||
if (_rate != null && !_setManually) {
|
if (_rate != null && !_setManually) {
|
||||||
_unitsController.text = ((double.tryParse(_carbsController.text) ?? 0) /
|
double units = (double.tryParse(_carbsController.text) ?? 0) /
|
||||||
(_rate!.carbs / _rate!.units))
|
(_rate!.carbs / _rate!.units);
|
||||||
.toString();
|
double remainder = units % Settings.insulinSteps;
|
||||||
if (_unitsController.text != '') {
|
int precision = Utils.getFractionDigitsLength(Settings.insulinSteps);
|
||||||
_delayedUnitsController.text =
|
|
||||||
((double.tryParse(_unitsController.text) ?? 0) *
|
if (remainder != 0) {
|
||||||
_delayPercentage /
|
units = Utils.addDoublesWithPrecision(units, -remainder, precision);
|
||||||
100)
|
if (remainder > Settings.insulinSteps / 2) {
|
||||||
.toString();
|
units = Utils.addDoublesWithPrecision(
|
||||||
_immediateUnitsController.text =
|
units, Settings.insulinSteps, precision);
|
||||||
((double.tryParse(_unitsController.text) ?? 0) *
|
|
||||||
(100 - _delayPercentage) /
|
|
||||||
100)
|
|
||||||
.toString();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void onChangeGlucose({GlucoseMeasurement? calculateFrom}) {
|
setState(() {
|
||||||
|
_unitsController.text = units.toString();
|
||||||
|
});
|
||||||
|
|
||||||
|
updateDelayedRatio(
|
||||||
|
totalUnitsUpdate: double.tryParse(_unitsController.text));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void onChangeGlucose() {
|
||||||
int? mgPerDlCurrent;
|
int? mgPerDlCurrent;
|
||||||
int? mgPerDlTarget;
|
int? mgPerDlTarget;
|
||||||
int? mgPerDlCorrection;
|
int? mgPerDlCorrection;
|
||||||
@ -237,14 +313,14 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
|
|||||||
double? mmolPerLTarget;
|
double? mmolPerLTarget;
|
||||||
double? mmolPerLCorrection;
|
double? mmolPerLCorrection;
|
||||||
|
|
||||||
if (calculateFrom != GlucoseMeasurement.mmolPerL &&
|
if (Settings.glucoseMeasurement == GlucoseMeasurement.mgPerDl &&
|
||||||
_mgPerDlCurrentController.text != '' &&
|
_mgPerDlCurrentController.text != '' &&
|
||||||
_mgPerDlTargetController.text != '') {
|
_mgPerDlTargetController.text != '') {
|
||||||
mgPerDlCurrent = int.tryParse(_mgPerDlCurrentController.text);
|
mgPerDlCurrent = int.tryParse(_mgPerDlCurrentController.text);
|
||||||
mgPerDlTarget = int.tryParse(_mgPerDlTargetController.text);
|
mgPerDlTarget = int.tryParse(_mgPerDlTargetController.text);
|
||||||
mgPerDlCorrection = max((mgPerDlCurrent ?? 0) - (mgPerDlTarget ?? 0), 0);
|
mgPerDlCorrection = max((mgPerDlCurrent ?? 0) - (mgPerDlTarget ?? 0), 0);
|
||||||
}
|
}
|
||||||
if (calculateFrom != GlucoseMeasurement.mgPerDl &&
|
if (Settings.glucoseMeasurement == GlucoseMeasurement.mmolPerL &&
|
||||||
_mmolPerLCurrentController.text != '') {
|
_mmolPerLCurrentController.text != '') {
|
||||||
mmolPerLCurrent = double.tryParse(_mmolPerLCurrentController.text);
|
mmolPerLCurrent = double.tryParse(_mmolPerLCurrentController.text);
|
||||||
mmolPerLTarget = double.tryParse(_mmolPerLTargetController.text);
|
mmolPerLTarget = double.tryParse(_mmolPerLTargetController.text);
|
||||||
@ -264,9 +340,9 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
|
|||||||
_mmolPerLCorrectionController.text =
|
_mmolPerLCorrectionController.text =
|
||||||
Utils.convertMgPerDlToMmolPerL(mgPerDlCorrection ?? 0).toString();
|
Utils.convertMgPerDlToMmolPerL(mgPerDlCorrection ?? 0).toString();
|
||||||
if (_rate != null && !_setManually) {
|
if (_rate != null && !_setManually) {
|
||||||
_unitsController.text = ((mgPerDlCorrection ?? 0) /
|
updateDelayedRatio(
|
||||||
((_rate!.mgPerDl ?? 0) / _rate!.units))
|
totalUnitsUpdate: (mgPerDlCorrection ?? 0) /
|
||||||
.toString();
|
((_rate!.mgPerDl ?? 0) / _rate!.units));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -282,9 +358,9 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
|
|||||||
_mgPerDlCorrectionController.text =
|
_mgPerDlCorrectionController.text =
|
||||||
Utils.convertMmolPerLToMgPerDl(mmolPerLCorrection ?? 0).toString();
|
Utils.convertMmolPerLToMgPerDl(mmolPerLCorrection ?? 0).toString();
|
||||||
if (_rate != null && !_setManually) {
|
if (_rate != null && !_setManually) {
|
||||||
_unitsController.text = ((mmolPerLCorrection ?? 0) /
|
updateDelayedRatio(
|
||||||
((_rate!.mmolPerL ?? 0) / _rate!.units))
|
totalUnitsUpdate: (mmolPerLCorrection ?? 0) /
|
||||||
.toString();
|
((_rate!.mmolPerL ?? 0) / _rate!.units));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -414,7 +490,7 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
|
|||||||
int.tryParse(_delayController.text) != _logBolus!.delay ||
|
int.tryParse(_delayController.text) != _logBolus!.delay ||
|
||||||
_setManually != _logBolus!.setManually ||
|
_setManually != _logBolus!.setManually ||
|
||||||
_notesController.text != (_logBolus!.notes ?? ''))))) {
|
_notesController.text != (_logBolus!.notes ?? ''))))) {
|
||||||
Dialogs.showCancelConfirmationDialog(
|
DialogUtils.showCancelConfirmationDialog(
|
||||||
context: context,
|
context: context,
|
||||||
isNew: _isNew,
|
isNew: _isNew,
|
||||||
onSave: handleSaveAction,
|
onSave: handleSaveAction,
|
||||||
@ -444,20 +520,18 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
|
|||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: TextFormField(
|
child: NumberFormField(
|
||||||
decoration: const InputDecoration(
|
label: 'Bolus Units',
|
||||||
labelText: 'Bolus Units',
|
suffix: ' U',
|
||||||
suffixText: ' U',
|
|
||||||
),
|
|
||||||
controller: _unitsController,
|
controller: _unitsController,
|
||||||
onChanged: (_) {
|
step: Settings.insulinSteps,
|
||||||
|
autoRoundToMultipleOfStep: true,
|
||||||
|
onChanged: (value) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_setManually = true;
|
_setManually = true;
|
||||||
});
|
});
|
||||||
updateDelayedRatio();
|
updateDelayedRatio(totalUnitsUpdate: value);
|
||||||
},
|
},
|
||||||
keyboardType: const TextInputType.numberWithOptions(
|
|
||||||
decimal: true),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
@ -486,6 +560,7 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
|
|||||||
onChanged: (_) {
|
onChanged: (_) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_bolusType = BolusType.glucose;
|
_bolusType = BolusType.glucose;
|
||||||
|
onChangeGlucose();
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
@ -497,6 +572,7 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
|
|||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_bolusType = BolusType.meal;
|
_bolusType = BolusType.meal;
|
||||||
|
calculateBolus();
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
@ -517,23 +593,13 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
|
|||||||
child: Padding(
|
child: Padding(
|
||||||
padding:
|
padding:
|
||||||
const EdgeInsets.only(right: 5.0),
|
const EdgeInsets.only(right: 5.0),
|
||||||
child: TextFormField(
|
child: NumberFormField(
|
||||||
decoration: const InputDecoration(
|
label: 'Current',
|
||||||
labelText: 'Current',
|
suffix: 'mg/dl',
|
||||||
suffixText: 'mg/dl',
|
|
||||||
),
|
|
||||||
controller:
|
controller:
|
||||||
_mgPerDlCurrentController,
|
_mgPerDlCurrentController,
|
||||||
onChanged: (_) async {
|
onChanged: (_) => onChangeGlucose(),
|
||||||
await Future.delayed(
|
showSteppers: false,
|
||||||
const Duration(seconds: 1));
|
|
||||||
onChangeGlucose(
|
|
||||||
calculateFrom:
|
|
||||||
GlucoseMeasurement
|
|
||||||
.mgPerDl);
|
|
||||||
},
|
|
||||||
keyboardType: const TextInputType
|
|
||||||
.numberWithOptions(),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -541,23 +607,13 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
|
|||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.symmetric(
|
padding: const EdgeInsets.symmetric(
|
||||||
horizontal: 5.0),
|
horizontal: 5.0),
|
||||||
child: TextFormField(
|
child: NumberFormField(
|
||||||
decoration: const InputDecoration(
|
label: 'Target',
|
||||||
labelText: 'Target',
|
suffix: 'mg/dl',
|
||||||
suffixText: 'mg/dl',
|
|
||||||
),
|
|
||||||
controller:
|
controller:
|
||||||
_mgPerDlTargetController,
|
_mgPerDlTargetController,
|
||||||
onChanged: (_) async {
|
onChanged: (_) => onChangeGlucose(),
|
||||||
await Future.delayed(
|
showSteppers: false,
|
||||||
const Duration(seconds: 1));
|
|
||||||
onChangeGlucose(
|
|
||||||
calculateFrom:
|
|
||||||
GlucoseMeasurement
|
|
||||||
.mgPerDl);
|
|
||||||
},
|
|
||||||
keyboardType: const TextInputType
|
|
||||||
.numberWithOptions(),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -576,22 +632,18 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
[
|
|
||||||
GlucoseDisplayMode.both,
|
|
||||||
GlucoseDisplayMode.bothForDetail
|
|
||||||
].contains(Settings.glucoseDisplayMode)
|
|
||||||
? IconButton(
|
|
||||||
onPressed: () => onChangeGlucose(
|
|
||||||
calculateFrom:
|
|
||||||
GlucoseMeasurement
|
|
||||||
.mmolPerL),
|
|
||||||
icon: const Icon(Icons.calculate),
|
|
||||||
)
|
|
||||||
: Container(),
|
|
||||||
]
|
]
|
||||||
: [],
|
: [],
|
||||||
),
|
),
|
||||||
Row(
|
Padding(
|
||||||
|
padding: EdgeInsets.only(
|
||||||
|
top: [
|
||||||
|
GlucoseDisplayMode.both,
|
||||||
|
GlucoseDisplayMode.bothForDetail
|
||||||
|
].contains(Settings.glucoseDisplayMode)
|
||||||
|
? 10.0
|
||||||
|
: 0.0),
|
||||||
|
child: Row(
|
||||||
children: Settings.glucoseMeasurement ==
|
children: Settings.glucoseMeasurement ==
|
||||||
GlucoseMeasurement.mmolPerL ||
|
GlucoseMeasurement.mmolPerL ||
|
||||||
[
|
[
|
||||||
@ -601,25 +653,16 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
|
|||||||
? [
|
? [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding:
|
padding: const EdgeInsets.only(
|
||||||
const EdgeInsets.only(right: 5.0),
|
right: 5.0),
|
||||||
child: TextFormField(
|
child: NumberFormField(
|
||||||
decoration: const InputDecoration(
|
label: 'Current',
|
||||||
labelText: 'Current',
|
suffix: 'mmol/l',
|
||||||
suffixText: 'mmol/l',
|
|
||||||
),
|
|
||||||
controller:
|
controller:
|
||||||
_mmolPerLCurrentController,
|
_mmolPerLCurrentController,
|
||||||
onChanged: (_) async {
|
onChanged: (_) =>
|
||||||
await Future.delayed(
|
onChangeGlucose(),
|
||||||
const Duration(seconds: 1));
|
showSteppers: false,
|
||||||
onChangeGlucose(
|
|
||||||
calculateFrom:
|
|
||||||
GlucoseMeasurement
|
|
||||||
.mmolPerL);
|
|
||||||
},
|
|
||||||
keyboardType: const TextInputType
|
|
||||||
.numberWithOptions(),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -627,30 +670,21 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
|
|||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.symmetric(
|
padding: const EdgeInsets.symmetric(
|
||||||
horizontal: 5.0),
|
horizontal: 5.0),
|
||||||
child: TextFormField(
|
child: NumberFormField(
|
||||||
decoration: const InputDecoration(
|
label: 'Target',
|
||||||
labelText: 'Target',
|
suffix: 'mmol/l',
|
||||||
suffixText: 'mmol/l',
|
|
||||||
),
|
|
||||||
controller:
|
controller:
|
||||||
_mmolPerLTargetController,
|
_mmolPerLTargetController,
|
||||||
onChanged: (_) async {
|
onChanged: (_) =>
|
||||||
await Future.delayed(
|
onChangeGlucose(),
|
||||||
const Duration(seconds: 1));
|
showSteppers: false,
|
||||||
onChangeGlucose(
|
|
||||||
calculateFrom:
|
|
||||||
GlucoseMeasurement
|
|
||||||
.mmolPerL);
|
|
||||||
},
|
|
||||||
keyboardType: const TextInputType
|
|
||||||
.numberWithOptions(),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding:
|
padding: const EdgeInsets.only(
|
||||||
const EdgeInsets.only(left: 5.0),
|
left: 5.0),
|
||||||
child: TextFormField(
|
child: TextFormField(
|
||||||
decoration: const InputDecoration(
|
decoration: const InputDecoration(
|
||||||
labelText: 'Correction',
|
labelText: 'Correction',
|
||||||
@ -662,21 +696,10 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
[
|
|
||||||
GlucoseDisplayMode.both,
|
|
||||||
GlucoseDisplayMode.bothForDetail
|
|
||||||
].contains(Settings.glucoseDisplayMode)
|
|
||||||
? IconButton(
|
|
||||||
onPressed: () => onChangeGlucose(
|
|
||||||
calculateFrom:
|
|
||||||
GlucoseMeasurement
|
|
||||||
.mgPerDl),
|
|
||||||
icon: const Icon(Icons.calculate),
|
|
||||||
)
|
|
||||||
: Container(),
|
|
||||||
]
|
]
|
||||||
: [],
|
: [],
|
||||||
),
|
),
|
||||||
|
),
|
||||||
]
|
]
|
||||||
: [
|
: [
|
||||||
Row(
|
Row(
|
||||||
@ -712,17 +735,15 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
|
|||||||
),
|
),
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(top: 10.0),
|
padding: const EdgeInsets.only(top: 10.0),
|
||||||
child: TextFormField(
|
child: NumberFormField(
|
||||||
decoration: InputDecoration(
|
label: 'Carbs',
|
||||||
labelText: 'Carbs',
|
suffix: Settings.nutritionMeasurementSuffix,
|
||||||
suffixText:
|
|
||||||
Settings.nutritionMeasurementSuffix,
|
|
||||||
),
|
|
||||||
controller: _carbsController,
|
controller: _carbsController,
|
||||||
onChanged: (_) => calculateBolus(),
|
step: Settings.nutritionSteps,
|
||||||
keyboardType:
|
onChanged: (value) {
|
||||||
const TextInputType.numberWithOptions(
|
_carbsController.text = (value ?? 0).toString();
|
||||||
decimal: true),
|
calculateBolus();
|
||||||
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -749,10 +770,7 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
|
|||||||
max: 100,
|
max: 100,
|
||||||
onChanged: _delayController.text != ''
|
onChanged: _delayController.text != ''
|
||||||
? (value) {
|
? (value) {
|
||||||
setState(() {
|
updateDelayedRatio(percentageUpdate: value);
|
||||||
_delayPercentage = value;
|
|
||||||
});
|
|
||||||
updateDelayedRatio();
|
|
||||||
}
|
}
|
||||||
: null,
|
: null,
|
||||||
),
|
),
|
||||||
@ -766,34 +784,30 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
|
|||||||
Expanded(
|
Expanded(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.only(right: 5.0),
|
padding: const EdgeInsets.only(right: 5.0),
|
||||||
child: TextFormField(
|
child: NumberFormField(
|
||||||
decoration: const InputDecoration(
|
label: 'Immediate Bolus',
|
||||||
labelText: 'Immediate Bolus',
|
suffix: ' U',
|
||||||
suffixText: ' U',
|
|
||||||
),
|
|
||||||
controller: _immediateUnitsController,
|
controller: _immediateUnitsController,
|
||||||
|
max: double.tryParse(_unitsController.text),
|
||||||
|
step: Settings.insulinSteps,
|
||||||
readOnly: true,
|
readOnly: true,
|
||||||
enabled:
|
onChanged: (value) => updateDelayedRatio(
|
||||||
(int.tryParse(_delayController.text) ??
|
immediateUnitsUpdate: value),
|
||||||
0) !=
|
|
||||||
0,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.only(left: 5.0),
|
padding: const EdgeInsets.only(left: 5.0),
|
||||||
child: TextFormField(
|
child: NumberFormField(
|
||||||
decoration: const InputDecoration(
|
label: 'Delayed Bolus',
|
||||||
labelText: 'Delayed Bolus',
|
suffix: ' U',
|
||||||
suffixText: ' U',
|
|
||||||
),
|
|
||||||
controller: _delayedUnitsController,
|
controller: _delayedUnitsController,
|
||||||
|
max: double.tryParse(_unitsController.text),
|
||||||
|
step: Settings.insulinSteps,
|
||||||
readOnly: true,
|
readOnly: true,
|
||||||
enabled:
|
onChanged: (value) => updateDelayedRatio(
|
||||||
(int.tryParse(_delayController.text) ??
|
delayedUnitsUpdate: value),
|
||||||
0) !=
|
|
||||||
0,
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import 'package:diameter/components/dialogs.dart';
|
import 'package:diameter/utils/dialog_utils.dart';
|
||||||
import 'package:diameter/models/log_bolus.dart';
|
import 'package:diameter/models/log_bolus.dart';
|
||||||
import 'package:diameter/models/log_entry.dart';
|
import 'package:diameter/models/log_entry.dart';
|
||||||
import 'package:diameter/models/settings.dart';
|
import 'package:diameter/models/settings.dart';
|
||||||
@ -25,6 +25,12 @@ class LogBolusListScreen extends StatefulWidget {
|
|||||||
class _LogBolusListScreenState extends State<LogBolusListScreen> {
|
class _LogBolusListScreenState extends State<LogBolusListScreen> {
|
||||||
final ScrollController _scrollController = ScrollController();
|
final ScrollController _scrollController = ScrollController();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_scrollController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
void reload({String? message}) {
|
void reload({String? message}) {
|
||||||
widget.reload();
|
widget.reload();
|
||||||
|
|
||||||
@ -60,7 +66,7 @@ class _LogBolusListScreenState extends State<LogBolusListScreen> {
|
|||||||
|
|
||||||
void handleDeleteAction(LogBolus logBolus) async {
|
void handleDeleteAction(LogBolus logBolus) async {
|
||||||
if (Settings.get().showConfirmationDialogOnDelete) {
|
if (Settings.get().showConfirmationDialogOnDelete) {
|
||||||
Dialogs.showConfirmationDialog(
|
DialogUtils.showConfirmationDialog(
|
||||||
context: context,
|
context: context,
|
||||||
onConfirm: () => onDelete(logBolus),
|
onConfirm: () => onDelete(logBolus),
|
||||||
message: 'Are you sure you want to delete this Bolus?',
|
message: 'Are you sure you want to delete this Bolus?',
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
import 'package:diameter/components/detail.dart';
|
import 'package:diameter/components/detail.dart';
|
||||||
import 'package:diameter/components/dialogs.dart';
|
import 'package:diameter/components/forms/date_time_form_field.dart';
|
||||||
import 'package:diameter/components/forms.dart';
|
import 'package:diameter/components/forms/number_form_field.dart';
|
||||||
|
import 'package:diameter/components/forms/time_of_day_form_field.dart';
|
||||||
|
import 'package:diameter/utils/dialog_utils.dart';
|
||||||
|
import 'package:diameter/components/forms/form_wrapper.dart';
|
||||||
import 'package:diameter/models/log_bolus.dart';
|
import 'package:diameter/models/log_bolus.dart';
|
||||||
import 'package:diameter/models/log_entry.dart';
|
import 'package:diameter/models/log_entry.dart';
|
||||||
import 'package:diameter/models/log_meal.dart';
|
import 'package:diameter/models/log_meal.dart';
|
||||||
@ -40,8 +43,10 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
|
|||||||
|
|
||||||
final _timeController = TextEditingController(text: '');
|
final _timeController = TextEditingController(text: '');
|
||||||
final _dateController = TextEditingController(text: '');
|
final _dateController = TextEditingController(text: '');
|
||||||
final _mgPerDlController = TextEditingController(text: '');
|
final _mgPerDlController =
|
||||||
final _mmolPerLController = TextEditingController(text: '');
|
TextEditingController(text: Settings.targetMgPerDl.toString());
|
||||||
|
final _mmolPerLController =
|
||||||
|
TextEditingController(text: Settings.targetMmolPerL.toString());
|
||||||
final _notesController = TextEditingController(text: '');
|
final _notesController = TextEditingController(text: '');
|
||||||
|
|
||||||
late FloatingActionButton addMealButton;
|
late FloatingActionButton addMealButton;
|
||||||
@ -109,6 +114,17 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
|
|||||||
updateTime();
|
updateTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_scrollController.dispose();
|
||||||
|
_timeController.dispose();
|
||||||
|
_dateController.dispose();
|
||||||
|
_mgPerDlController.dispose();
|
||||||
|
_mmolPerLController.dispose();
|
||||||
|
_notesController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
void reload({String? message}) {
|
void reload({String? message}) {
|
||||||
if (widget.id != 0) {
|
if (widget.id != 0) {
|
||||||
setState(() {
|
setState(() {
|
||||||
@ -137,27 +153,28 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
|
|||||||
_dateController.text = DateTimeUtils.displayDate(_time);
|
_dateController.text = DateTimeUtils.displayDate(_time);
|
||||||
}
|
}
|
||||||
|
|
||||||
void convertBetweenMgPerDlAndMmolPerL() {
|
void convertBetweenMgPerDlAndMmolPerL(double? value) async {
|
||||||
int? mgPerDl;
|
if (value != null) {
|
||||||
double? mmolPerL;
|
if (Settings.glucoseMeasurement == GlucoseMeasurement.mgPerDl &&
|
||||||
|
|
||||||
if (Settings.glucoseMeasurement != GlucoseMeasurement.mmolPerL &&
|
|
||||||
_mgPerDlController.text != '') {
|
_mgPerDlController.text != '') {
|
||||||
mgPerDl = int.tryParse(_mgPerDlController.text);
|
_mgPerDlController.text = value.toInt().toString();
|
||||||
setState(() {
|
setState(() {
|
||||||
_mmolPerLController.text =
|
_mmolPerLController.text =
|
||||||
Utils.convertMgPerDlToMmolPerL(mgPerDl ?? 0).toString();
|
Utils.convertMgPerDlToMmolPerL(value.toInt()).toString();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (Settings.glucoseMeasurement != GlucoseMeasurement.mgPerDl &&
|
if (Settings.glucoseMeasurement == GlucoseMeasurement.mmolPerL &&
|
||||||
_mmolPerLController.text != '') {
|
_mmolPerLController.text != '') {
|
||||||
mmolPerL = double.tryParse(_mmolPerLController.text);
|
_mmolPerLController.text =
|
||||||
|
Utils.toStringMatchingTemplateFractionPrecision(
|
||||||
|
value, Settings.mmolPerLSteps);
|
||||||
setState(() {
|
setState(() {
|
||||||
_mgPerDlController.text =
|
_mgPerDlController.text =
|
||||||
Utils.convertMmolPerLToMgPerDl(mmolPerL ?? 0).toString();
|
Utils.convertMmolPerLToMgPerDl(value.toDouble()).toString();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void handleSaveAction({bool close = false}) async {
|
void handleSaveAction({bool close = false}) async {
|
||||||
setState(() {
|
setState(() {
|
||||||
@ -207,7 +224,7 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
|
|||||||
double.tryParse(_mmolPerLController.text) !=
|
double.tryParse(_mmolPerLController.text) !=
|
||||||
_logEntry!.mmolPerL ||
|
_logEntry!.mmolPerL ||
|
||||||
_notesController.text != (_logEntry!.notes ?? ''))))) {
|
_notesController.text != (_logEntry!.notes ?? ''))))) {
|
||||||
Dialogs.showCancelConfirmationDialog(
|
DialogUtils.showCancelConfirmationDialog(
|
||||||
context: context,
|
context: context,
|
||||||
isNew: _isNew,
|
isNew: _isNew,
|
||||||
onSave: handleSaveAction,
|
onSave: handleSaveAction,
|
||||||
@ -337,76 +354,52 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
|
|||||||
children: [
|
children: [
|
||||||
Settings.glucoseMeasurement ==
|
Settings.glucoseMeasurement ==
|
||||||
GlucoseMeasurement.mgPerDl ||
|
GlucoseMeasurement.mgPerDl ||
|
||||||
[
|
|
||||||
GlucoseDisplayMode.both,
|
|
||||||
GlucoseDisplayMode.bothForDetail
|
|
||||||
].contains(Settings.glucoseDisplayMode)
|
|
||||||
? Expanded(
|
|
||||||
child: TextFormField(
|
|
||||||
decoration: const InputDecoration(
|
|
||||||
labelText: 'mg/dl',
|
|
||||||
suffixText: 'mg/dl',
|
|
||||||
),
|
|
||||||
readOnly: Settings.glucoseMeasurement ==
|
|
||||||
GlucoseMeasurement.mmolPerL,
|
|
||||||
controller: _mgPerDlController,
|
|
||||||
onChanged: (_) async {
|
|
||||||
await Future.delayed(
|
|
||||||
const Duration(seconds: 1));
|
|
||||||
convertBetweenMgPerDlAndMmolPerL();
|
|
||||||
},
|
|
||||||
keyboardType: const TextInputType
|
|
||||||
.numberWithOptions(),
|
|
||||||
validator: (value) {
|
|
||||||
if (value!.trim().isEmpty &&
|
|
||||||
_mmolPerLController.text
|
|
||||||
.trim()
|
|
||||||
.isEmpty) {
|
|
||||||
return 'How high is your blood sugar?';
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
},
|
|
||||||
),
|
|
||||||
)
|
|
||||||
: Container(),
|
|
||||||
[
|
|
||||||
GlucoseDisplayMode.both,
|
|
||||||
GlucoseDisplayMode.bothForDetail
|
|
||||||
].contains(Settings.glucoseDisplayMode)
|
|
||||||
? const SizedBox(width: 10.0)
|
|
||||||
: Container(),
|
|
||||||
Settings.glucoseMeasurement ==
|
|
||||||
GlucoseMeasurement.mmolPerL ||
|
|
||||||
Settings.glucoseDisplayMode ==
|
Settings.glucoseDisplayMode ==
|
||||||
GlucoseDisplayMode.both ||
|
GlucoseDisplayMode.both ||
|
||||||
Settings.glucoseDisplayMode ==
|
Settings.glucoseDisplayMode ==
|
||||||
GlucoseDisplayMode.bothForDetail
|
GlucoseDisplayMode.bothForDetail
|
||||||
? Expanded(
|
? Expanded(
|
||||||
child: TextFormField(
|
flex: Settings.glucoseMeasurement ==
|
||||||
decoration: const InputDecoration(
|
GlucoseMeasurement.mgPerDl
|
||||||
labelText: 'mmol/l',
|
? 2
|
||||||
suffixText: 'mmol/l',
|
: 1,
|
||||||
|
child: NumberFormField(
|
||||||
|
label: 'per mg/dl',
|
||||||
|
suffix: 'mg/dl',
|
||||||
|
readOnly: Settings.glucoseMeasurement ==
|
||||||
|
GlucoseMeasurement.mmolPerL,
|
||||||
|
showSteppers:
|
||||||
|
Settings.glucoseMeasurement ==
|
||||||
|
GlucoseMeasurement.mgPerDl,
|
||||||
|
controller: _mgPerDlController,
|
||||||
|
onChanged:
|
||||||
|
convertBetweenMgPerDlAndMmolPerL,
|
||||||
),
|
),
|
||||||
|
)
|
||||||
|
: Container(),
|
||||||
|
Settings.glucoseMeasurement ==
|
||||||
|
GlucoseMeasurement.mmolPerL ||
|
||||||
|
[
|
||||||
|
GlucoseDisplayMode.both,
|
||||||
|
GlucoseDisplayMode.bothForDetail
|
||||||
|
].contains(Settings.glucoseDisplayMode)
|
||||||
|
? Expanded(
|
||||||
|
flex: Settings.glucoseMeasurement ==
|
||||||
|
GlucoseMeasurement.mmolPerL
|
||||||
|
? 2
|
||||||
|
: 1,
|
||||||
|
child: NumberFormField(
|
||||||
|
label: 'per mmol/l',
|
||||||
|
suffix: 'mmol/l',
|
||||||
readOnly: Settings.glucoseMeasurement ==
|
readOnly: Settings.glucoseMeasurement ==
|
||||||
GlucoseMeasurement.mgPerDl,
|
GlucoseMeasurement.mgPerDl,
|
||||||
|
showSteppers:
|
||||||
|
Settings.glucoseMeasurement ==
|
||||||
|
GlucoseMeasurement.mmolPerL,
|
||||||
controller: _mmolPerLController,
|
controller: _mmolPerLController,
|
||||||
onChanged: (_) async {
|
step: Settings.mmolPerLSteps,
|
||||||
await Future.delayed(
|
onChanged:
|
||||||
const Duration(seconds: 1));
|
convertBetweenMgPerDlAndMmolPerL,
|
||||||
convertBetweenMgPerDlAndMmolPerL();
|
|
||||||
},
|
|
||||||
keyboardType:
|
|
||||||
const TextInputType.numberWithOptions(
|
|
||||||
decimal: true),
|
|
||||||
validator: (value) {
|
|
||||||
if (value!.trim().isEmpty &&
|
|
||||||
_mgPerDlController.text
|
|
||||||
.trim()
|
|
||||||
.isEmpty) {
|
|
||||||
return 'How high is your blood sugar?';
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
: Container(),
|
: Container(),
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import 'package:diameter/components/detail.dart';
|
import 'package:diameter/components/detail.dart';
|
||||||
import 'package:diameter/components/dialogs.dart';
|
import 'package:diameter/components/forms/number_form_field.dart';
|
||||||
import 'package:diameter/components/dropdown.dart';
|
import 'package:diameter/utils/dialog_utils.dart';
|
||||||
import 'package:diameter/components/forms.dart';
|
import 'package:diameter/components/forms/auto_complete_dropdown_button.dart';
|
||||||
|
import 'package:diameter/components/forms/form_wrapper.dart';
|
||||||
import 'package:diameter/models/accuracy.dart';
|
import 'package:diameter/models/accuracy.dart';
|
||||||
import 'package:diameter/models/log_meal.dart';
|
import 'package:diameter/models/log_meal.dart';
|
||||||
import 'package:diameter/models/meal.dart';
|
import 'package:diameter/models/meal.dart';
|
||||||
@ -37,13 +38,10 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
|
|||||||
bool _isSaving = false;
|
bool _isSaving = false;
|
||||||
bool _isExpanded = false;
|
bool _isExpanded = false;
|
||||||
|
|
||||||
double _amount = 1;
|
|
||||||
|
|
||||||
final GlobalKey<FormState> _logMealForm = GlobalKey<FormState>();
|
final GlobalKey<FormState> _logMealForm = GlobalKey<FormState>();
|
||||||
final ScrollController _scrollController = ScrollController();
|
final ScrollController _scrollController = ScrollController();
|
||||||
|
|
||||||
final _valueController = TextEditingController(text: '');
|
final _valueController = TextEditingController(text: '');
|
||||||
final _amountController = TextEditingController(text: '');
|
|
||||||
final _carbsRatioController = TextEditingController(text: '');
|
final _carbsRatioController = TextEditingController(text: '');
|
||||||
final _portionSizeController = TextEditingController(text: '');
|
final _portionSizeController = TextEditingController(text: '');
|
||||||
final _totalCarbsController = TextEditingController(text: '');
|
final _totalCarbsController = TextEditingController(text: '');
|
||||||
@ -62,6 +60,7 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
|
|||||||
final _mealPortionTypeController = TextEditingController(text: '');
|
final _mealPortionTypeController = TextEditingController(text: '');
|
||||||
final _portionSizeAccuracyController = TextEditingController(text: '');
|
final _portionSizeAccuracyController = TextEditingController(text: '');
|
||||||
final _carbsRatioAccuracyController = TextEditingController(text: '');
|
final _carbsRatioAccuracyController = TextEditingController(text: '');
|
||||||
|
final _amountController = TextEditingController(text: '1');
|
||||||
|
|
||||||
List<Meal> _meals = [];
|
List<Meal> _meals = [];
|
||||||
List<MealCategory> _mealCategories = [];
|
List<MealCategory> _mealCategories = [];
|
||||||
@ -84,12 +83,11 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
|
|||||||
|
|
||||||
if (widget.id != 0) {
|
if (widget.id != 0) {
|
||||||
_valueController.text = _logMeal!.value;
|
_valueController.text = _logMeal!.value;
|
||||||
_amountController.text = _logMeal!.amount.toString();
|
|
||||||
_carbsRatioController.text = (_logMeal!.carbsRatio ?? '').toString();
|
_carbsRatioController.text = (_logMeal!.carbsRatio ?? '').toString();
|
||||||
_portionSizeController.text = (_logMeal!.portionSize ?? '').toString();
|
_portionSizeController.text = (_logMeal!.portionSize ?? '').toString();
|
||||||
_totalCarbsController.text = (_logMeal!.totalCarbs ?? '').toString();
|
_totalCarbsController.text = (_logMeal!.totalCarbs ?? '').toString();
|
||||||
|
_amountController.text = (_logMeal!.amount).toString();
|
||||||
_notesController.text = _logMeal!.notes ?? '';
|
_notesController.text = _logMeal!.notes ?? '';
|
||||||
|
|
||||||
_meal = _logMeal!.meal.target;
|
_meal = _logMeal!.meal.target;
|
||||||
_mealController.text = (_meal ?? '').toString();
|
_mealController.text = (_meal ?? '').toString();
|
||||||
_mealSource = _logMeal!.mealSource.target;
|
_mealSource = _logMeal!.mealSource.target;
|
||||||
@ -105,10 +103,24 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
|
|||||||
_carbsRatioAccuracyController.text =
|
_carbsRatioAccuracyController.text =
|
||||||
(_carbsRatioAccuracy ?? '').toString();
|
(_carbsRatioAccuracy ?? '').toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_amountController.text == '') {
|
|
||||||
_amountController.text = '1';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_scrollController.dispose();
|
||||||
|
_valueController.dispose();
|
||||||
|
_carbsRatioController.dispose();
|
||||||
|
_portionSizeController.dispose();
|
||||||
|
_totalCarbsController.dispose();
|
||||||
|
_notesController.dispose();
|
||||||
|
_mealController.dispose();
|
||||||
|
_mealSourceController.dispose();
|
||||||
|
_mealCategoryController.dispose();
|
||||||
|
_mealPortionTypeController.dispose();
|
||||||
|
_portionSizeAccuracyController.dispose();
|
||||||
|
_carbsRatioAccuracyController.dispose();
|
||||||
|
_amountController.dispose();
|
||||||
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
void reload({String? message}) {
|
void reload({String? message}) {
|
||||||
@ -197,6 +209,7 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
|
|||||||
carbsRatio: double.tryParse(_carbsRatioController.text),
|
carbsRatio: double.tryParse(_carbsRatioController.text),
|
||||||
portionSize: double.tryParse(_portionSizeController.text),
|
portionSize: double.tryParse(_portionSizeController.text),
|
||||||
totalCarbs: double.tryParse(_totalCarbsController.text),
|
totalCarbs: double.tryParse(_totalCarbsController.text),
|
||||||
|
amount: double.parse(_amountController.text),
|
||||||
);
|
);
|
||||||
logMeal.logEntry.targetId = widget.logEntryId;
|
logMeal.logEntry.targetId = widget.logEntryId;
|
||||||
logMeal.meal.target = _meal;
|
logMeal.meal.target = _meal;
|
||||||
@ -222,6 +235,7 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
|
|||||||
_mealSource != null ||
|
_mealSource != null ||
|
||||||
_mealCategory != null ||
|
_mealCategory != null ||
|
||||||
_mealPortionType != null ||
|
_mealPortionType != null ||
|
||||||
|
double.tryParse(_amountController.text) != 1 ||
|
||||||
double.tryParse(_carbsRatioController.text) != null ||
|
double.tryParse(_carbsRatioController.text) != null ||
|
||||||
double.tryParse(_portionSizeController.text) != null ||
|
double.tryParse(_portionSizeController.text) != null ||
|
||||||
double.tryParse(_totalCarbsController.text) != null ||
|
double.tryParse(_totalCarbsController.text) != null ||
|
||||||
@ -234,6 +248,8 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
|
|||||||
_mealSource != _logMeal!.mealSource.target ||
|
_mealSource != _logMeal!.mealSource.target ||
|
||||||
_mealCategory != _logMeal!.mealCategory.target ||
|
_mealCategory != _logMeal!.mealCategory.target ||
|
||||||
_mealPortionType != _logMeal!.mealPortionType.target ||
|
_mealPortionType != _logMeal!.mealPortionType.target ||
|
||||||
|
double.tryParse(_amountController.text) !=
|
||||||
|
_logMeal!.amount ||
|
||||||
double.tryParse(_carbsRatioController.text) !=
|
double.tryParse(_carbsRatioController.text) !=
|
||||||
_logMeal!.carbsRatio ||
|
_logMeal!.carbsRatio ||
|
||||||
double.tryParse(_portionSizeController.text) !=
|
double.tryParse(_portionSizeController.text) !=
|
||||||
@ -245,7 +261,7 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
|
|||||||
_portionSizeAccuracy !=
|
_portionSizeAccuracy !=
|
||||||
_logMeal!.portionSizeAccuracy.target ||
|
_logMeal!.portionSizeAccuracy.target ||
|
||||||
_notesController.text != (_logMeal!.notes ?? ''))))) {
|
_notesController.text != (_logMeal!.notes ?? ''))))) {
|
||||||
Dialogs.showCancelConfirmationDialog(
|
DialogUtils.showCancelConfirmationDialog(
|
||||||
context: context,
|
context: context,
|
||||||
isNew: _isNew,
|
isNew: _isNew,
|
||||||
onSave: handleSaveAction,
|
onSave: handleSaveAction,
|
||||||
@ -256,15 +272,16 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void updateAmount(double? amount) {
|
void updateAmount(double? amount) {
|
||||||
double? previousAmount;
|
double previousAmount;
|
||||||
|
double newAmount;
|
||||||
double? portionSize;
|
double? portionSize;
|
||||||
double? carbsRatio;
|
double? carbsRatio;
|
||||||
|
|
||||||
previousAmount = _amount;
|
previousAmount = double.tryParse(_amountController.text) ?? 1;
|
||||||
|
newAmount = amount?.toDouble() ?? 1;
|
||||||
|
|
||||||
setState(() {
|
setState(() {
|
||||||
_amountController.text = (amount ?? '').toString();
|
_amountController.text = newAmount.toString();
|
||||||
_amount = amount ?? 1;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (_carbsRatioController.text != '') {
|
if (_carbsRatioController.text != '') {
|
||||||
@ -274,9 +291,9 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
|
|||||||
portionSize = double.tryParse(_portionSizeController.text);
|
portionSize = double.tryParse(_portionSizeController.text);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (amount != null && portionSize != null) {
|
if (portionSize != null) {
|
||||||
setState(() {
|
setState(() {
|
||||||
portionSize = portionSize! / (previousAmount ?? 1) * amount;
|
portionSize = portionSize! / previousAmount * newAmount;
|
||||||
_portionSizeController.text = portionSize.toString();
|
_portionSizeController.text = portionSize.toString();
|
||||||
});
|
});
|
||||||
if (carbsRatio != null) {
|
if (carbsRatio != null) {
|
||||||
@ -289,14 +306,11 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void calculateThirdMeasurementOfPortionCarbsRelation() {
|
void calculateThirdMeasurementOfPortionCarbsRelation() {
|
||||||
int? amount;
|
double? amount = double.tryParse(_amountController.text) ?? 1;
|
||||||
double? carbsRatio;
|
double? carbsRatio;
|
||||||
double? portionSize;
|
double? portionSize;
|
||||||
double? carbsPerPortion;
|
double? carbsPerPortion;
|
||||||
|
|
||||||
if (_amountController.text != '') {
|
|
||||||
amount = int.tryParse(_amountController.text);
|
|
||||||
}
|
|
||||||
if (_carbsRatioController.text != '') {
|
if (_carbsRatioController.text != '') {
|
||||||
carbsRatio = double.tryParse(_carbsRatioController.text);
|
carbsRatio = double.tryParse(_carbsRatioController.text);
|
||||||
}
|
}
|
||||||
@ -310,14 +324,13 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
|
|||||||
if (carbsRatio != null && portionSize != null && carbsPerPortion == null) {
|
if (carbsRatio != null && portionSize != null && carbsPerPortion == null) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_totalCarbsController.text =
|
_totalCarbsController.text =
|
||||||
Utils.calculateCarbs(carbsRatio!, portionSize! * (amount ?? 1))
|
Utils.calculateCarbs(carbsRatio!, portionSize! * amount).toString();
|
||||||
.toString();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (carbsRatio == null && portionSize != null && carbsPerPortion != null) {
|
if (carbsRatio == null && portionSize != null && carbsPerPortion != null) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_carbsRatioController.text = Utils.calculateCarbsRatio(
|
_carbsRatioController.text =
|
||||||
carbsPerPortion!, portionSize! * (amount ?? 1))
|
Utils.calculateCarbsRatio(carbsPerPortion!, portionSize! * amount)
|
||||||
.toString();
|
.toString();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -388,13 +401,45 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
NumberFormField(
|
Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: NumberFormField(
|
||||||
controller: _amountController,
|
controller: _amountController,
|
||||||
label: 'Amount',
|
label: 'Amount',
|
||||||
suffix: _mealPortionType?.value,
|
suffix: _mealPortionType?.value,
|
||||||
min: 0,
|
|
||||||
onChanged: updateAmount,
|
onChanged: updateAmount,
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => updateAmount(0.5),
|
||||||
|
child: Column(
|
||||||
|
children: const [
|
||||||
|
Text('1', style: TextStyle(decoration: TextDecoration.underline, decorationThickness: 2),),
|
||||||
|
Text('2'),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => updateAmount(0.33),
|
||||||
|
child: Column(
|
||||||
|
children: const [
|
||||||
|
Text('1', style: TextStyle(decoration: TextDecoration.underline, decorationThickness: 2),),
|
||||||
|
Text('3'),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => updateAmount(0.67),
|
||||||
|
child: Column(
|
||||||
|
children: const [
|
||||||
|
Text('2', style: TextStyle(decoration: TextDecoration.underline, decorationThickness: 2),),
|
||||||
|
Text('3'),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import 'package:diameter/components/dialogs.dart';
|
import 'package:diameter/utils/dialog_utils.dart';
|
||||||
import 'package:diameter/models/log_entry.dart';
|
import 'package:diameter/models/log_entry.dart';
|
||||||
import 'package:diameter/models/log_meal.dart';
|
import 'package:diameter/models/log_meal.dart';
|
||||||
import 'package:diameter/models/settings.dart';
|
import 'package:diameter/models/settings.dart';
|
||||||
@ -21,6 +21,12 @@ class LogMealListScreen extends StatefulWidget {
|
|||||||
class _LogMealListScreenState extends State<LogMealListScreen> {
|
class _LogMealListScreenState extends State<LogMealListScreen> {
|
||||||
final ScrollController _scrollController = ScrollController();
|
final ScrollController _scrollController = ScrollController();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_scrollController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
void reload({String? message}) {
|
void reload({String? message}) {
|
||||||
widget.reload();
|
widget.reload();
|
||||||
|
|
||||||
@ -56,7 +62,7 @@ class _LogMealListScreenState extends State<LogMealListScreen> {
|
|||||||
|
|
||||||
void handleDeleteAction(LogMeal meal) async {
|
void handleDeleteAction(LogMeal meal) async {
|
||||||
if (Settings.get().showConfirmationDialogOnDelete) {
|
if (Settings.get().showConfirmationDialogOnDelete) {
|
||||||
Dialogs.showConfirmationDialog(
|
DialogUtils.showConfirmationDialog(
|
||||||
context: context,
|
context: context,
|
||||||
onConfirm: () => onDelete(meal),
|
onConfirm: () => onDelete(meal),
|
||||||
message: 'Are you sure you want to delete this Meal?',
|
message: 'Are you sure you want to delete this Meal?',
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
import 'package:diameter/components/detail.dart';
|
import 'package:diameter/components/detail.dart';
|
||||||
import 'package:diameter/components/dialogs.dart';
|
import 'package:diameter/components/forms/boolean_form_field.dart';
|
||||||
import 'package:diameter/components/dropdown.dart';
|
import 'package:diameter/components/forms/date_time_form_field.dart';
|
||||||
import 'package:diameter/components/forms.dart';
|
import 'package:diameter/components/forms/time_of_day_form_field.dart';
|
||||||
|
import 'package:diameter/utils/dialog_utils.dart';
|
||||||
|
import 'package:diameter/components/forms/auto_complete_dropdown_button.dart';
|
||||||
|
import 'package:diameter/components/forms/form_wrapper.dart';
|
||||||
import 'package:diameter/models/basal_profile.dart';
|
import 'package:diameter/models/basal_profile.dart';
|
||||||
import 'package:diameter/models/bolus_profile.dart';
|
import 'package:diameter/models/bolus_profile.dart';
|
||||||
import 'package:diameter/models/log_event.dart';
|
import 'package:diameter/models/log_event.dart';
|
||||||
@ -99,6 +102,21 @@ class _LogEventDetailScreenState extends State<LogEventDetailScreen> {
|
|||||||
updateEndTime();
|
updateEndTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_scrollController.dispose();
|
||||||
|
_timeController.dispose();
|
||||||
|
_endTimeController.dispose();
|
||||||
|
_dateController.dispose();
|
||||||
|
_endDateController.dispose();
|
||||||
|
_reminderDurationController.dispose();
|
||||||
|
_notesController.dispose();
|
||||||
|
_eventTypeController.dispose();
|
||||||
|
_bolusProfileController.dispose();
|
||||||
|
_basalProfileController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
void reload({String? message}) {
|
void reload({String? message}) {
|
||||||
if (widget.id != 0) {
|
if (widget.id != 0) {
|
||||||
setState(() {
|
setState(() {
|
||||||
@ -242,7 +260,7 @@ class _LogEventDetailScreenState extends State<LogEventDetailScreen> {
|
|||||||
(_notesController.text != (_logEvent!.notes ?? '') ||
|
(_notesController.text != (_logEvent!.notes ?? '') ||
|
||||||
_eventType != _logEvent!.eventType.target ||
|
_eventType != _logEvent!.eventType.target ||
|
||||||
_hasEndTime != _logEvent!.hasEndTime)))) {
|
_hasEndTime != _logEvent!.hasEndTime)))) {
|
||||||
Dialogs.showCancelConfirmationDialog(
|
DialogUtils.showCancelConfirmationDialog(
|
||||||
context: context,
|
context: context,
|
||||||
isNew: _isNew,
|
isNew: _isNew,
|
||||||
onSave: handleSaveAction,
|
onSave: handleSaveAction,
|
||||||
@ -382,7 +400,9 @@ class _LogEventDetailScreenState extends State<LogEventDetailScreen> {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
TextFormField(
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 10.0),
|
||||||
|
child: TextFormField(
|
||||||
controller: _reminderDurationController,
|
controller: _reminderDurationController,
|
||||||
keyboardType:
|
keyboardType:
|
||||||
const TextInputType.numberWithOptions(),
|
const TextInputType.numberWithOptions(),
|
||||||
@ -392,6 +412,7 @@ class _LogEventDetailScreenState extends State<LogEventDetailScreen> {
|
|||||||
enabled: _hasEndTime,
|
enabled: _hasEndTime,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
@ -426,7 +447,9 @@ class _LogEventDetailScreenState extends State<LogEventDetailScreen> {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
Row(
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(top: 10.0),
|
||||||
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: AutoCompleteDropdownButton<
|
child: AutoCompleteDropdownButton<
|
||||||
@ -459,6 +482,7 @@ class _LogEventDetailScreenState extends State<LogEventDetailScreen> {
|
|||||||
: Icons.edit),
|
: Icons.edit),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
),
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
: []),
|
: []),
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import 'package:diameter/components/dialogs.dart';
|
import 'package:diameter/utils/dialog_utils.dart';
|
||||||
import 'package:diameter/models/log_event.dart';
|
import 'package:diameter/models/log_event.dart';
|
||||||
import 'package:diameter/models/settings.dart';
|
import 'package:diameter/models/settings.dart';
|
||||||
import 'package:diameter/screens/log/log_event/log_event_detail.dart';
|
import 'package:diameter/screens/log/log_event/log_event_detail.dart';
|
||||||
@ -25,6 +25,12 @@ class _LogEventListScreenState extends State<LogEventListScreen> {
|
|||||||
reload();
|
reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_scrollController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
void reload({String? message}) {
|
void reload({String? message}) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_activeEvents = LogEvent.getAllActiveForTime(DateTime.now());
|
_activeEvents = LogEvent.getAllActiveForTime(DateTime.now());
|
||||||
@ -73,7 +79,7 @@ class _LogEventListScreenState extends State<LogEventListScreen> {
|
|||||||
|
|
||||||
void handleDeleteAction(LogEvent logEvent) async {
|
void handleDeleteAction(LogEvent logEvent) async {
|
||||||
if (Settings.get().showConfirmationDialogOnDelete) {
|
if (Settings.get().showConfirmationDialogOnDelete) {
|
||||||
Dialogs.showConfirmationDialog(
|
DialogUtils.showConfirmationDialog(
|
||||||
context: context,
|
context: context,
|
||||||
onConfirm: () => onDelete(logEvent),
|
onConfirm: () => onDelete(logEvent),
|
||||||
message: 'Are you sure you want to delete this Event?',
|
message: 'Are you sure you want to delete this Event?',
|
||||||
@ -91,7 +97,7 @@ class _LogEventListScreenState extends State<LogEventListScreen> {
|
|||||||
|
|
||||||
void handleStopAction(LogEvent event) async {
|
void handleStopAction(LogEvent event) async {
|
||||||
if (Settings.get().showConfirmationDialogOnStopEvent) {
|
if (Settings.get().showConfirmationDialogOnStopEvent) {
|
||||||
Dialogs.showConfirmationDialog(
|
DialogUtils.showConfirmationDialog(
|
||||||
context: context,
|
context: context,
|
||||||
onConfirm: () => onStop(event),
|
onConfirm: () => onStop(event),
|
||||||
message: 'Are you sure you want to end this Event?',
|
message: 'Are you sure you want to end this Event?',
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
import 'package:diameter/components/detail.dart';
|
import 'package:diameter/components/detail.dart';
|
||||||
import 'package:diameter/components/dialogs.dart';
|
import 'package:diameter/components/forms/boolean_form_field.dart';
|
||||||
import 'package:diameter/components/dropdown.dart';
|
import 'package:diameter/components/forms/duration_form_field.dart';
|
||||||
import 'package:diameter/components/forms.dart';
|
import 'package:diameter/utils/dialog_utils.dart';
|
||||||
|
import 'package:diameter/components/forms/auto_complete_dropdown_button.dart';
|
||||||
|
import 'package:diameter/components/forms/form_wrapper.dart';
|
||||||
import 'package:diameter/models/basal_profile.dart';
|
import 'package:diameter/models/basal_profile.dart';
|
||||||
import 'package:diameter/models/bolus_profile.dart';
|
import 'package:diameter/models/bolus_profile.dart';
|
||||||
import 'package:diameter/models/log_event_type.dart';
|
import 'package:diameter/models/log_event_type.dart';
|
||||||
@ -32,10 +34,10 @@ class _EventTypeDetailScreenState extends State<EventTypeDetailScreen> {
|
|||||||
final ScrollController _scrollController = ScrollController();
|
final ScrollController _scrollController = ScrollController();
|
||||||
|
|
||||||
final _valueController = TextEditingController(text: '');
|
final _valueController = TextEditingController(text: '');
|
||||||
final _defaultReminderDurationController = TextEditingController(text: '');
|
|
||||||
final _notesController = TextEditingController(text: '');
|
final _notesController = TextEditingController(text: '');
|
||||||
|
|
||||||
bool _hasEndTime = false;
|
bool _hasEndTime = false;
|
||||||
|
int _defaultReminderDuration = 0;
|
||||||
BolusProfile? _bolusProfile;
|
BolusProfile? _bolusProfile;
|
||||||
BasalProfile? _basalProfile;
|
BasalProfile? _basalProfile;
|
||||||
final _bolusProfileController = TextEditingController(text: '');
|
final _bolusProfileController = TextEditingController(text: '');
|
||||||
@ -52,8 +54,8 @@ class _EventTypeDetailScreenState extends State<EventTypeDetailScreen> {
|
|||||||
|
|
||||||
if (_logEventType != null) {
|
if (_logEventType != null) {
|
||||||
_valueController.text = _logEventType!.value;
|
_valueController.text = _logEventType!.value;
|
||||||
_defaultReminderDurationController.text =
|
_defaultReminderDuration =
|
||||||
(_logEventType!.defaultReminderDuration ?? '').toString();
|
_logEventType!.defaultReminderDuration ?? 0;
|
||||||
_hasEndTime = _logEventType!.hasEndTime;
|
_hasEndTime = _logEventType!.hasEndTime;
|
||||||
_notesController.text = _logEventType!.notes ?? '';
|
_notesController.text = _logEventType!.notes ?? '';
|
||||||
_basalProfile = _logEventType!.basalProfile.target;
|
_basalProfile = _logEventType!.basalProfile.target;
|
||||||
@ -63,6 +65,16 @@ class _EventTypeDetailScreenState extends State<EventTypeDetailScreen> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_scrollController.dispose();
|
||||||
|
_valueController.dispose();
|
||||||
|
_notesController.dispose();
|
||||||
|
_bolusProfileController.dispose();
|
||||||
|
_basalProfileController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
void reload({String? message}) {
|
void reload({String? message}) {
|
||||||
if (widget.id != 0) {
|
if (widget.id != 0) {
|
||||||
setState(() {
|
setState(() {
|
||||||
@ -107,8 +119,7 @@ class _EventTypeDetailScreenState extends State<EventTypeDetailScreen> {
|
|||||||
id: widget.id,
|
id: widget.id,
|
||||||
value: _valueController.text,
|
value: _valueController.text,
|
||||||
notes: _notesController.text,
|
notes: _notesController.text,
|
||||||
defaultReminderDuration:
|
defaultReminderDuration: _defaultReminderDuration,
|
||||||
int.tryParse(_defaultReminderDurationController.text),
|
|
||||||
hasEndTime: _hasEndTime,
|
hasEndTime: _hasEndTime,
|
||||||
);
|
);
|
||||||
eventType.basalProfile.target = _basalProfile;
|
eventType.basalProfile.target = _basalProfile;
|
||||||
@ -127,17 +138,16 @@ class _EventTypeDetailScreenState extends State<EventTypeDetailScreen> {
|
|||||||
if (Settings.get().showConfirmationDialogOnCancel &&
|
if (Settings.get().showConfirmationDialogOnCancel &&
|
||||||
((isNew &&
|
((isNew &&
|
||||||
(_valueController.text != '' ||
|
(_valueController.text != '' ||
|
||||||
int.tryParse(_defaultReminderDurationController.text) !=
|
_defaultReminderDuration != 0 ||
|
||||||
null ||
|
|
||||||
_notesController.text != '' ||
|
_notesController.text != '' ||
|
||||||
_hasEndTime)) ||
|
_hasEndTime)) ||
|
||||||
(!isNew &&
|
(!isNew &&
|
||||||
(_valueController.text != _logEventType!.value ||
|
(_valueController.text != _logEventType!.value ||
|
||||||
int.tryParse(_defaultReminderDurationController.text) !=
|
_defaultReminderDuration !=
|
||||||
_logEventType!.defaultReminderDuration ||
|
_logEventType!.defaultReminderDuration ||
|
||||||
_notesController.text != (_logEventType!.notes ?? '') ||
|
_notesController.text != (_logEventType!.notes ?? '') ||
|
||||||
_hasEndTime != _logEventType!.hasEndTime)))) {
|
_hasEndTime != _logEventType!.hasEndTime)))) {
|
||||||
Dialogs.showCancelConfirmationDialog(
|
DialogUtils.showCancelConfirmationDialog(
|
||||||
context: context,
|
context: context,
|
||||||
isNew: isNew,
|
isNew: isNew,
|
||||||
onSave: handleSaveAction,
|
onSave: handleSaveAction,
|
||||||
@ -189,15 +199,11 @@ class _EventTypeDetailScreenState extends State<EventTypeDetailScreen> {
|
|||||||
? [
|
? [
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(bottom: 10.0),
|
padding: const EdgeInsets.only(bottom: 10.0),
|
||||||
child: TextFormField(
|
child: DurationFormField(
|
||||||
controller: _defaultReminderDurationController,
|
minutes: _defaultReminderDuration,
|
||||||
keyboardType:
|
label: 'Default Reminder Duration',
|
||||||
const TextInputType.numberWithOptions(),
|
onChanged: (value) => _defaultReminderDuration = value ?? 0,
|
||||||
decoration: InputDecoration(
|
showSteppers: true,
|
||||||
labelText: 'Default Reminder Duration',
|
|
||||||
suffixText: ' min',
|
|
||||||
enabled: _hasEndTime,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Padding(
|
Padding(
|
||||||
|
@ -22,6 +22,12 @@ class _LogEventTypeListScreenState extends State<LogEventTypeListScreen> {
|
|||||||
reload();
|
reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_scrollController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
void reload({String? message}) {
|
void reload({String? message}) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_logEventTypes = LogEventType.getAll();
|
_logEventTypes = LogEventType.getAll();
|
||||||
@ -66,8 +72,8 @@ class _LogEventTypeListScreenState extends State<LogEventTypeListScreen> {
|
|||||||
Navigator.push(
|
Navigator.push(
|
||||||
context,
|
context,
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (context) =>
|
builder: (context) => EventTypeDetailScreen(
|
||||||
EventTypeDetailScreen(id: logEventType.id),
|
id: logEventType.id),
|
||||||
),
|
),
|
||||||
).then((result) => reload(message: result?[0]));
|
).then((result) => reload(message: result?[0]));
|
||||||
},
|
},
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import 'package:diameter/components/detail.dart';
|
import 'package:diameter/components/detail.dart';
|
||||||
import 'package:diameter/components/dialogs.dart';
|
import 'package:diameter/utils/dialog_utils.dart';
|
||||||
import 'package:diameter/components/forms.dart';
|
import 'package:diameter/components/forms/form_wrapper.dart';
|
||||||
import 'package:diameter/models/settings.dart';
|
import 'package:diameter/models/settings.dart';
|
||||||
import 'package:diameter/navigation.dart';
|
import 'package:diameter/navigation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
@ -38,6 +38,14 @@ class _MealCategoryDetailScreenState extends State<MealCategoryDetailScreen> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_scrollController.dispose();
|
||||||
|
_valueController.dispose();
|
||||||
|
_notesController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
void reload({String? message}) {
|
void reload({String? message}) {
|
||||||
if (widget.id != 0) {
|
if (widget.id != 0) {
|
||||||
setState(() {
|
setState(() {
|
||||||
@ -80,7 +88,7 @@ class _MealCategoryDetailScreenState extends State<MealCategoryDetailScreen> {
|
|||||||
(!_isNew &&
|
(!_isNew &&
|
||||||
(_mealCategory!.value != _valueController.text ||
|
(_mealCategory!.value != _valueController.text ||
|
||||||
(_mealCategory!.notes ?? '') != _notesController.text))) {
|
(_mealCategory!.notes ?? '') != _notesController.text))) {
|
||||||
Dialogs.showCancelConfirmationDialog(
|
DialogUtils.showCancelConfirmationDialog(
|
||||||
context: context,
|
context: context,
|
||||||
isNew: _isNew,
|
isNew: _isNew,
|
||||||
onSave: handleSaveAction,
|
onSave: handleSaveAction,
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import 'package:diameter/components/dialogs.dart';
|
import 'package:diameter/utils/dialog_utils.dart';
|
||||||
import 'package:diameter/models/settings.dart';
|
import 'package:diameter/models/settings.dart';
|
||||||
import 'package:diameter/navigation.dart';
|
import 'package:diameter/navigation.dart';
|
||||||
import 'package:diameter/screens/meal/meal_category_detail.dart';
|
import 'package:diameter/screens/meal/meal_category_detail.dart';
|
||||||
@ -25,6 +25,12 @@ class _MealCategoryListScreenState extends State<MealCategoryListScreen> {
|
|||||||
reload();
|
reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_scrollController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
void reload({String? message}) {
|
void reload({String? message}) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_mealCategories = MealCategory.getAll();
|
_mealCategories = MealCategory.getAll();
|
||||||
@ -49,7 +55,7 @@ class _MealCategoryListScreenState extends State<MealCategoryListScreen> {
|
|||||||
|
|
||||||
void handleDeleteAction(MealCategory mealCategory) async {
|
void handleDeleteAction(MealCategory mealCategory) async {
|
||||||
if (Settings.get().showConfirmationDialogOnDelete) {
|
if (Settings.get().showConfirmationDialogOnDelete) {
|
||||||
Dialogs.showConfirmationDialog(
|
DialogUtils.showConfirmationDialog(
|
||||||
context: context,
|
context: context,
|
||||||
onConfirm: () => onDelete(mealCategory),
|
onConfirm: () => onDelete(mealCategory),
|
||||||
message: 'Are you sure you want to delete this Meal Category?',
|
message: 'Are you sure you want to delete this Meal Category?',
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import 'package:diameter/components/detail.dart';
|
import 'package:diameter/components/detail.dart';
|
||||||
import 'package:diameter/components/dialogs.dart';
|
import 'package:diameter/utils/dialog_utils.dart';
|
||||||
import 'package:diameter/components/dropdown.dart';
|
import 'package:diameter/components/forms/auto_complete_dropdown_button.dart';
|
||||||
import 'package:diameter/components/forms.dart';
|
import 'package:diameter/components/forms/form_wrapper.dart';
|
||||||
import 'package:diameter/models/accuracy.dart';
|
import 'package:diameter/models/accuracy.dart';
|
||||||
import 'package:diameter/models/meal.dart';
|
import 'package:diameter/models/meal.dart';
|
||||||
import 'package:diameter/models/meal_category.dart';
|
import 'package:diameter/models/meal_category.dart';
|
||||||
@ -101,6 +101,23 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_scrollController.dispose();
|
||||||
|
_valueController.dispose();
|
||||||
|
_carbsRatioController.dispose();
|
||||||
|
_portionSizeController.dispose();
|
||||||
|
_carbsPerPortionController.dispose();
|
||||||
|
_delayedBolusDurationController.dispose();
|
||||||
|
_notesController.dispose();
|
||||||
|
_mealSourceController.dispose();
|
||||||
|
_mealCategoryController.dispose();
|
||||||
|
_mealPortionTypeController.dispose();
|
||||||
|
_portionSizeAccuracyController.dispose();
|
||||||
|
_carbsRatioAccuracyController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
void reload({String? message}) {
|
void reload({String? message}) {
|
||||||
if (widget.id != 0) {
|
if (widget.id != 0) {
|
||||||
setState(() {
|
setState(() {
|
||||||
@ -215,7 +232,7 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
|
|||||||
_meal!.delayedBolusDuration ||
|
_meal!.delayedBolusDuration ||
|
||||||
_delayedBolusPercentage != _meal!.delayedBolusPercentage ||
|
_delayedBolusPercentage != _meal!.delayedBolusPercentage ||
|
||||||
_notesController.text != (_meal!.notes ?? ''))))) {
|
_notesController.text != (_meal!.notes ?? ''))))) {
|
||||||
Dialogs.showCancelConfirmationDialog(
|
DialogUtils.showCancelConfirmationDialog(
|
||||||
context: context,
|
context: context,
|
||||||
isNew: _isNew,
|
isNew: _isNew,
|
||||||
onSave: handleSaveAction,
|
onSave: handleSaveAction,
|
||||||
@ -488,11 +505,12 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
|
|||||||
child: Row(
|
child: Row(
|
||||||
mainAxisSize: MainAxisSize.max,
|
mainAxisSize: MainAxisSize.max,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Expanded(
|
||||||
|
child: Text(
|
||||||
'ADDITIONAL FIELDS',
|
'ADDITIONAL FIELDS',
|
||||||
style: Theme.of(context).textTheme.subtitle2,
|
style: Theme.of(context).textTheme.subtitle2,
|
||||||
),
|
),
|
||||||
const Spacer(),
|
),
|
||||||
Icon(_isExpanded
|
Icon(_isExpanded
|
||||||
? Icons.expand_less
|
? Icons.expand_less
|
||||||
: Icons.expand_more),
|
: Icons.expand_more),
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import 'package:diameter/components/dialogs.dart';
|
import 'package:diameter/utils/dialog_utils.dart';
|
||||||
import 'package:diameter/models/meal.dart';
|
import 'package:diameter/models/meal.dart';
|
||||||
import 'package:diameter/models/settings.dart';
|
import 'package:diameter/models/settings.dart';
|
||||||
import 'package:diameter/navigation.dart';
|
import 'package:diameter/navigation.dart';
|
||||||
@ -25,6 +25,12 @@ class _MealListScreenState extends State<MealListScreen> {
|
|||||||
reload();
|
reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_scrollController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
void reload({String? message}) {
|
void reload({String? message}) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_meals = Meal.getAll();
|
_meals = Meal.getAll();
|
||||||
@ -49,7 +55,7 @@ class _MealListScreenState extends State<MealListScreen> {
|
|||||||
|
|
||||||
void handleDeleteAction(Meal meal) async {
|
void handleDeleteAction(Meal meal) async {
|
||||||
if (Settings.get().showConfirmationDialogOnDelete) {
|
if (Settings.get().showConfirmationDialogOnDelete) {
|
||||||
Dialogs.showConfirmationDialog(
|
DialogUtils.showConfirmationDialog(
|
||||||
context: context,
|
context: context,
|
||||||
onConfirm: () => onDelete(meal),
|
onConfirm: () => onDelete(meal),
|
||||||
message: 'Are you sure you want to delete this Meal?',
|
message: 'Are you sure you want to delete this Meal?',
|
||||||
@ -97,13 +103,12 @@ class _MealListScreenState extends State<MealListScreen> {
|
|||||||
),
|
),
|
||||||
subtitle: Padding(
|
subtitle: Padding(
|
||||||
padding: const EdgeInsets.symmetric(vertical: 10.0),
|
padding: const EdgeInsets.symmetric(vertical: 10.0),
|
||||||
child: Row(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
Column(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
Text(meal.mealSource.target?.value ?? ''),
|
Expanded(
|
||||||
Text(meal.notes ?? ''),
|
child: Text(meal.mealSource.target?.value ?? ''),
|
||||||
],
|
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Column(
|
child: Column(
|
||||||
@ -123,7 +128,7 @@ class _MealListScreenState extends State<MealListScreen> {
|
|||||||
child: Column(
|
child: Column(
|
||||||
children: (meal.mealPortionType.hasValue)
|
children: (meal.mealPortionType.hasValue)
|
||||||
? [
|
? [
|
||||||
Text(meal.portionSize!.toStringAsPrecision(3)),
|
Text(meal.portionSize?.toStringAsPrecision(3) ?? ''),
|
||||||
Text(
|
Text(
|
||||||
'${Settings.nutritionMeasurementSuffix}$portionType',
|
'${Settings.nutritionMeasurementSuffix}$portionType',
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
@ -135,6 +140,16 @@ class _MealListScreenState extends State<MealListScreen> {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
meal.notes != null && meal.notes!.trim() != '' ? Padding(
|
||||||
|
padding: const EdgeInsets.only(top: 10.0),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Expanded(child: Text(meal.notes ?? '')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
) : Container(),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
trailing: Row(
|
trailing: Row(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import 'package:diameter/components/detail.dart';
|
import 'package:diameter/components/detail.dart';
|
||||||
import 'package:diameter/components/dialogs.dart';
|
import 'package:diameter/utils/dialog_utils.dart';
|
||||||
import 'package:diameter/components/forms.dart';
|
import 'package:diameter/components/forms/form_wrapper.dart';
|
||||||
import 'package:diameter/models/settings.dart';
|
import 'package:diameter/models/settings.dart';
|
||||||
import 'package:diameter/navigation.dart';
|
import 'package:diameter/navigation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
@ -40,6 +40,14 @@ class _MealPortionTypeDetailScreenState
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_scrollController.dispose();
|
||||||
|
_valueController.dispose();
|
||||||
|
_notesController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
void reload({String? message}) {
|
void reload({String? message}) {
|
||||||
if (widget.id != 0) {
|
if (widget.id != 0) {
|
||||||
setState(() {
|
setState(() {
|
||||||
@ -82,7 +90,7 @@ class _MealPortionTypeDetailScreenState
|
|||||||
(_valueController.text != _mealPortionType!.value ||
|
(_valueController.text != _mealPortionType!.value ||
|
||||||
_notesController.text !=
|
_notesController.text !=
|
||||||
(_mealPortionType!.notes ?? ''))))) {
|
(_mealPortionType!.notes ?? ''))))) {
|
||||||
Dialogs.showCancelConfirmationDialog(
|
DialogUtils.showCancelConfirmationDialog(
|
||||||
context: context,
|
context: context,
|
||||||
isNew: _isNew,
|
isNew: _isNew,
|
||||||
onSave: handleSaveAction,
|
onSave: handleSaveAction,
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import 'package:diameter/components/dialogs.dart';
|
import 'package:diameter/utils/dialog_utils.dart';
|
||||||
import 'package:diameter/models/settings.dart';
|
import 'package:diameter/models/settings.dart';
|
||||||
import 'package:diameter/navigation.dart';
|
import 'package:diameter/navigation.dart';
|
||||||
import 'package:diameter/screens/meal/meal_portion_type_detail.dart';
|
import 'package:diameter/screens/meal/meal_portion_type_detail.dart';
|
||||||
@ -26,6 +26,12 @@ class _MealPortionTypeListScreenState extends State<MealPortionTypeListScreen> {
|
|||||||
reload();
|
reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_scrollController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
void reload({String? message}) {
|
void reload({String? message}) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_mealPortionTypes = MealPortionType.getAll();
|
_mealPortionTypes = MealPortionType.getAll();
|
||||||
@ -50,7 +56,7 @@ class _MealPortionTypeListScreenState extends State<MealPortionTypeListScreen> {
|
|||||||
|
|
||||||
void handleDeleteAction(MealPortionType mealPortionType) async {
|
void handleDeleteAction(MealPortionType mealPortionType) async {
|
||||||
if (Settings.get().showConfirmationDialogOnDelete) {
|
if (Settings.get().showConfirmationDialogOnDelete) {
|
||||||
Dialogs.showConfirmationDialog(
|
DialogUtils.showConfirmationDialog(
|
||||||
context: context,
|
context: context,
|
||||||
onConfirm: () => onDelete(mealPortionType),
|
onConfirm: () => onDelete(mealPortionType),
|
||||||
message: 'Are you sure you want to delete this Meal Portion Type?',
|
message: 'Are you sure you want to delete this Meal Portion Type?',
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import 'package:diameter/components/detail.dart';
|
import 'package:diameter/components/detail.dart';
|
||||||
import 'package:diameter/components/dialogs.dart';
|
import 'package:diameter/utils/dialog_utils.dart';
|
||||||
import 'package:diameter/components/dropdown.dart';
|
import 'package:diameter/components/forms/auto_complete_dropdown_button.dart';
|
||||||
import 'package:diameter/components/forms.dart';
|
import 'package:diameter/components/forms/form_wrapper.dart';
|
||||||
import 'package:diameter/models/accuracy.dart';
|
import 'package:diameter/models/accuracy.dart';
|
||||||
import 'package:diameter/models/meal_category.dart';
|
import 'package:diameter/models/meal_category.dart';
|
||||||
import 'package:diameter/models/meal_portion_type.dart';
|
import 'package:diameter/models/meal_portion_type.dart';
|
||||||
@ -77,6 +77,18 @@ class _MealSourceDetailScreenState extends State<MealSourceDetailScreen> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_scrollController.dispose();
|
||||||
|
_valueController.dispose();
|
||||||
|
_notesController.dispose();
|
||||||
|
_defaultCarbsRatioAccuracyController.dispose();
|
||||||
|
_defaultPortionSizeAccuracyController.dispose();
|
||||||
|
_defaultMealCategoryController.dispose();
|
||||||
|
_defaultMealPortionTypeController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
void reload({String? message}) {
|
void reload({String? message}) {
|
||||||
if (widget.id != 0) {
|
if (widget.id != 0) {
|
||||||
setState(() {
|
setState(() {
|
||||||
@ -132,7 +144,7 @@ class _MealSourceDetailScreenState extends State<MealSourceDetailScreen> {
|
|||||||
_defaultMealPortionType !=
|
_defaultMealPortionType !=
|
||||||
_mealSource!.defaultMealPortionType.target ||
|
_mealSource!.defaultMealPortionType.target ||
|
||||||
_notesController.text != (_mealSource!.notes ?? ''))))) {
|
_notesController.text != (_mealSource!.notes ?? ''))))) {
|
||||||
Dialogs.showCancelConfirmationDialog(
|
DialogUtils.showCancelConfirmationDialog(
|
||||||
context: context,
|
context: context,
|
||||||
isNew: _isNew,
|
isNew: _isNew,
|
||||||
onSave: handleSaveAction,
|
onSave: handleSaveAction,
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import 'package:diameter/components/dialogs.dart';
|
import 'package:diameter/utils/dialog_utils.dart';
|
||||||
import 'package:diameter/models/meal_source.dart';
|
import 'package:diameter/models/meal_source.dart';
|
||||||
import 'package:diameter/models/settings.dart';
|
import 'package:diameter/models/settings.dart';
|
||||||
import 'package:diameter/navigation.dart';
|
import 'package:diameter/navigation.dart';
|
||||||
@ -25,6 +25,12 @@ class _MealSourceListScreenState extends State<MealSourceListScreen> {
|
|||||||
reload();
|
reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_scrollController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
void reload({String? message}) {
|
void reload({String? message}) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_mealSources = MealSource.getAll();
|
_mealSources = MealSource.getAll();
|
||||||
@ -49,7 +55,7 @@ class _MealSourceListScreenState extends State<MealSourceListScreen> {
|
|||||||
|
|
||||||
void handleDeleteAction(MealSource mealSource) async {
|
void handleDeleteAction(MealSource mealSource) async {
|
||||||
if (Settings.get().showConfirmationDialogOnDelete) {
|
if (Settings.get().showConfirmationDialogOnDelete) {
|
||||||
Dialogs.showConfirmationDialog(
|
DialogUtils.showConfirmationDialog(
|
||||||
context: context,
|
context: context,
|
||||||
onConfirm: () => onDelete(mealSource),
|
onConfirm: () => onDelete(mealSource),
|
||||||
message: 'Are you sure you want to delete this Meal Source?',
|
message: 'Are you sure you want to delete this Meal Source?',
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import 'package:diameter/components/detail.dart';
|
import 'package:diameter/components/detail.dart';
|
||||||
import 'package:diameter/components/dialogs.dart';
|
import 'package:diameter/utils/dialog_utils.dart';
|
||||||
import 'package:diameter/components/dropdown.dart';
|
import 'package:diameter/components/forms/auto_complete_dropdown_button.dart';
|
||||||
import 'package:diameter/components/forms.dart';
|
import 'package:diameter/components/forms/form_wrapper.dart';
|
||||||
import 'package:diameter/models/ingredient.dart';
|
import 'package:diameter/models/ingredient.dart';
|
||||||
import 'package:diameter/models/meal.dart';
|
import 'package:diameter/models/meal.dart';
|
||||||
import 'package:diameter/models/recipe.dart';
|
import 'package:diameter/models/recipe.dart';
|
||||||
@ -31,11 +31,11 @@ class _RecipeDetailScreenState extends State<RecipeDetailScreen> {
|
|||||||
final ScrollController _scrollController = ScrollController();
|
final ScrollController _scrollController = ScrollController();
|
||||||
|
|
||||||
final _nameController = TextEditingController(text: '');
|
final _nameController = TextEditingController(text: '');
|
||||||
final _servingsController = TextEditingController(text: '');
|
|
||||||
final _notesController = TextEditingController(text: '');
|
final _notesController = TextEditingController(text: '');
|
||||||
|
|
||||||
|
double _servings = 1;
|
||||||
|
|
||||||
final List<TextEditingController> _ingredientControllers = [];
|
final List<TextEditingController> _ingredientControllers = [];
|
||||||
final List<TextEditingController> _ingredientAmountControllers = [];
|
|
||||||
|
|
||||||
List<Meal> _meals = [];
|
List<Meal> _meals = [];
|
||||||
|
|
||||||
@ -49,15 +49,13 @@ class _RecipeDetailScreenState extends State<RecipeDetailScreen> {
|
|||||||
|
|
||||||
if (_recipe != null) {
|
if (_recipe != null) {
|
||||||
_nameController.text = _recipe!.name;
|
_nameController.text = _recipe!.name;
|
||||||
_servingsController.text = (_recipe!.servings ?? '').toString();
|
_servings = _recipe!.servings ?? 1;
|
||||||
_notesController.text = _recipe!.notes ?? '';
|
_notesController.text = _recipe!.notes ?? '';
|
||||||
|
|
||||||
if (_ingredients.isNotEmpty) {
|
if (_ingredients.isNotEmpty) {
|
||||||
for (Ingredient ingredient in _ingredients) {
|
for (Ingredient ingredient in _ingredients) {
|
||||||
_ingredientControllers.add(
|
_ingredientControllers.add(
|
||||||
TextEditingController(text: ingredient.ingredient.target?.value));
|
TextEditingController(text: ingredient.ingredient.target?.value));
|
||||||
_ingredientAmountControllers
|
|
||||||
.add(TextEditingController(text: ingredient.amount.toString()));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -91,7 +89,6 @@ class _RecipeDetailScreenState extends State<RecipeDetailScreen> {
|
|||||||
newIngredient.recipe.target = _recipe;
|
newIngredient.recipe.target = _recipe;
|
||||||
_ingredients.add(newIngredient);
|
_ingredients.add(newIngredient);
|
||||||
_ingredientControllers.add(TextEditingController(text: ''));
|
_ingredientControllers.add(TextEditingController(text: ''));
|
||||||
_ingredientAmountControllers.add(TextEditingController(text: ''));
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -103,7 +100,7 @@ class _RecipeDetailScreenState extends State<RecipeDetailScreen> {
|
|||||||
Recipe recipe = Recipe(
|
Recipe recipe = Recipe(
|
||||||
id: widget.id,
|
id: widget.id,
|
||||||
name: _nameController.text,
|
name: _nameController.text,
|
||||||
servings: double.tryParse(_servingsController.text),
|
servings: _servings,
|
||||||
notes: _notesController.text,
|
notes: _notesController.text,
|
||||||
);
|
);
|
||||||
Recipe.put(recipe);
|
Recipe.put(recipe);
|
||||||
@ -145,14 +142,13 @@ class _RecipeDetailScreenState extends State<RecipeDetailScreen> {
|
|||||||
if (Settings.get().showConfirmationDialogOnCancel &&
|
if (Settings.get().showConfirmationDialogOnCancel &&
|
||||||
((_isNew &&
|
((_isNew &&
|
||||||
(_nameController.text != '' ||
|
(_nameController.text != '' ||
|
||||||
_servingsController.text != '' ||
|
_servings != 1 ||
|
||||||
_notesController.text != '')) ||
|
_notesController.text != '')) ||
|
||||||
(!_isNew &&
|
(!_isNew &&
|
||||||
(_nameController.text != _recipe!.name ||
|
(_nameController.text != _recipe!.name ||
|
||||||
_servingsController.text !=
|
_servings != _recipe!.servings ||
|
||||||
(_recipe!.servings ?? '').toString() ||
|
|
||||||
_notesController.text != (_recipe!.notes ?? ''))))) {
|
_notesController.text != (_recipe!.notes ?? ''))))) {
|
||||||
Dialogs.showCancelConfirmationDialog(
|
DialogUtils.showCancelConfirmationDialog(
|
||||||
context: context,
|
context: context,
|
||||||
isNew: _isNew,
|
isNew: _isNew,
|
||||||
onSave: handleSaveAction,
|
onSave: handleSaveAction,
|
||||||
@ -190,19 +186,19 @@ class _RecipeDetailScreenState extends State<RecipeDetailScreen> {
|
|||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
NumberFormField(
|
// NumberFormField(
|
||||||
controller: _servingsController,
|
// value: _servings,
|
||||||
label: 'Servings',
|
// label: 'Servings',
|
||||||
suffix: ' portions',
|
// suffix: ' portions',
|
||||||
min: 0,
|
// min: 0,
|
||||||
onChanged: (value) {
|
// onChanged: (value) {
|
||||||
if ((value ?? 0) >= 0) {
|
// if (value != null && value >= 0) {
|
||||||
setState(() {
|
// setState(() {
|
||||||
_servingsController.text = (value ?? 0).toString();
|
// _servings = value.toDouble();
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
},
|
// },
|
||||||
),
|
// ),
|
||||||
TextFormField(
|
TextFormField(
|
||||||
keyboardType: TextInputType.multiline,
|
keyboardType: TextInputType.multiline,
|
||||||
controller: _notesController,
|
controller: _notesController,
|
||||||
@ -285,25 +281,23 @@ class _RecipeDetailScreenState extends State<RecipeDetailScreen> {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
Padding(
|
// Padding(
|
||||||
padding: const EdgeInsets.only(top: 10.0),
|
// padding: const EdgeInsets.only(top: 10.0),
|
||||||
child: NumberFormField(
|
// child: NumberFormField(
|
||||||
controller:
|
// controller:
|
||||||
_ingredientAmountControllers[index],
|
// _ingredients[index].amount,
|
||||||
label: 'Amount',
|
// label: 'Amount',
|
||||||
suffix: Settings.nutritionMeasurementSuffix,
|
// suffix: Settings.nutritionMeasurementSuffix,
|
||||||
min: 0,
|
// min: 0,
|
||||||
onChanged: (value) {
|
// onChanged: (value) {
|
||||||
if ((value ?? 0) >= 0) {
|
// if (value != null && value >= 0) {
|
||||||
setState(() {
|
// setState(() {
|
||||||
_ingredients[index].amount = value ?? 0;
|
// _ingredients[index].amount = value.toDouble();
|
||||||
_ingredientAmountControllers[index]
|
// });
|
||||||
.text = (value ?? 0).toString();
|
// }
|
||||||
});
|
// },
|
||||||
}
|
// ),
|
||||||
},
|
// ),
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import 'package:diameter/components/dialogs.dart';
|
import 'package:diameter/utils/dialog_utils.dart';
|
||||||
import 'package:diameter/models/ingredient.dart';
|
import 'package:diameter/models/ingredient.dart';
|
||||||
import 'package:diameter/models/recipe.dart';
|
import 'package:diameter/models/recipe.dart';
|
||||||
import 'package:diameter/models/settings.dart';
|
import 'package:diameter/models/settings.dart';
|
||||||
@ -50,7 +50,7 @@ class _RecipeListScreenState extends State<RecipeListScreen> {
|
|||||||
|
|
||||||
void handleDeleteAction(Recipe recipe) async {
|
void handleDeleteAction(Recipe recipe) async {
|
||||||
if (Settings.get().showConfirmationDialogOnDelete) {
|
if (Settings.get().showConfirmationDialogOnDelete) {
|
||||||
Dialogs.showConfirmationDialog(
|
DialogUtils.showConfirmationDialog(
|
||||||
context: context,
|
context: context,
|
||||||
onConfirm: () => onDelete(recipe),
|
onConfirm: () => onDelete(recipe),
|
||||||
message: 'Are you sure you want to delete this Recipe?',
|
message: 'Are you sure you want to delete this Recipe?',
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
import 'package:diameter/components/dialogs.dart';
|
import 'package:diameter/components/forms/boolean_form_field.dart';
|
||||||
import 'package:diameter/components/dropdown.dart';
|
import 'package:diameter/components/forms/number_form_field.dart';
|
||||||
import 'package:diameter/components/forms.dart';
|
import 'package:diameter/utils/dialog_utils.dart';
|
||||||
|
import 'package:diameter/components/forms/auto_complete_dropdown_button.dart';
|
||||||
import 'package:diameter/models/settings.dart';
|
import 'package:diameter/models/settings.dart';
|
||||||
import 'package:diameter/navigation.dart';
|
import 'package:diameter/navigation.dart';
|
||||||
|
import 'package:diameter/utils/utils.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
|
|
||||||
class SettingsScreen extends StatefulWidget {
|
class SettingsScreen extends StatefulWidget {
|
||||||
static const String routeName = '/settings';
|
static const String routeName = '/settings';
|
||||||
@ -17,29 +20,33 @@ class SettingsScreen extends StatefulWidget {
|
|||||||
class _SettingsScreenState extends State<SettingsScreen> {
|
class _SettingsScreenState extends State<SettingsScreen> {
|
||||||
late Settings _settings;
|
late Settings _settings;
|
||||||
|
|
||||||
final TextEditingController _nutritionMeasurementLabelController = TextEditingController(text: '');
|
final ScrollController _scrollController = ScrollController();
|
||||||
final TextEditingController _glucoseMeasurementLabelController = TextEditingController(text: '');
|
|
||||||
|
bool _measurementsIsExpanded = true;
|
||||||
|
bool _promptsIsExpanded = true;
|
||||||
|
bool _formatIsExpanded = true;
|
||||||
|
|
||||||
|
final _nutritionMeasurementLabelController = TextEditingController(text: '');
|
||||||
|
final _glucoseMeasurementLabelController = TextEditingController(text: '');
|
||||||
|
final _dateFormatController = TextEditingController(text: '');
|
||||||
|
final _longDateFormatController = TextEditingController(text: '');
|
||||||
|
final _timeFormatController = TextEditingController(text: '');
|
||||||
|
final _longTimeFormatController = TextEditingController(text: '');
|
||||||
|
|
||||||
|
final _insulinIncrementsController = TextEditingController(text: '');
|
||||||
|
final _nutritionIncrementsController = TextEditingController(text: '');
|
||||||
|
final _mmolPerLIncrementsController = TextEditingController(text: '');
|
||||||
|
final _targetGlucoseMgPerDlController = TextEditingController(text: '');
|
||||||
|
final _targetGlucoseMmolPerLController = TextEditingController(text: '');
|
||||||
|
|
||||||
late bool _onlyDisplayActiveGlucoseMeasurement;
|
late bool _onlyDisplayActiveGlucoseMeasurement;
|
||||||
late bool _displayBothGlucoseMeasurementsInDetailView;
|
late bool _displayBothGlucoseMeasurementsInDetailView;
|
||||||
late bool _displayBothGlucoseMeasurementsInListView;
|
late bool _displayBothGlucoseMeasurementsInListView;
|
||||||
|
|
||||||
// late String _dateFormat;
|
|
||||||
// late String? _longDateFormat;
|
|
||||||
// late String _timeFormat;
|
|
||||||
// late String? _longTimeFormat;
|
|
||||||
|
|
||||||
late bool _showConfirmationDialogOnCancel;
|
late bool _showConfirmationDialogOnCancel;
|
||||||
late bool _showConfirmationDialogOnDelete;
|
late bool _showConfirmationDialogOnDelete;
|
||||||
late bool _showConfirmationDialogOnStopEvent;
|
late bool _showConfirmationDialogOnStopEvent;
|
||||||
|
|
||||||
// late int _lowGlucoseMgPerDl;
|
|
||||||
// late int _moderateGlucoseMgPerDl;
|
|
||||||
// late int _highGlucoseMgPerDl;
|
|
||||||
// late double _lowGlucoseMmolPerL;
|
|
||||||
// late double _moderateGlucoseMmolPerL;
|
|
||||||
// late double _highGlucoseMmolPerDl;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
@ -48,27 +55,50 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||||||
nutritionMeasurementLabels[_settings.nutritionMeasurementIndex];
|
nutritionMeasurementLabels[_settings.nutritionMeasurementIndex];
|
||||||
_glucoseMeasurementLabelController.text =
|
_glucoseMeasurementLabelController.text =
|
||||||
glucoseMeasurementLabels[_settings.glucoseMeasurementIndex];
|
glucoseMeasurementLabels[_settings.glucoseMeasurementIndex];
|
||||||
_onlyDisplayActiveGlucoseMeasurement = _settings.glucoseDisplayModeIndex == GlucoseDisplayMode.activeOnly.index;
|
_insulinIncrementsController.text = _settings.insulinIncrements.toString();
|
||||||
|
_nutritionIncrementsController.text =
|
||||||
|
_settings.nutritionIncrements.toString();
|
||||||
|
_mmolPerLIncrementsController.text =
|
||||||
|
_settings.mmolPerLIncrements.toString();
|
||||||
|
_targetGlucoseMgPerDlController.text =
|
||||||
|
_settings.targetGlucoseMgPerDl.toInt().toString();
|
||||||
|
_targetGlucoseMmolPerLController.text =
|
||||||
|
_settings.targetGlucoseMmolPerL.toString();
|
||||||
|
_onlyDisplayActiveGlucoseMeasurement = _settings.glucoseDisplayModeIndex ==
|
||||||
|
GlucoseDisplayMode.activeOnly.index;
|
||||||
_displayBothGlucoseMeasurementsInDetailView =
|
_displayBothGlucoseMeasurementsInDetailView =
|
||||||
_settings.glucoseDisplayModeIndex == GlucoseDisplayMode.both.index ||
|
_settings.glucoseDisplayModeIndex == GlucoseDisplayMode.both.index ||
|
||||||
_settings.glucoseDisplayModeIndex == GlucoseDisplayMode.bothForDetail.index;
|
_settings.glucoseDisplayModeIndex ==
|
||||||
|
GlucoseDisplayMode.bothForDetail.index;
|
||||||
_displayBothGlucoseMeasurementsInListView =
|
_displayBothGlucoseMeasurementsInListView =
|
||||||
_settings.glucoseDisplayModeIndex == GlucoseDisplayMode.both.index ||
|
_settings.glucoseDisplayModeIndex == GlucoseDisplayMode.both.index ||
|
||||||
_settings.glucoseDisplayModeIndex == GlucoseDisplayMode.bothForList.index;
|
_settings.glucoseDisplayModeIndex ==
|
||||||
// _dateFormat = _settings.dateFormat;
|
GlucoseDisplayMode.bothForList.index;
|
||||||
// _longDateFormat = _settings.longDateFormat;
|
_dateFormatController.text = _settings.dateFormat;
|
||||||
// _timeFormat = _settings.timeFormat;
|
_longDateFormatController.text = _settings.longDateFormat ?? '';
|
||||||
// _longTimeFormat = _settings.longTimeFormat;
|
_timeFormatController.text = _settings.timeFormat;
|
||||||
|
_longTimeFormatController.text = _settings.longTimeFormat ?? '';
|
||||||
_showConfirmationDialogOnCancel = _settings.showConfirmationDialogOnCancel;
|
_showConfirmationDialogOnCancel = _settings.showConfirmationDialogOnCancel;
|
||||||
_showConfirmationDialogOnDelete = _settings.showConfirmationDialogOnDelete;
|
_showConfirmationDialogOnDelete = _settings.showConfirmationDialogOnDelete;
|
||||||
_showConfirmationDialogOnStopEvent =
|
_showConfirmationDialogOnStopEvent =
|
||||||
_settings.showConfirmationDialogOnStopEvent;
|
_settings.showConfirmationDialogOnStopEvent;
|
||||||
// _lowGlucoseMgPerDl = _settings.lowGlucoseMgPerDl;
|
}
|
||||||
// _moderateGlucoseMgPerDl = _settings.moderateGlucoseMgPerDl;
|
|
||||||
// _highGlucoseMgPerDl = _settings.highGlucoseMgPerDl;
|
@override
|
||||||
// _lowGlucoseMmolPerL = _settings.lowGlucoseMmolPerL;
|
void dispose() {
|
||||||
// _moderateGlucoseMmolPerL = _settings.moderateGlucoseMmolPerL;
|
_scrollController.dispose();
|
||||||
// _highGlucoseMmolPerDl = _settings.highGlucoseMmolPerDl;
|
_nutritionMeasurementLabelController.dispose();
|
||||||
|
_glucoseMeasurementLabelController.dispose();
|
||||||
|
_dateFormatController.dispose();
|
||||||
|
_longDateFormatController.dispose();
|
||||||
|
_timeFormatController.dispose();
|
||||||
|
_longTimeFormatController.dispose();
|
||||||
|
_insulinIncrementsController.dispose();
|
||||||
|
_nutritionIncrementsController.dispose();
|
||||||
|
_mmolPerLIncrementsController.dispose();
|
||||||
|
_targetGlucoseMgPerDlController.dispose();
|
||||||
|
_targetGlucoseMmolPerLController.dispose();
|
||||||
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
void reload({String? message}) {
|
void reload({String? message}) {
|
||||||
@ -92,42 +122,35 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||||||
void saveSettings() {
|
void saveSettings() {
|
||||||
Settings.put(Settings(
|
Settings.put(Settings(
|
||||||
id: _settings.id,
|
id: _settings.id,
|
||||||
nutritionMeasurementIndex:
|
nutritionMeasurementIndex: nutritionMeasurementLabels
|
||||||
nutritionMeasurementLabels.indexOf(_nutritionMeasurementLabelController.text),
|
.indexOf(_nutritionMeasurementLabelController.text),
|
||||||
glucoseMeasurementIndex:
|
glucoseMeasurementIndex: glucoseMeasurementLabels
|
||||||
glucoseMeasurementLabels.indexOf(_glucoseMeasurementLabelController.text),
|
.indexOf(_glucoseMeasurementLabelController.text),
|
||||||
glucoseDisplayModeIndex: _onlyDisplayActiveGlucoseMeasurement
|
glucoseDisplayModeIndex: _onlyDisplayActiveGlucoseMeasurement
|
||||||
? GlucoseDisplayMode.activeOnly.index
|
? GlucoseDisplayMode.activeOnly.index
|
||||||
: _displayBothGlucoseMeasurementsInDetailView && _displayBothGlucoseMeasurementsInListView
|
: _displayBothGlucoseMeasurementsInDetailView &&
|
||||||
|
_displayBothGlucoseMeasurementsInListView
|
||||||
? GlucoseDisplayMode.both.index
|
? GlucoseDisplayMode.both.index
|
||||||
: _displayBothGlucoseMeasurementsInDetailView
|
: _displayBothGlucoseMeasurementsInDetailView
|
||||||
? GlucoseDisplayMode.bothForDetail.index
|
? GlucoseDisplayMode.bothForDetail.index
|
||||||
: GlucoseDisplayMode.bothForList.index,
|
: GlucoseDisplayMode.bothForList.index,
|
||||||
// dateFormat: _dateFormat,
|
targetGlucoseMgPerDl:
|
||||||
// longDateFormat: _longDateFormat,
|
int.tryParse(_targetGlucoseMgPerDlController.text) ?? _settings.targetGlucoseMgPerDl,
|
||||||
// timeFormat: _timeFormat,
|
targetGlucoseMmolPerL:
|
||||||
// longTimeFormat: _longTimeFormat,
|
double.tryParse(_targetGlucoseMmolPerLController.text) ?? _settings.targetGlucoseMmolPerL,
|
||||||
// showConfirmationDialogOnCancel: _showConfirmationDialogOnCancel,
|
insulinIncrements:
|
||||||
// showConfirmationDialogOnDelete: _showConfirmationDialogOnDelete,
|
double.tryParse(_insulinIncrementsController.text) ?? _settings.insulinIncrements,
|
||||||
// showConfirmationDialogOnStopEvent: _showConfirmationDialogOnStopEvent,
|
nutritionIncrements:
|
||||||
// lowGlucoseMgPerDl: _dateFormat: _dateFormat,
|
double.tryParse(_nutritionIncrementsController.text) ?? _settings.nutritionIncrements,
|
||||||
// longDateFormat: _longDateFormat,
|
mmolPerLIncrements:
|
||||||
// timeFormat: _timeFormat,
|
double.tryParse(_mmolPerLIncrementsController.text) ?? _settings.mmolPerLIncrements,
|
||||||
// longTimeFormat: _longTimeFormat,
|
dateFormat: _dateFormatController.text,
|
||||||
|
longDateFormat: _longDateFormatController.text,
|
||||||
|
timeFormat: _timeFormatController.text,
|
||||||
|
longTimeFormat: _longTimeFormatController.text,
|
||||||
showConfirmationDialogOnCancel: _showConfirmationDialogOnCancel,
|
showConfirmationDialogOnCancel: _showConfirmationDialogOnCancel,
|
||||||
showConfirmationDialogOnDelete: _showConfirmationDialogOnDelete,
|
showConfirmationDialogOnDelete: _showConfirmationDialogOnDelete,
|
||||||
showConfirmationDialogOnStopEvent: _showConfirmationDialogOnStopEvent,
|
showConfirmationDialogOnStopEvent: _showConfirmationDialogOnStopEvent,
|
||||||
// lowGlucoseMgPerDl: _lowGlucoseMgPerDl,
|
|
||||||
// moderateGlucoseMgPerDl: _moderateGlucoseMgPerDl,
|
|
||||||
// highGlucoseMgPerDl: _highGlucoseMgPerDl,
|
|
||||||
// lowGlucoseMmolPerL: _lowGlucoseMmolPerL,
|
|
||||||
// moderateGlucoseMmolPerL: _moderateGlucoseMmolPerL,
|
|
||||||
// highGlucoseMmolPerDl: _highGlucoseMmolPerDl,lowGlucoseMgPerDl,
|
|
||||||
// moderateGlucoseMgPerDl: _moderateGlucoseMgPerDl,
|
|
||||||
// highGlucoseMgPerDl: _highGlucoseMgPerDl,
|
|
||||||
// lowGlucoseMmolPerL: _lowGlucoseMmolPerL,
|
|
||||||
// moderateGlucoseMmolPerL: _moderateGlucoseMmolPerL,
|
|
||||||
// highGlucoseMmolPerDl: _highGlucoseMmolPerDl,
|
|
||||||
));
|
));
|
||||||
reload(message: 'Settings updated');
|
reload(message: 'Settings updated');
|
||||||
}
|
}
|
||||||
@ -138,7 +161,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void handleResetAction() async {
|
void handleResetAction() async {
|
||||||
Dialogs.showConfirmationDialog(
|
DialogUtils.showConfirmationDialog(
|
||||||
context: context,
|
context: context,
|
||||||
onConfirm: onReset,
|
onConfirm: onReset,
|
||||||
message: 'Are you sure you want to reset all settings?',
|
message: 'Are you sure you want to reset all settings?',
|
||||||
@ -153,31 +176,43 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||||||
),
|
),
|
||||||
drawer: const Navigation(currentLocation: SettingsScreen.routeName),
|
drawer: const Navigation(currentLocation: SettingsScreen.routeName),
|
||||||
body: SingleChildScrollView(
|
body: SingleChildScrollView(
|
||||||
|
controller: _scrollController,
|
||||||
padding: const EdgeInsets.all(10.0),
|
padding: const EdgeInsets.all(10.0),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(bottom: 10.0),
|
padding: const EdgeInsets.only(bottom: 10.0),
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: () => setState(() {
|
||||||
|
_measurementsIsExpanded = !_measurementsIsExpanded;
|
||||||
|
}),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Expanded(
|
||||||
|
child: Text(
|
||||||
'MEASUREMENTS',
|
'MEASUREMENTS',
|
||||||
style: Theme.of(context).textTheme.subtitle2,
|
style: Theme.of(context).textTheme.subtitle2,
|
||||||
),
|
),
|
||||||
const Spacer(),
|
),
|
||||||
|
Icon(_measurementsIsExpanded
|
||||||
|
? Icons.expand_less
|
||||||
|
: Icons.expand_more),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
Column(
|
||||||
|
children: _measurementsIsExpanded
|
||||||
|
? [
|
||||||
AutoCompleteDropdownButton<String>(
|
AutoCompleteDropdownButton<String>(
|
||||||
controller: _nutritionMeasurementLabelController,
|
controller: _nutritionMeasurementLabelController,
|
||||||
selectedItem: _nutritionMeasurementLabelController.text,
|
selectedItem: _nutritionMeasurementLabelController.text,
|
||||||
label: 'Preferred Nutrition Measurement',
|
label: 'Preferred Nutrition Measurement',
|
||||||
items: nutritionMeasurementLabels,
|
items: nutritionMeasurementLabels,
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
setState(() {
|
_nutritionMeasurementLabelController.text =
|
||||||
_nutritionMeasurementLabelController.text = value ?? '';
|
value ?? '';
|
||||||
});
|
|
||||||
saveSettings();
|
saveSettings();
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@ -189,13 +224,87 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||||||
label: 'Preferred Glucose Measurement',
|
label: 'Preferred Glucose Measurement',
|
||||||
items: glucoseMeasurementLabels,
|
items: glucoseMeasurementLabels,
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
setState(() {
|
_glucoseMeasurementLabelController.text =
|
||||||
_glucoseMeasurementLabelController.text = value ?? '';
|
value ?? '';
|
||||||
});
|
|
||||||
saveSettings();
|
saveSettings();
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
Settings.glucoseMeasurement == GlucoseMeasurement.mgPerDl
|
||||||
|
? NumberFormField(
|
||||||
|
label: 'Target glucose',
|
||||||
|
suffix: 'mg/dl',
|
||||||
|
controller: _targetGlucoseMgPerDlController,
|
||||||
|
showSteppers: false,
|
||||||
|
onChanged: (_) async {
|
||||||
|
await Future.delayed(
|
||||||
|
const Duration(seconds: 1));
|
||||||
|
if (Settings.glucoseMeasurement ==
|
||||||
|
GlucoseMeasurement.mgPerDl) {
|
||||||
|
final value = int.tryParse(
|
||||||
|
_targetGlucoseMgPerDlController.text);
|
||||||
|
_targetGlucoseMmolPerLController.text = Utils
|
||||||
|
.toStringMatchingTemplateFractionPrecision(
|
||||||
|
Utils.convertMgPerDlToMmolPerL(value ?? 0),
|
||||||
|
Settings.mmolPerLSteps);
|
||||||
|
await Future.delayed(
|
||||||
|
const Duration(seconds: 1));
|
||||||
|
saveSettings();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
: NumberFormField(
|
||||||
|
label: 'Target glucose',
|
||||||
|
suffix: 'mmol/l',
|
||||||
|
controller: _targetGlucoseMmolPerLController,
|
||||||
|
showSteppers: false,
|
||||||
|
onChanged: (_) async {
|
||||||
|
await Future.delayed(
|
||||||
|
const Duration(seconds: 1));
|
||||||
|
if (Settings.glucoseMeasurement ==
|
||||||
|
GlucoseMeasurement.mmolPerL) {
|
||||||
|
final value = double.tryParse(
|
||||||
|
_targetGlucoseMmolPerLController.text);
|
||||||
|
_targetGlucoseMgPerDlController.text =
|
||||||
|
Utils.convertMmolPerLToMgPerDl(value ?? 0)
|
||||||
|
.toString();
|
||||||
|
saveSettings();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 10.0),
|
||||||
|
child: NumberFormField(
|
||||||
|
controller: _insulinIncrementsController,
|
||||||
|
showSteppers: false,
|
||||||
|
label: 'Insulin increment',
|
||||||
|
onChanged: (value) {
|
||||||
|
_insulinIncrementsController.text =
|
||||||
|
(value ?? 0).toString();
|
||||||
|
saveSettings();
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
NumberFormField(
|
||||||
|
controller: _nutritionIncrementsController,
|
||||||
|
showSteppers: false,
|
||||||
|
label: 'Nutrition increment',
|
||||||
|
onChanged: (value) {
|
||||||
|
_nutritionIncrementsController.text =
|
||||||
|
(value ?? 0).toString();
|
||||||
|
saveSettings();
|
||||||
|
}),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 10.0),
|
||||||
|
child: NumberFormField(
|
||||||
|
controller: _mmolPerLIncrementsController,
|
||||||
|
showSteppers: false,
|
||||||
|
label: 'Mmol/L increment',
|
||||||
|
onChanged: (value) {
|
||||||
|
_mmolPerLIncrementsController.text =
|
||||||
|
(value ?? 0).toString();
|
||||||
|
saveSettings();
|
||||||
|
}),
|
||||||
|
),
|
||||||
BooleanFormField(
|
BooleanFormField(
|
||||||
value: _onlyDisplayActiveGlucoseMeasurement,
|
value: _onlyDisplayActiveGlucoseMeasurement,
|
||||||
label: 'only display active glucose measurement',
|
label: 'only display active glucose measurement',
|
||||||
@ -207,7 +316,8 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||||||
BooleanFormField(
|
BooleanFormField(
|
||||||
value: _displayBothGlucoseMeasurementsInDetailView,
|
value: _displayBothGlucoseMeasurementsInDetailView,
|
||||||
enabled: !_onlyDisplayActiveGlucoseMeasurement,
|
enabled: !_onlyDisplayActiveGlucoseMeasurement,
|
||||||
label: 'display both glucose measurements in detail view',
|
label:
|
||||||
|
'display both glucose measurements in detail view',
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
_displayBothGlucoseMeasurementsInDetailView = value;
|
_displayBothGlucoseMeasurementsInDetailView = value;
|
||||||
saveSettings();
|
saveSettings();
|
||||||
@ -222,22 +332,39 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||||||
saveSettings();
|
saveSettings();
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
]
|
||||||
|
: [],
|
||||||
|
),
|
||||||
const Divider(),
|
const Divider(),
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(bottom: 10.0),
|
padding: const EdgeInsets.only(bottom: 10.0),
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: () => setState(() {
|
||||||
|
_promptsIsExpanded = !_promptsIsExpanded;
|
||||||
|
}),
|
||||||
child: Row(
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.max,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Expanded(
|
||||||
|
child: Text(
|
||||||
'CONFIRMATION PROMPTS',
|
'CONFIRMATION PROMPTS',
|
||||||
style: Theme.of(context).textTheme.subtitle2,
|
style: Theme.of(context).textTheme.subtitle2,
|
||||||
),
|
),
|
||||||
const Spacer(),
|
),
|
||||||
|
Icon(_promptsIsExpanded
|
||||||
|
? Icons.expand_less
|
||||||
|
: Icons.expand_more),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
Column(
|
||||||
|
children: _promptsIsExpanded
|
||||||
|
? [
|
||||||
BooleanFormField(
|
BooleanFormField(
|
||||||
value: _showConfirmationDialogOnCancel,
|
value: _showConfirmationDialogOnCancel,
|
||||||
label: 'on cancelling edit or creation of a record if changes have already been made',
|
label:
|
||||||
|
'on cancelling edit or creation of a record if changes have already been made',
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
_showConfirmationDialogOnCancel = value;
|
_showConfirmationDialogOnCancel = value;
|
||||||
saveSettings();
|
saveSettings();
|
||||||
@ -259,6 +386,190 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||||||
saveSettings();
|
saveSettings();
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
]
|
||||||
|
: [],
|
||||||
|
),
|
||||||
|
const Divider(),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(bottom: 10.0),
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: () => setState(() {
|
||||||
|
_formatIsExpanded = !_formatIsExpanded;
|
||||||
|
}),
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.max,
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
'TIME & DATE FORMAT',
|
||||||
|
style: Theme.of(context).textTheme.subtitle2,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Icon(_formatIsExpanded
|
||||||
|
? Icons.expand_less
|
||||||
|
: Icons.expand_more),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Column(
|
||||||
|
children: _formatIsExpanded
|
||||||
|
? [
|
||||||
|
Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: TextFormField(
|
||||||
|
controller: _dateFormatController,
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
labelText: 'Date Format',
|
||||||
|
),
|
||||||
|
validator: (value) {
|
||||||
|
if (value!.trim().isEmpty) {
|
||||||
|
return 'Empty title';
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.only(
|
||||||
|
left: 5.0, bottom: 10.0),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
const Text('Example', textScaleFactor: 0.75),
|
||||||
|
Text(
|
||||||
|
DateFormat(_dateFormatController.text)
|
||||||
|
.format(DateTime.now()),
|
||||||
|
textScaleFactor: 1.25,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 10.0),
|
||||||
|
child: Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: TextFormField(
|
||||||
|
controller: _longDateFormatController,
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
labelText: 'Long Date Format',
|
||||||
|
),
|
||||||
|
validator: (value) {
|
||||||
|
if (value!.trim().isEmpty) {
|
||||||
|
return 'Empty title';
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.only(
|
||||||
|
left: 5.0, bottom: 10.0),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
const Text('Example',
|
||||||
|
textScaleFactor: 0.75),
|
||||||
|
Text(
|
||||||
|
DateFormat(_longDateFormatController.text)
|
||||||
|
.format(DateTime.now()),
|
||||||
|
textScaleFactor: 1.25,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: TextFormField(
|
||||||
|
controller: _timeFormatController,
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
labelText: 'Time Format',
|
||||||
|
),
|
||||||
|
validator: (value) {
|
||||||
|
if (value!.trim().isEmpty) {
|
||||||
|
return 'Empty title';
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.only(
|
||||||
|
left: 5.0, bottom: 10.0),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
const Text('Example', textScaleFactor: 0.75),
|
||||||
|
Text(
|
||||||
|
DateFormat(_timeFormatController.text)
|
||||||
|
.format(DateTime.now()),
|
||||||
|
textScaleFactor: 1.25,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 10.0),
|
||||||
|
child: Row(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: TextFormField(
|
||||||
|
controller: _longTimeFormatController,
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
labelText: 'Long Time Format',
|
||||||
|
),
|
||||||
|
validator: (value) {
|
||||||
|
if (value!.trim().isEmpty) {
|
||||||
|
return 'Empty title';
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.only(
|
||||||
|
left: 5.0, bottom: 10.0),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
const Text('Example',
|
||||||
|
textScaleFactor: 0.75),
|
||||||
|
Text(
|
||||||
|
DateFormat(_longTimeFormatController.text)
|
||||||
|
.format(DateTime.now()),
|
||||||
|
textScaleFactor: 1.25,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
: [],
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -10,7 +10,8 @@ class DateTimeUtils {
|
|||||||
return fallback;
|
return fallback;
|
||||||
}
|
}
|
||||||
DateTime localDate = date.toLocal();
|
DateTime localDate = date.toLocal();
|
||||||
final DateFormat formatter = DateFormat('${Settings.get().dateFormat} ${Settings.get().timeFormat}');
|
final DateFormat formatter =
|
||||||
|
DateFormat('${Settings.get().dateFormat} ${Settings.get().timeFormat}');
|
||||||
return formatter.format(localDate);
|
return formatter.format(localDate);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -19,7 +20,8 @@ class DateTimeUtils {
|
|||||||
return fallback;
|
return fallback;
|
||||||
}
|
}
|
||||||
DateTime localDate = date.toLocal();
|
DateTime localDate = date.toLocal();
|
||||||
final DateFormat formatter = DateFormat(Settings.get().longDateFormat ?? Settings.get().dateFormat);
|
final DateFormat formatter =
|
||||||
|
DateFormat(Settings.get().longDateFormat ?? Settings.get().dateFormat);
|
||||||
return formatter.format(localDate);
|
return formatter.format(localDate);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -29,8 +31,9 @@ class DateTimeUtils {
|
|||||||
return fallback;
|
return fallback;
|
||||||
}
|
}
|
||||||
DateTime localDate = date.toLocal();
|
DateTime localDate = date.toLocal();
|
||||||
final DateFormat formatter = DateFormat(
|
final DateFormat formatter = DateFormat(longFormat == true
|
||||||
longFormat == true ? Settings.get().longTimeFormat ?? Settings.get().timeFormat : Settings.get().timeFormat);
|
? Settings.get().longTimeFormat ?? Settings.get().timeFormat
|
||||||
|
: Settings.get().timeFormat);
|
||||||
return formatter.format(localDate);
|
return formatter.format(localDate);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -39,8 +42,9 @@ class DateTimeUtils {
|
|||||||
if (time == null) {
|
if (time == null) {
|
||||||
return fallback;
|
return fallback;
|
||||||
}
|
}
|
||||||
final DateFormat formatter = DateFormat(
|
final DateFormat formatter = DateFormat(longFormat == true
|
||||||
longFormat == true ? Settings.get().longTimeFormat ?? Settings.get().timeFormat : Settings.get().timeFormat);
|
? Settings.get().longTimeFormat ?? Settings.get().timeFormat
|
||||||
|
: Settings.get().timeFormat);
|
||||||
return formatter.format(convertTimeOfDayToDateTime(time));
|
return formatter.format(convertTimeOfDayToDateTime(time));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class Dialogs {
|
class DialogUtils {
|
||||||
static void showCancelConfirmationDialog(
|
static void showCancelConfirmationDialog(
|
||||||
{required BuildContext context,
|
{required BuildContext context,
|
||||||
required bool isNew,
|
required bool isNew,
|
@ -1,11 +1,28 @@
|
|||||||
import 'dart:math';
|
import 'dart:math';
|
||||||
|
|
||||||
class Utils {
|
class Utils {
|
||||||
static double roundToDecimalPlaces(double value, int places) {
|
static double roundToDecimalPlaces(double value, int precision) {
|
||||||
double mod = pow(10.0, places).toDouble();
|
double mod = pow(10.0, precision).toDouble();
|
||||||
return ((value * mod).round().toDouble() / mod);
|
return ((value * mod).round().toDouble() / mod);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static double addDoublesWithPrecision(double a, double b, int precision) {
|
||||||
|
double mod = pow(10.0, precision).toDouble();
|
||||||
|
double difference = (a * mod) + (b * mod);
|
||||||
|
return difference.round() / mod;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int getFractionDigitsLength(double value) {
|
||||||
|
final fractionDigits = value.toString().split('.');
|
||||||
|
return fractionDigits[1].length;
|
||||||
|
}
|
||||||
|
|
||||||
|
static String toStringMatchingTemplateFractionPrecision(
|
||||||
|
double value, double template) {
|
||||||
|
final precision = getFractionDigitsLength(template);
|
||||||
|
return value.toStringAsFixed(precision);
|
||||||
|
}
|
||||||
|
|
||||||
static double convertMgPerDlToMmolPerL(int mgPerDl) {
|
static double convertMgPerDlToMmolPerL(int mgPerDl) {
|
||||||
return Utils.roundToDecimalPlaces(mgPerDl * 0.0555, 2);
|
return Utils.roundToDecimalPlaces(mgPerDl * 0.0555, 2);
|
||||||
}
|
}
|
||||||
@ -14,18 +31,21 @@ class Utils {
|
|||||||
return (mmolPerL * 18.018).round();
|
return (mmolPerL * 18.018).round();
|
||||||
}
|
}
|
||||||
|
|
||||||
static double calculateCarbs(
|
static double calculateCarbs(double carbsRatio, double portionSize) {
|
||||||
double carbsRatio, double portionSize) {
|
|
||||||
return Utils.roundToDecimalPlaces(carbsRatio * portionSize / 100, 2);
|
return Utils.roundToDecimalPlaces(carbsRatio * portionSize / 100, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
static double calculateCarbsRatio(
|
static double calculateCarbsRatio(
|
||||||
double carbsPerPortion, double portionSize) {
|
double carbsPerPortion, double portionSize) {
|
||||||
return portionSize > 0 ? Utils.roundToDecimalPlaces(carbsPerPortion * 100 / portionSize, 2) : 0;
|
return portionSize > 0
|
||||||
|
? Utils.roundToDecimalPlaces(carbsPerPortion * 100 / portionSize, 2)
|
||||||
|
: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static double calculatePortionSize(
|
static double calculatePortionSize(
|
||||||
double carbsRatio, double carbsPerPortion) {
|
double carbsRatio, double carbsPerPortion) {
|
||||||
return carbsRatio > 0 ? Utils.roundToDecimalPlaces(carbsPerPortion * 100 / carbsRatio, 2) : 0;
|
return carbsRatio > 0
|
||||||
|
? Utils.roundToDecimalPlaces(carbsPerPortion * 100 / carbsRatio, 2)
|
||||||
|
: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
BIN
objectbox/data.mdb
Normal file
BIN
objectbox/data.mdb
Normal file
Binary file not shown.
BIN
objectbox/lock.mdb
Normal file
BIN
objectbox/lock.mdb
Normal file
Binary file not shown.
273
pubspec.lock
273
pubspec.lock
@ -141,48 +141,6 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.15.0"
|
version: "1.15.0"
|
||||||
connectivity_plus:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: connectivity_plus
|
|
||||||
url: "https://pub.dartlang.org"
|
|
||||||
source: hosted
|
|
||||||
version: "1.4.0"
|
|
||||||
connectivity_plus_linux:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: connectivity_plus_linux
|
|
||||||
url: "https://pub.dartlang.org"
|
|
||||||
source: hosted
|
|
||||||
version: "1.1.1"
|
|
||||||
connectivity_plus_macos:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: connectivity_plus_macos
|
|
||||||
url: "https://pub.dartlang.org"
|
|
||||||
source: hosted
|
|
||||||
version: "1.2.1"
|
|
||||||
connectivity_plus_platform_interface:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: connectivity_plus_platform_interface
|
|
||||||
url: "https://pub.dartlang.org"
|
|
||||||
source: hosted
|
|
||||||
version: "1.1.1"
|
|
||||||
connectivity_plus_web:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: connectivity_plus_web
|
|
||||||
url: "https://pub.dartlang.org"
|
|
||||||
source: hosted
|
|
||||||
version: "1.1.0+1"
|
|
||||||
connectivity_plus_windows:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: connectivity_plus_windows
|
|
||||||
url: "https://pub.dartlang.org"
|
|
||||||
source: hosted
|
|
||||||
version: "1.2.0"
|
|
||||||
convert:
|
convert:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -211,20 +169,6 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.0"
|
version: "2.2.0"
|
||||||
dbus:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: dbus
|
|
||||||
url: "https://pub.dartlang.org"
|
|
||||||
source: hosted
|
|
||||||
version: "0.6.6"
|
|
||||||
dio:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: dio
|
|
||||||
url: "https://pub.dartlang.org"
|
|
||||||
source: hosted
|
|
||||||
version: "4.0.4"
|
|
||||||
fake_async:
|
fake_async:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -277,11 +221,6 @@ packages:
|
|||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
version: "0.0.0"
|
||||||
flutter_web_plugins:
|
|
||||||
dependency: transitive
|
|
||||||
description: flutter
|
|
||||||
source: sdk
|
|
||||||
version: "0.0.0"
|
|
||||||
frontend_server_client:
|
frontend_server_client:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -303,13 +242,6 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.1.0"
|
version: "2.1.0"
|
||||||
http:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: http
|
|
||||||
url: "https://pub.dartlang.org"
|
|
||||||
source: hosted
|
|
||||||
version: "0.13.4"
|
|
||||||
http_multi_server:
|
http_multi_server:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -324,13 +256,6 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.0.0"
|
version: "4.0.0"
|
||||||
idb_shim:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: idb_shim
|
|
||||||
url: "https://pub.dartlang.org"
|
|
||||||
source: hosted
|
|
||||||
version: "2.0.1"
|
|
||||||
intl:
|
intl:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -380,6 +305,13 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.12.11"
|
version: "0.12.11"
|
||||||
|
material_color_utilities:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: material_color_utilities
|
||||||
|
url: "https://pub.dartlang.org"
|
||||||
|
source: hosted
|
||||||
|
version: "0.1.3"
|
||||||
meta:
|
meta:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -394,13 +326,6 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.1"
|
version: "1.0.1"
|
||||||
mime_type:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: mime_type
|
|
||||||
url: "https://pub.dartlang.org"
|
|
||||||
source: hosted
|
|
||||||
version: "1.0.0"
|
|
||||||
nested:
|
nested:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -408,13 +333,6 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.0"
|
version: "1.0.0"
|
||||||
nm:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: nm
|
|
||||||
url: "https://pub.dartlang.org"
|
|
||||||
source: hosted
|
|
||||||
version: "0.4.1"
|
|
||||||
objectbox:
|
objectbox:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
@ -443,62 +361,6 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.2"
|
version: "2.0.2"
|
||||||
package_info_plus:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: package_info_plus
|
|
||||||
url: "https://pub.dartlang.org"
|
|
||||||
source: hosted
|
|
||||||
version: "1.3.0"
|
|
||||||
package_info_plus_linux:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: package_info_plus_linux
|
|
||||||
url: "https://pub.dartlang.org"
|
|
||||||
source: hosted
|
|
||||||
version: "1.0.3"
|
|
||||||
package_info_plus_macos:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: package_info_plus_macos
|
|
||||||
url: "https://pub.dartlang.org"
|
|
||||||
source: hosted
|
|
||||||
version: "1.3.0"
|
|
||||||
package_info_plus_platform_interface:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: package_info_plus_platform_interface
|
|
||||||
url: "https://pub.dartlang.org"
|
|
||||||
source: hosted
|
|
||||||
version: "1.0.2"
|
|
||||||
package_info_plus_web:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: package_info_plus_web
|
|
||||||
url: "https://pub.dartlang.org"
|
|
||||||
source: hosted
|
|
||||||
version: "1.0.4"
|
|
||||||
package_info_plus_windows:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: package_info_plus_windows
|
|
||||||
url: "https://pub.dartlang.org"
|
|
||||||
source: hosted
|
|
||||||
version: "1.0.4"
|
|
||||||
parse_server_sdk:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: parse_server_sdk
|
|
||||||
url: "https://pub.dartlang.org"
|
|
||||||
source: hosted
|
|
||||||
version: "3.1.0"
|
|
||||||
parse_server_sdk_flutter:
|
|
||||||
dependency: "direct main"
|
|
||||||
description:
|
|
||||||
name: parse_server_sdk_flutter
|
|
||||||
url: "https://pub.dartlang.org"
|
|
||||||
source: hosted
|
|
||||||
version: "3.1.0"
|
|
||||||
path:
|
path:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -555,13 +417,6 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.0.4"
|
version: "2.0.4"
|
||||||
petitparser:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: petitparser
|
|
||||||
url: "https://pub.dartlang.org"
|
|
||||||
source: hosted
|
|
||||||
version: "4.4.0"
|
|
||||||
platform:
|
platform:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -611,76 +466,6 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.2.0"
|
version: "1.2.0"
|
||||||
sembast:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: sembast
|
|
||||||
url: "https://pub.dartlang.org"
|
|
||||||
source: hosted
|
|
||||||
version: "3.1.1"
|
|
||||||
sembast_web:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: sembast_web
|
|
||||||
url: "https://pub.dartlang.org"
|
|
||||||
source: hosted
|
|
||||||
version: "2.0.1+1"
|
|
||||||
shared_preferences:
|
|
||||||
dependency: "direct main"
|
|
||||||
description:
|
|
||||||
name: shared_preferences
|
|
||||||
url: "https://pub.dartlang.org"
|
|
||||||
source: hosted
|
|
||||||
version: "2.0.9"
|
|
||||||
shared_preferences_android:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: shared_preferences_android
|
|
||||||
url: "https://pub.dartlang.org"
|
|
||||||
source: hosted
|
|
||||||
version: "2.0.9"
|
|
||||||
shared_preferences_ios:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: shared_preferences_ios
|
|
||||||
url: "https://pub.dartlang.org"
|
|
||||||
source: hosted
|
|
||||||
version: "2.0.8"
|
|
||||||
shared_preferences_linux:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: shared_preferences_linux
|
|
||||||
url: "https://pub.dartlang.org"
|
|
||||||
source: hosted
|
|
||||||
version: "2.0.3"
|
|
||||||
shared_preferences_macos:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: shared_preferences_macos
|
|
||||||
url: "https://pub.dartlang.org"
|
|
||||||
source: hosted
|
|
||||||
version: "2.0.2"
|
|
||||||
shared_preferences_platform_interface:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: shared_preferences_platform_interface
|
|
||||||
url: "https://pub.dartlang.org"
|
|
||||||
source: hosted
|
|
||||||
version: "2.0.0"
|
|
||||||
shared_preferences_web:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: shared_preferences_web
|
|
||||||
url: "https://pub.dartlang.org"
|
|
||||||
source: hosted
|
|
||||||
version: "2.0.2"
|
|
||||||
shared_preferences_windows:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: shared_preferences_windows
|
|
||||||
url: "https://pub.dartlang.org"
|
|
||||||
source: hosted
|
|
||||||
version: "2.0.3"
|
|
||||||
shelf:
|
shelf:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -714,20 +499,6 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.8.1"
|
version: "1.8.1"
|
||||||
sqflite:
|
|
||||||
dependency: "direct main"
|
|
||||||
description:
|
|
||||||
name: sqflite
|
|
||||||
url: "https://pub.dartlang.org"
|
|
||||||
source: hosted
|
|
||||||
version: "2.0.1"
|
|
||||||
sqflite_common:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: sqflite_common
|
|
||||||
url: "https://pub.dartlang.org"
|
|
||||||
source: hosted
|
|
||||||
version: "2.0.1+1"
|
|
||||||
stack_trace:
|
stack_trace:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -756,13 +527,6 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.0"
|
version: "1.1.0"
|
||||||
synchronized:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: synchronized
|
|
||||||
url: "https://pub.dartlang.org"
|
|
||||||
source: hosted
|
|
||||||
version: "3.0.0"
|
|
||||||
term_glyph:
|
term_glyph:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -776,7 +540,7 @@ packages:
|
|||||||
name: test_api
|
name: test_api
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.4.3"
|
version: "0.4.8"
|
||||||
timing:
|
timing:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -791,13 +555,6 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.3.0"
|
version: "1.3.0"
|
||||||
uuid:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: uuid
|
|
||||||
url: "https://pub.dartlang.org"
|
|
||||||
source: hosted
|
|
||||||
version: "3.0.5"
|
|
||||||
vector_math:
|
vector_math:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@ -833,20 +590,6 @@ packages:
|
|||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.2.0"
|
version: "0.2.0"
|
||||||
xml:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: xml
|
|
||||||
url: "https://pub.dartlang.org"
|
|
||||||
source: hosted
|
|
||||||
version: "5.3.1"
|
|
||||||
xxtea:
|
|
||||||
dependency: transitive
|
|
||||||
description:
|
|
||||||
name: xxtea
|
|
||||||
url: "https://pub.dartlang.org"
|
|
||||||
source: hosted
|
|
||||||
version: "2.1.0"
|
|
||||||
yaml:
|
yaml:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -9,14 +9,11 @@ environment:
|
|||||||
sdk: ">=2.12.0 <3.0.0"
|
sdk: ">=2.12.0 <3.0.0"
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
parse_server_sdk_flutter: ^3.1.0
|
|
||||||
flutter:
|
flutter:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
sqflite: ^2.0.0+4
|
|
||||||
path_provider: ^2.0.5
|
path_provider: ^2.0.5
|
||||||
cupertino_icons: ^1.0.2
|
cupertino_icons: ^1.0.2
|
||||||
flex_color_scheme: ^3.0.1
|
flex_color_scheme: ^3.0.1
|
||||||
shared_preferences: ^2.0.8
|
|
||||||
intl: ^0.17.0
|
intl: ^0.17.0
|
||||||
objectbox: ^1.2.0
|
objectbox: ^1.2.0
|
||||||
objectbox_sync_flutter_libs: any
|
objectbox_sync_flutter_libs: any
|
||||||
|
BIN
sync-server
Executable file
BIN
sync-server
Executable file
Binary file not shown.
12
sync-server-config.js
Normal file
12
sync-server-config.js
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"dbDirectory": "objectbox",
|
||||||
|
"dbMaxSize": "100G",
|
||||||
|
"modelFile": "lib/objectbox-model.json",
|
||||||
|
"bind": "ws://192.168.1.184:9999",
|
||||||
|
"browserBind": "http://127.0.0.1:9980",
|
||||||
|
"browserThreads": 4,
|
||||||
|
"certificatePath": "",
|
||||||
|
"auth": {
|
||||||
|
"sharedSecret": "m4Gwehzgv18jZ5gCVUBZl5li3Z0FX2Yb"
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user