various usability improvements for ui
This commit is contained in:
parent
0dfcedff0b
commit
575130aba0
23
TODO
23
TODO
@ -1,31 +1,28 @@
|
||||
BUGFIXES:
|
||||
General/Framework:
|
||||
☐ make sure 'null' isn't shown in text fields
|
||||
Basal/Bolus:
|
||||
☐ "no element" error on creating basal/bolus rates when working from apk
|
||||
|
||||
MAIN TASKS:
|
||||
Layout:
|
||||
☐ make a styleguide (actively decide what components should look like)
|
||||
☐ make components rounder/nicer/closer to new material style @started(21-12-08 02:17)
|
||||
✔ make components rounder/nicer/closer to new material style @done(21-12-10 04:10)
|
||||
General/Framework:
|
||||
✔ make sure 'null' isn't shown in text fields @done(21-12-10 04:23)
|
||||
☐ show indicator and make all fields readonly if user somehow gets to a deleted record detail view
|
||||
☐ clean up controllers (dispose method of each stateful widget)
|
||||
☐ account for deleted/disabled elements in dropdowns
|
||||
☐ check through all detail forms and set required fields/according messages
|
||||
☐ set name properties as unique (and add checks to forms)
|
||||
☐ implement component for durations
|
||||
☐ change placement of delete and floating button because its very easy to accidentally hit delete
|
||||
☐ hide details like accuracies etc when picking meals
|
||||
✔ hide details like accuracies etc when picking meals @done(21-12-10 06:12)
|
||||
Basal/Bolus:
|
||||
☐ add save and close and next buttons on rate creations
|
||||
☐ always calculate other glucose measurement from active one and make other one readonly
|
||||
✔ add save and close and next buttons on rate creations @done(21-12-10 06:12)
|
||||
✔ always calculate other glucose measurement from active one and make other one readonly @done(21-12-10 04:33)
|
||||
Log Entry:
|
||||
☐ add save and close button
|
||||
☐ move on to newly created entry after saving
|
||||
✔ add save and close button @done(21-12-10 06:11)
|
||||
✔ move on to newly created entry after saving @done(21-12-10 06:11)
|
||||
☐ recalculate bolus upon deactivating 'set manually' option
|
||||
☐ account for delayed percentage setting on choosing meals
|
||||
☐ give option to supply quantity
|
||||
☐ give option to pick meal from a different log entry (that doesn't have an associated bolus yet)
|
||||
☐ give option to specify quantity
|
||||
☐ give option to pick meal from a different log entry (that doesn't have an associated bolus yet and within certain time span)
|
||||
Event Types:
|
||||
☐ add colors as indicators for log entries (and later graphs in reports)
|
||||
Settings:
|
||||
|
@ -2,10 +2,22 @@ import 'package:flutter/material.dart';
|
||||
|
||||
class DetailBottomRow extends StatefulWidget {
|
||||
final void Function()? onCancel;
|
||||
final void Function()? onSave;
|
||||
final void Function()? onAction;
|
||||
final void Function()? onMiddleAction;
|
||||
final String actionText;
|
||||
final String middleActionText;
|
||||
final IconData actionIcon;
|
||||
final IconData middleActionIcon;
|
||||
|
||||
const DetailBottomRow(
|
||||
{Key? key, required this.onCancel, required this.onSave})
|
||||
{Key? key,
|
||||
required this.onCancel,
|
||||
required this.onAction,
|
||||
this.onMiddleAction,
|
||||
this.actionText = 'SAVE',
|
||||
this.actionIcon = Icons.save,
|
||||
this.middleActionText = 'SAVE & CLOSE',
|
||||
this.middleActionIcon = Icons.done})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
@ -19,6 +31,7 @@ class _DetailBottomRowState<T> extends State<DetailBottomRow> {
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(10.0),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
ElevatedButton.icon(
|
||||
onPressed: widget.onCancel,
|
||||
@ -28,14 +41,23 @@ class _DetailBottomRowState<T> extends State<DetailBottomRow> {
|
||||
),
|
||||
label: const Text('CANCEL'),
|
||||
),
|
||||
const Spacer(),
|
||||
ElevatedButton.icon(
|
||||
onPressed: widget.onSave,
|
||||
icon: const Icon(
|
||||
Icons.save,
|
||||
widget.onMiddleAction != null
|
||||
? ElevatedButton.icon(
|
||||
onPressed: widget.onMiddleAction,
|
||||
icon: Icon(
|
||||
widget.middleActionIcon,
|
||||
size: 18.0,
|
||||
),
|
||||
label: const Text('SAVE'),
|
||||
label: Text(widget.middleActionText),
|
||||
)
|
||||
: const Spacer(),
|
||||
ElevatedButton.icon(
|
||||
onPressed: widget.onAction,
|
||||
icon: Icon(
|
||||
widget.actionIcon,
|
||||
size: 18.0,
|
||||
),
|
||||
label: Text(widget.actionText),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
@ -183,7 +183,7 @@ class _AccuracyDetailScreenState extends State<AccuracyDetailScreen> {
|
||||
),
|
||||
bottomNavigationBar: DetailBottomRow(
|
||||
onCancel: handleCancelAction,
|
||||
onSave: _isSaving ? null : handleSaveAction,
|
||||
onAction: _isSaving ? null : handleSaveAction,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -32,6 +32,7 @@ class _BasalDetailScreenState extends State<BasalDetailScreen> {
|
||||
Basal? _basal;
|
||||
bool _isNew = true;
|
||||
bool _isSaving = false;
|
||||
bool _isFinalRate = true;
|
||||
|
||||
final GlobalKey<FormState> _basalForm = GlobalKey<FormState>();
|
||||
final ScrollController _scrollController = ScrollController();
|
||||
@ -61,8 +62,8 @@ class _BasalDetailScreenState extends State<BasalDetailScreen> {
|
||||
_unitsController.text = _basal!.units.toString();
|
||||
}
|
||||
|
||||
updateStartTime();
|
||||
updateEndTime();
|
||||
_startTimeController.text = DateTimeUtils.displayTimeOfDay(_startTime);
|
||||
_endTimeController.text = DateTimeUtils.displayTimeOfDay(_endTime);
|
||||
}
|
||||
|
||||
void reload({String? message}) {
|
||||
@ -86,12 +87,24 @@ class _BasalDetailScreenState extends State<BasalDetailScreen> {
|
||||
});
|
||||
}
|
||||
|
||||
void updateStartTime() {
|
||||
void updateStartTime(TimeOfDay? value) {
|
||||
if (value != null) {
|
||||
setState(() {
|
||||
_startTime = value;
|
||||
_startTimeController.text = DateTimeUtils.displayTimeOfDay(_startTime);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void updateEndTime() {
|
||||
void updateEndTime(TimeOfDay? value) {
|
||||
if (value != null) {
|
||||
setState(() {
|
||||
_endTime = value;
|
||||
_endTimeController.text = DateTimeUtils.displayTimeOfDay(_endTime);
|
||||
_isFinalRate = widget.suggestedEndTime == null ||
|
||||
_endTime == widget.suggestedEndTime!;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Future<String?> validateTimePeriod() async {
|
||||
@ -139,7 +152,7 @@ class _BasalDetailScreenState extends State<BasalDetailScreen> {
|
||||
});
|
||||
}
|
||||
|
||||
void handleSaveAction() async {
|
||||
void handleSaveAction({bool next = true}) async {
|
||||
setState(() {
|
||||
_isSaving = true;
|
||||
});
|
||||
@ -154,7 +167,30 @@ class _BasalDetailScreenState extends State<BasalDetailScreen> {
|
||||
);
|
||||
basal.basalProfile.targetId = widget.basalProfileId;
|
||||
Basal.put(basal);
|
||||
Navigator.pop(context, ['${_isNew ? 'New' : ''} Basal Rate saved', basal]);
|
||||
|
||||
if (next) {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) {
|
||||
return BasalDetailScreen(
|
||||
basalProfileId: widget.basalProfileId,
|
||||
suggestedStartTime: _endTime,
|
||||
suggestedEndTime: widget.suggestedEndTime,
|
||||
);
|
||||
},
|
||||
),
|
||||
).then((result) {
|
||||
Navigator.pop(
|
||||
context,
|
||||
['New Basal Rate${result[1] != null ? 's' : ''} saved', basal] +
|
||||
[result[1]],
|
||||
);
|
||||
});
|
||||
} else {
|
||||
Navigator.pop(
|
||||
context, ['${_isNew ? 'New' : ''} Basal Rate saved', basal]);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -173,8 +209,7 @@ class _BasalDetailScreenState extends State<BasalDetailScreen> {
|
||||
_endTime.minute != (widget.suggestedEndTime?.minute ?? 0) ||
|
||||
double.tryParse(_unitsController.text) != null)) ||
|
||||
(!_isNew &&
|
||||
(TimeOfDay.fromDateTime(_basal!.startTime) !=
|
||||
_startTime ||
|
||||
(TimeOfDay.fromDateTime(_basal!.startTime) != _startTime ||
|
||||
TimeOfDay.fromDateTime(_basal!.endTime) != _endTime ||
|
||||
(double.tryParse(_unitsController.text) ?? 0) !=
|
||||
_basal!.units)))) {
|
||||
@ -214,14 +249,7 @@ class _BasalDetailScreenState extends State<BasalDetailScreen> {
|
||||
label: 'Start Time',
|
||||
controller: _startTimeController,
|
||||
time: _startTime,
|
||||
onChanged: (newStartTime) {
|
||||
if (newStartTime != null) {
|
||||
setState(() {
|
||||
_startTime = newStartTime;
|
||||
});
|
||||
updateStartTime();
|
||||
}
|
||||
},
|
||||
onChanged: updateStartTime,
|
||||
),
|
||||
),
|
||||
),
|
||||
@ -232,14 +260,7 @@ class _BasalDetailScreenState extends State<BasalDetailScreen> {
|
||||
label: 'End Time',
|
||||
controller: _endTimeController,
|
||||
time: _endTime,
|
||||
onChanged: (newEndTime) {
|
||||
if (newEndTime != null) {
|
||||
setState(() {
|
||||
_endTime = newEndTime;
|
||||
});
|
||||
updateEndTime();
|
||||
}
|
||||
},
|
||||
onChanged: updateEndTime,
|
||||
),
|
||||
),
|
||||
),
|
||||
@ -268,7 +289,13 @@ class _BasalDetailScreenState extends State<BasalDetailScreen> {
|
||||
),
|
||||
bottomNavigationBar: DetailBottomRow(
|
||||
onCancel: handleCancelAction,
|
||||
onSave: _isSaving ? null : handleSaveAction,
|
||||
onAction:
|
||||
_isSaving ? null : () => handleSaveAction(next: !_isFinalRate),
|
||||
onMiddleAction: _isSaving || _isFinalRate
|
||||
? null
|
||||
: () => handleSaveAction(next: false),
|
||||
actionText: _isFinalRate ? 'SAVE & CLOSE' : 'NEXT',
|
||||
middleActionText: 'SAVE & CLOSE',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -78,12 +78,13 @@ class _BasalProfileDetailScreenState extends State<BasalProfileDetailScreen> {
|
||||
|
||||
detailBottomRow = DetailBottomRow(
|
||||
onCancel: handleCancelAction,
|
||||
onSave: handleSaveAction,
|
||||
onAction: handleSaveAction,
|
||||
onMiddleAction: () => handleSaveAction(close: true),
|
||||
);
|
||||
|
||||
detailBottomRowWhileSaving = DetailBottomRow(
|
||||
onCancel: handleCancelAction,
|
||||
onSave: null,
|
||||
onAction: null,
|
||||
);
|
||||
|
||||
actionButton = null;
|
||||
@ -184,6 +185,10 @@ class _BasalProfileDetailScreenState extends State<BasalProfileDetailScreen> {
|
||||
TimeOfDay? suggestedStartTime;
|
||||
TimeOfDay? suggestedEndTime;
|
||||
|
||||
if (_basalRates.isEmpty) {
|
||||
suggestedStartTime = const TimeOfDay(hour: 0, minute: 0);
|
||||
suggestedEndTime = const TimeOfDay(hour: 0, minute: 0);
|
||||
} else {
|
||||
_basalRates.asMap().forEach((index, basal) {
|
||||
if (suggestedStartTime == null && suggestedEndTime == null) {
|
||||
if (index == 0 &&
|
||||
@ -203,6 +208,7 @@ class _BasalProfileDetailScreenState extends State<BasalProfileDetailScreen> {
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Navigator.push(
|
||||
context,
|
||||
@ -218,7 +224,7 @@ class _BasalProfileDetailScreenState extends State<BasalProfileDetailScreen> {
|
||||
).then((result) => reload(message: result?[0]));
|
||||
}
|
||||
|
||||
void handleSaveAction() async {
|
||||
void handleSaveAction({bool close = false}) async {
|
||||
setState(() {
|
||||
bottomNav = detailBottomRowWhileSaving;
|
||||
});
|
||||
@ -231,7 +237,23 @@ class _BasalProfileDetailScreenState extends State<BasalProfileDetailScreen> {
|
||||
notes: _notesController.text,
|
||||
);
|
||||
BasalProfile.put(basalProfile);
|
||||
Navigator.pop(context, ['${_isNew ? 'New' : ''} Basal Profile saved', basalProfile]);
|
||||
|
||||
if (close) {
|
||||
Navigator.pop(context,
|
||||
['${_isNew ? 'New' : ''} Basal Profile saved', basalProfile]);
|
||||
} else {
|
||||
if (_isNew) {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) =>
|
||||
BasalProfileDetailScreen(id: basalProfile.id),
|
||||
),
|
||||
).then((result) => Navigator.pop(context, result));
|
||||
} else {
|
||||
reload(message: 'Basal Profile saved');
|
||||
}
|
||||
}
|
||||
}
|
||||
setState(() {
|
||||
bottomNav = detailBottomRow;
|
||||
|
@ -33,6 +33,7 @@ class _BolusDetailScreenState extends State<BolusDetailScreen> {
|
||||
Bolus? _bolus;
|
||||
bool _isNew = true;
|
||||
bool _isSaving = false;
|
||||
bool _isFinalRate = true;
|
||||
|
||||
final GlobalKey<FormState> _bolusForm = GlobalKey<FormState>();
|
||||
final ScrollController _scrollController = ScrollController();
|
||||
@ -61,16 +62,16 @@ class _BolusDetailScreenState extends State<BolusDetailScreen> {
|
||||
|
||||
if (_bolus != null) {
|
||||
_startTime = TimeOfDay.fromDateTime(_bolus!.startTime);
|
||||
|
||||
_endTime = TimeOfDay.fromDateTime(_bolus!.endTime);
|
||||
|
||||
_unitsController.text = _bolus!.units.toString();
|
||||
_carbsController.text = _bolus!.carbs.toString();
|
||||
_mgPerDlController.text = _bolus!.mgPerDl.toString();
|
||||
_mmolPerLController.text = _bolus!.mmolPerL.toString();
|
||||
_mgPerDlController.text = (_bolus!.mgPerDl ?? '').toString();
|
||||
_mmolPerLController.text = (_bolus!.mmolPerL ?? '').toString();
|
||||
}
|
||||
|
||||
updateStartTime();
|
||||
updateEndTime();
|
||||
_startTimeController.text = DateTimeUtils.displayTimeOfDay(_startTime);
|
||||
_endTimeController.text = DateTimeUtils.displayTimeOfDay(_endTime);
|
||||
}
|
||||
|
||||
void reload({String? message}) {
|
||||
@ -94,12 +95,24 @@ class _BolusDetailScreenState extends State<BolusDetailScreen> {
|
||||
});
|
||||
}
|
||||
|
||||
void updateStartTime() {
|
||||
void updateStartTime(TimeOfDay? value) {
|
||||
if (value != null) {
|
||||
setState(() {
|
||||
_startTime = value;
|
||||
_startTimeController.text = DateTimeUtils.displayTimeOfDay(_startTime);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void updateEndTime() {
|
||||
void updateEndTime(TimeOfDay? value) {
|
||||
if (value != null) {
|
||||
setState(() {
|
||||
_endTime = value;
|
||||
_endTimeController.text = DateTimeUtils.displayTimeOfDay(_endTime);
|
||||
_isFinalRate = widget.suggestedEndTime == null ||
|
||||
_endTime == widget.suggestedEndTime!;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Future<String?> validateTimePeriod() async {
|
||||
@ -148,7 +161,7 @@ class _BolusDetailScreenState extends State<BolusDetailScreen> {
|
||||
});
|
||||
}
|
||||
|
||||
void handleSaveAction() async {
|
||||
void handleSaveAction({bool next = true}) async {
|
||||
setState(() {
|
||||
_isSaving = true;
|
||||
});
|
||||
@ -167,7 +180,29 @@ class _BolusDetailScreenState extends State<BolusDetailScreen> {
|
||||
);
|
||||
bolus.bolusProfile.targetId = widget.bolusProfileId;
|
||||
Bolus.put(bolus);
|
||||
Navigator.pop(context, ['${_isNew ? 'New' : ''} Bolus Rate saved', bolus]);
|
||||
|
||||
if (next) {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) {
|
||||
return BolusDetailScreen(
|
||||
bolusProfileId: widget.bolusProfileId,
|
||||
suggestedStartTime: _endTime,
|
||||
suggestedEndTime: widget.suggestedEndTime,
|
||||
);
|
||||
},
|
||||
),
|
||||
).then((result) {
|
||||
Navigator.pop(
|
||||
context,
|
||||
['New Bolus Rate${result[1] != null ? 's' : ''} saved', bolus] + [result[1]],
|
||||
);
|
||||
});
|
||||
} else {
|
||||
Navigator.pop(
|
||||
context, ['${_isNew ? 'New' : ''} Bolus Rate saved', bolus]);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -263,14 +298,7 @@ class _BolusDetailScreenState extends State<BolusDetailScreen> {
|
||||
label: 'Start Time',
|
||||
controller: _startTimeController,
|
||||
time: _startTime,
|
||||
onChanged: (newStartTime) {
|
||||
if (newStartTime != null) {
|
||||
setState(() {
|
||||
_startTime = newStartTime;
|
||||
});
|
||||
updateStartTime();
|
||||
}
|
||||
},
|
||||
onChanged: updateStartTime,
|
||||
),
|
||||
),
|
||||
),
|
||||
@ -281,14 +309,7 @@ class _BolusDetailScreenState extends State<BolusDetailScreen> {
|
||||
label: 'End Time',
|
||||
controller: _endTimeController,
|
||||
time: _endTime,
|
||||
onChanged: (newEndTime) {
|
||||
if (newEndTime != null) {
|
||||
setState(() {
|
||||
_endTime = newEndTime;
|
||||
});
|
||||
updateEndTime();
|
||||
}
|
||||
},
|
||||
onChanged: updateEndTime,
|
||||
),
|
||||
),
|
||||
),
|
||||
@ -326,8 +347,10 @@ class _BolusDetailScreenState extends State<BolusDetailScreen> {
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
Settings.glucoseMeasurement == GlucoseMeasurement.mgPerDl ||
|
||||
Settings.glucoseDisplayMode == GlucoseDisplayMode.both ||
|
||||
Settings.glucoseMeasurement ==
|
||||
GlucoseMeasurement.mgPerDl ||
|
||||
Settings.glucoseDisplayMode ==
|
||||
GlucoseDisplayMode.both ||
|
||||
Settings.glucoseDisplayMode ==
|
||||
GlucoseDisplayMode.bothForDetail
|
||||
? Expanded(
|
||||
@ -336,9 +359,12 @@ class _BolusDetailScreenState extends State<BolusDetailScreen> {
|
||||
labelText: 'per mg/dl',
|
||||
suffixText: 'mg/dl',
|
||||
),
|
||||
readOnly: Settings.glucoseMeasurement ==
|
||||
GlucoseMeasurement.mmolPerL,
|
||||
controller: _mgPerDlController,
|
||||
onChanged: (_) async {
|
||||
await Future.delayed(const Duration(seconds: 1));
|
||||
await Future.delayed(
|
||||
const Duration(seconds: 1));
|
||||
convertBetweenMgPerDlAndMmolPerL(
|
||||
calculateFrom:
|
||||
GlucoseMeasurement.mgPerDl);
|
||||
@ -364,17 +390,24 @@ class _BolusDetailScreenState extends State<BolusDetailScreen> {
|
||||
icon: const Icon(Icons.calculate),
|
||||
)
|
||||
: Container(),
|
||||
Settings.glucoseMeasurement == GlucoseMeasurement.mmolPerL ||
|
||||
[GlucoseDisplayMode.both, GlucoseDisplayMode.bothForDetail].contains(Settings.glucoseDisplayMode)
|
||||
Settings.glucoseMeasurement ==
|
||||
GlucoseMeasurement.mmolPerL ||
|
||||
[
|
||||
GlucoseDisplayMode.both,
|
||||
GlucoseDisplayMode.bothForDetail
|
||||
].contains(Settings.glucoseDisplayMode)
|
||||
? Expanded(
|
||||
child: TextFormField(
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'per mmol/l',
|
||||
suffixText: 'mmol/l',
|
||||
),
|
||||
readOnly: Settings.glucoseMeasurement ==
|
||||
GlucoseMeasurement.mgPerDl,
|
||||
controller: _mmolPerLController,
|
||||
onChanged: (_) async {
|
||||
await Future.delayed(const Duration(seconds: 1));
|
||||
await Future.delayed(
|
||||
const Duration(seconds: 1));
|
||||
convertBetweenMgPerDlAndMmolPerL(
|
||||
calculateFrom:
|
||||
GlucoseMeasurement.mmolPerL);
|
||||
@ -392,7 +425,10 @@ class _BolusDetailScreenState extends State<BolusDetailScreen> {
|
||||
),
|
||||
)
|
||||
: Container(),
|
||||
[GlucoseDisplayMode.both, GlucoseDisplayMode.bothForDetail].contains(Settings.glucoseDisplayMode)
|
||||
[
|
||||
GlucoseDisplayMode.both,
|
||||
GlucoseDisplayMode.bothForDetail
|
||||
].contains(Settings.glucoseDisplayMode)
|
||||
? IconButton(
|
||||
onPressed: () => convertBetweenMgPerDlAndMmolPerL(
|
||||
calculateFrom: GlucoseMeasurement.mgPerDl),
|
||||
@ -409,7 +445,13 @@ class _BolusDetailScreenState extends State<BolusDetailScreen> {
|
||||
),
|
||||
bottomNavigationBar: DetailBottomRow(
|
||||
onCancel: handleCancelAction,
|
||||
onSave: _isSaving ? null : handleSaveAction,
|
||||
onAction:
|
||||
_isSaving ? null : () => handleSaveAction(next: !_isFinalRate),
|
||||
onMiddleAction: _isSaving || _isFinalRate
|
||||
? null
|
||||
: () => handleSaveAction(next: false),
|
||||
actionText: _isFinalRate ? 'SAVE & CLOSE' : 'NEXT',
|
||||
middleActionText: 'SAVE & CLOSE',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -167,7 +167,7 @@ class _BolusListScreenState extends State<BolusListScreen> {
|
||||
child: Column(
|
||||
children: (bolus.units > 0 && (bolus.mgPerDl ?? bolus.mmolPerL ?? 0) > 0)
|
||||
? [
|
||||
Text((((Settings.glucoseMeasurement == GlucoseMeasurement.mgPerDl ? bolus.mgPerDl : bolus.mmolPerL)! / bolus.units)).toString()),
|
||||
Text((((Settings.glucoseMeasurement == GlucoseMeasurement.mgPerDl ? bolus.mgPerDl : bolus.mmolPerL ?? 0)! / bolus.units)).toString()),
|
||||
Text('${Settings.glucoseMeasurementSuffix} per unit',
|
||||
textAlign: TextAlign.center, textScaleFactor: 0.75),
|
||||
]
|
||||
|
@ -76,12 +76,13 @@ class _BolusProfileDetailScreenState extends State<BolusProfileDetailScreen> {
|
||||
|
||||
detailBottomRow = DetailBottomRow(
|
||||
onCancel: handleCancelAction,
|
||||
onSave: handleSaveAction,
|
||||
onAction: handleSaveAction,
|
||||
onMiddleAction: () => handleSaveAction(close: true),
|
||||
);
|
||||
|
||||
detailBottomRowWhileSaving = DetailBottomRow(
|
||||
onCancel: handleCancelAction,
|
||||
onSave: null,
|
||||
onAction: null,
|
||||
);
|
||||
|
||||
actionButton = null;
|
||||
@ -181,6 +182,10 @@ class _BolusProfileDetailScreenState extends State<BolusProfileDetailScreen> {
|
||||
TimeOfDay? suggestedStartTime;
|
||||
TimeOfDay? suggestedEndTime;
|
||||
|
||||
if (_bolusRates.isEmpty) {
|
||||
suggestedStartTime = const TimeOfDay(hour: 0, minute: 0);
|
||||
suggestedEndTime = const TimeOfDay(hour: 0, minute: 0);
|
||||
} else {
|
||||
_bolusRates.asMap().forEach((index, bolus) {
|
||||
if (suggestedStartTime == null && suggestedEndTime == null) {
|
||||
if (index == 0 &&
|
||||
@ -200,6 +205,7 @@ class _BolusProfileDetailScreenState extends State<BolusProfileDetailScreen> {
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Navigator.push(
|
||||
context,
|
||||
@ -215,7 +221,7 @@ class _BolusProfileDetailScreenState extends State<BolusProfileDetailScreen> {
|
||||
).then((result) => reload(message: result?[0]));
|
||||
}
|
||||
|
||||
void handleSaveAction() async {
|
||||
void handleSaveAction({bool close = false}) async {
|
||||
setState(() {
|
||||
bottomNav = detailBottomRowWhileSaving;
|
||||
});
|
||||
@ -229,8 +235,23 @@ class _BolusProfileDetailScreenState extends State<BolusProfileDetailScreen> {
|
||||
notes: _notesController.text,
|
||||
);
|
||||
BolusProfile.put(bolusProfile);
|
||||
|
||||
if (close) {
|
||||
Navigator.pop(context,
|
||||
['${_isNew ? 'New' : ''} Bolus Profile saved', bolusProfile]);
|
||||
} else {
|
||||
if (_isNew) {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) =>
|
||||
BolusProfileDetailScreen(id: bolusProfile.id),
|
||||
),
|
||||
).then((result) => Navigator.pop(context, result));
|
||||
} else {
|
||||
reload(message: 'Bolus Profile saved');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setState(() {
|
||||
|
@ -162,6 +162,21 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
|
||||
_meal = value;
|
||||
_mealController.text = (_meal ?? '').toString();
|
||||
});
|
||||
if (_meal != null) {
|
||||
if (_meal!.carbsPerPortion != null) {
|
||||
_carbsController.text = (_meal!.carbsPerPortion).toString();
|
||||
}
|
||||
if (_meal!.meal.hasValue) {
|
||||
if (_meal!.meal.target!.delayedBolusDuration != null) {
|
||||
_delayController.text =
|
||||
(_meal!.meal.target?.delayedBolusDuration).toString();
|
||||
}
|
||||
if (_meal!.meal.target!.delayedBolusDuration != null) {
|
||||
_delayPercentage = _meal!.meal.target!.delayedBolusPercentage!;
|
||||
}
|
||||
}
|
||||
calculateBolus();
|
||||
}
|
||||
}
|
||||
|
||||
void updateDelayedRatio() {
|
||||
@ -186,12 +201,12 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
|
||||
if (meal != null && meal.carbsPerPortion != null) {
|
||||
setState(() {
|
||||
_carbsController.text = meal.carbsPerPortion.toString();
|
||||
onChangeCarbs();
|
||||
calculateBolus();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void onChangeCarbs() {
|
||||
void calculateBolus() {
|
||||
setState(() {
|
||||
if (_rate != null && !_setManually) {
|
||||
_unitsController.text = ((double.tryParse(_carbsController.text) ?? 0) /
|
||||
@ -356,7 +371,8 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
|
||||
LogBolus.put(delayedBolus);
|
||||
}
|
||||
|
||||
Navigator.pop(context, ['${_isNew ? 'New' : ''} Bolus Saved', logBolus, delayedBolus]);
|
||||
Navigator.pop(context,
|
||||
['${_isNew ? 'New' : ''} Bolus Saved', logBolus, delayedBolus]);
|
||||
}
|
||||
setState(() {
|
||||
_isSaving = false;
|
||||
@ -453,6 +469,7 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_setManually = value;
|
||||
calculateBolus();
|
||||
});
|
||||
},
|
||||
),
|
||||
@ -698,10 +715,11 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
|
||||
child: TextFormField(
|
||||
decoration: InputDecoration(
|
||||
labelText: 'Carbs',
|
||||
suffixText: Settings.nutritionMeasurementSuffix,
|
||||
suffixText:
|
||||
Settings.nutritionMeasurementSuffix,
|
||||
),
|
||||
controller: _carbsController,
|
||||
onChanged: (_) => onChangeCarbs(),
|
||||
onChanged: (_) => calculateBolus(),
|
||||
keyboardType:
|
||||
const TextInputType.numberWithOptions(
|
||||
decimal: true),
|
||||
@ -709,7 +727,10 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
|
||||
),
|
||||
],
|
||||
),
|
||||
TextFormField(
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: TextFormField(
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Delayed Bolus Duration',
|
||||
suffixText: ' min',
|
||||
@ -718,8 +739,9 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
|
||||
onChanged: (value) => setState(() {}),
|
||||
keyboardType: const TextInputType.numberWithOptions(),
|
||||
),
|
||||
(int.tryParse(_delayController.text) ?? 0) != 0
|
||||
? Slider(
|
||||
),
|
||||
Expanded(
|
||||
child: Slider(
|
||||
label: '${_delayPercentage.floor().toString()}%',
|
||||
divisions: 100,
|
||||
value: _delayPercentage,
|
||||
@ -733,8 +755,11 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
|
||||
updateDelayedRatio();
|
||||
}
|
||||
: null,
|
||||
)
|
||||
: Container(),
|
||||
),
|
||||
),
|
||||
const Text('%', textScaleFactor: 1.5),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
children: (int.tryParse(_delayController.text) ?? 0) != 0
|
||||
? [
|
||||
@ -792,7 +817,7 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
|
||||
),
|
||||
bottomNavigationBar: DetailBottomRow(
|
||||
onCancel: handleCancelAction,
|
||||
onSave: _isSaving ? null : handleSaveAction,
|
||||
onAction: _isSaving ? null : handleSaveAction,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -88,6 +88,7 @@ class _LogBolusListScreenState extends State<LogBolusListScreen> {
|
||||
? Scrollbar(
|
||||
controller: _scrollController,
|
||||
child: ListView.builder(
|
||||
padding: const EdgeInsets.all(10.0),
|
||||
controller: _scrollController,
|
||||
shrinkWrap: true,
|
||||
itemCount: widget.logBoli.length,
|
||||
@ -99,9 +100,12 @@ class _LogBolusListScreenState extends State<LogBolusListScreen> {
|
||||
return Card(
|
||||
child: ListTile(
|
||||
onTap: () => handleEditAction(bolus),
|
||||
title: Text(titleText),
|
||||
title: Text(
|
||||
titleText.toUpperCase(),
|
||||
style: Theme.of(context).textTheme.subtitle2,
|
||||
),
|
||||
subtitle: Text(bolus.carbs != null ?
|
||||
'for ${bolus.meal.target.toString()} (${bolus.carbs}${Settings.nutritionMeasurementSuffix} carbs)'
|
||||
'for ${(bolus.meal.target ?? '').toString()} (${bolus.carbs}${Settings.nutritionMeasurementSuffix} carbs)'
|
||||
: 'to correct ${Settings.glucoseMeasurement == GlucoseMeasurement.mgPerDl ? bolus.mgPerDlCorrection : bolus.mmolPerLCorrection} ${Settings.glucoseMeasurementSuffix}'),
|
||||
trailing: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
|
@ -31,7 +31,6 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
|
||||
List<LogBolus> _logBoli = [];
|
||||
|
||||
bool _isNew = true;
|
||||
bool _isSaving = false;
|
||||
|
||||
final GlobalKey<FormState> logEntryForm = GlobalKey<FormState>();
|
||||
final ScrollController _scrollController = ScrollController();
|
||||
@ -50,6 +49,7 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
|
||||
late IconButton refreshButton;
|
||||
late IconButton closeButton;
|
||||
late DetailBottomRow detailBottomRow;
|
||||
late DetailBottomRow detailBottomRowWhileSaving;
|
||||
|
||||
FloatingActionButton? actionButton;
|
||||
List<Widget> appBarActions = [];
|
||||
@ -83,7 +83,13 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
|
||||
|
||||
detailBottomRow = DetailBottomRow(
|
||||
onCancel: handleCancelAction,
|
||||
onSave: _isSaving ? null : handleSaveAction,
|
||||
onAction: handleSaveAction,
|
||||
onMiddleAction: () => handleSaveAction(close: true),
|
||||
);
|
||||
|
||||
detailBottomRowWhileSaving = DetailBottomRow(
|
||||
onCancel: handleCancelAction,
|
||||
onAction: null,
|
||||
);
|
||||
|
||||
actionButton = null;
|
||||
@ -140,7 +146,7 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
|
||||
mgPerDl = int.tryParse(_mgPerDlController.text);
|
||||
setState(() {
|
||||
_mmolPerLController.text =
|
||||
Utils.convertMgPerDlToMmolPerL(mgPerDl!).toString();
|
||||
Utils.convertMgPerDlToMmolPerL(mgPerDl ?? 0).toString();
|
||||
});
|
||||
}
|
||||
if (Settings.glucoseMeasurement != GlucoseMeasurement.mgPerDl &&
|
||||
@ -148,14 +154,14 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
|
||||
mmolPerL = double.tryParse(_mmolPerLController.text);
|
||||
setState(() {
|
||||
_mgPerDlController.text =
|
||||
Utils.convertMmolPerLToMgPerDl(mmolPerL!).toString();
|
||||
Utils.convertMmolPerLToMgPerDl(mmolPerL ?? 0).toString();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void handleSaveAction() async {
|
||||
void handleSaveAction({bool close = false}) async {
|
||||
setState(() {
|
||||
_isSaving = true;
|
||||
bottomNav = detailBottomRowWhileSaving;
|
||||
});
|
||||
if (logEntryForm.currentState!.validate()) {
|
||||
LogEntry logEntry = LogEntry(
|
||||
@ -167,11 +173,26 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
|
||||
notes: _notesController.text,
|
||||
);
|
||||
LogEntry.put(logEntry);
|
||||
Navigator.pushReplacementNamed(context, '/log',
|
||||
arguments: ['${_isNew ? 'New' : ''} Log Entry Saved', logEntry]);
|
||||
|
||||
if (close) {
|
||||
Navigator.pop(
|
||||
context, ['${_isNew ? 'New' : ''} Log Entry Saved', logEntry]);
|
||||
} else {
|
||||
if (_isNew) {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => LogEntryScreen(id: logEntry.id),
|
||||
),
|
||||
).then((result) => Navigator.pop(context, result));
|
||||
} else {
|
||||
reload(message: 'Log Entry Saved');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setState(() {
|
||||
_isSaving = false;
|
||||
bottomNav = detailBottomRow;
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -670,7 +670,7 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
|
||||
),
|
||||
bottomNavigationBar: DetailBottomRow(
|
||||
onCancel: handleCancelAction,
|
||||
onSave: _isSaving ? null : handleSaveAction,
|
||||
onAction: _isSaving ? null : handleSaveAction,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -72,6 +72,7 @@ class _LogMealListScreenState extends State<LogMealListScreen> {
|
||||
? Scrollbar(
|
||||
controller: _scrollController,
|
||||
child: ListView.builder(
|
||||
padding: const EdgeInsets.all(10.0),
|
||||
controller: _scrollController,
|
||||
shrinkWrap: true,
|
||||
itemCount: widget.logMeals.length,
|
||||
@ -82,7 +83,10 @@ class _LogMealListScreenState extends State<LogMealListScreen> {
|
||||
onTap: () => handleEditAction(meal),
|
||||
title: Row(
|
||||
children: [
|
||||
Expanded(child: Text(meal.value)),
|
||||
Expanded(child: Text(
|
||||
meal.value.toUpperCase(),
|
||||
style: Theme.of(context).textTheme.subtitle2,
|
||||
)),
|
||||
Expanded(
|
||||
child: Column(
|
||||
children: ((meal.carbsPerPortion ?? 0) > 0)
|
||||
|
@ -479,7 +479,7 @@ class _LogEventDetailScreenState extends State<LogEventDetailScreen> {
|
||||
),
|
||||
bottomNavigationBar: DetailBottomRow(
|
||||
onCancel: handleCancelAction,
|
||||
onSave: _isSaving ? null : handleSaveAction,
|
||||
onAction: _isSaving ? null : handleSaveAction,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -228,8 +228,6 @@ class _EventTypeDetailScreenState extends State<EventTypeDetailScreen> {
|
||||
).then((result) {
|
||||
setState(() {
|
||||
updateBolusProfile(result?[1]);
|
||||
_bolusProfileController.text =
|
||||
_bolusProfile.toString();
|
||||
});
|
||||
reload(message: result?[0]);
|
||||
});
|
||||
@ -293,7 +291,7 @@ class _EventTypeDetailScreenState extends State<EventTypeDetailScreen> {
|
||||
),
|
||||
bottomNavigationBar: DetailBottomRow(
|
||||
onCancel: handleCancelAction,
|
||||
onSave: _isSaving ? null : handleSaveAction,
|
||||
onAction: _isSaving ? null : handleSaveAction,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -137,7 +137,7 @@ class _MealCategoryDetailScreenState extends State<MealCategoryDetailScreen> {
|
||||
),
|
||||
bottomNavigationBar: DetailBottomRow(
|
||||
onCancel: handleCancelAction,
|
||||
onSave: handleSaveAction,
|
||||
onAction: handleSaveAction,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -228,7 +228,7 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
|
||||
Future<void> onSelectMealSource(MealSource? mealSource) async {
|
||||
setState(() {
|
||||
_mealSource = mealSource;
|
||||
_mealSourceController.text = _mealSource.toString();
|
||||
_mealSourceController.text = (_mealSource ?? '').toString();
|
||||
});
|
||||
if (mealSource != null) {
|
||||
if (mealSource.defaultCarbsRatioAccuracy.hasValue) {
|
||||
@ -458,6 +458,7 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
|
||||
suffixText: ' min',
|
||||
),
|
||||
controller: _delayedBolusDurationController,
|
||||
onChanged: (value) => setState(() {}),
|
||||
keyboardType: const TextInputType.numberWithOptions(),
|
||||
),
|
||||
),
|
||||
@ -469,11 +470,13 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
|
||||
value: _delayedBolusPercentage,
|
||||
min: 0,
|
||||
max: 100,
|
||||
onChanged: (value) {
|
||||
onChanged: _delayedBolusDurationController.text != ''
|
||||
? (value) {
|
||||
setState(() {
|
||||
_delayedBolusPercentage = value;
|
||||
});
|
||||
}),
|
||||
} : null
|
||||
),
|
||||
),
|
||||
const Text('%', textScaleFactor: 1.5),
|
||||
],
|
||||
@ -626,7 +629,7 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
|
||||
),
|
||||
bottomNavigationBar: DetailBottomRow(
|
||||
onCancel: handleCancelAction,
|
||||
onSave: _isSaving ? null : handleSaveAction,
|
||||
onAction: _isSaving ? null : handleSaveAction,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -140,7 +140,7 @@ class _MealPortionTypeDetailScreenState
|
||||
),
|
||||
bottomNavigationBar: DetailBottomRow(
|
||||
onCancel: handleCancelAction,
|
||||
onSave: handleSaveAction,
|
||||
onAction: handleSaveAction,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -362,7 +362,7 @@ class _MealSourceDetailScreenState extends State<MealSourceDetailScreen> {
|
||||
),
|
||||
bottomNavigationBar: DetailBottomRow(
|
||||
onCancel: handleCancelAction,
|
||||
onSave: handleSaveAction,
|
||||
onAction: handleSaveAction,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -175,12 +175,10 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
label: 'Preferred Nutrition Measurement',
|
||||
items: nutritionMeasurementLabels,
|
||||
onChanged: (value) {
|
||||
if (value != null) {
|
||||
setState(() {
|
||||
_nutritionMeasurementLabelController.text = value;
|
||||
_nutritionMeasurementLabelController.text = value ?? '';
|
||||
});
|
||||
saveSettings();
|
||||
}
|
||||
},
|
||||
),
|
||||
Padding(
|
||||
@ -191,12 +189,10 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
label: 'Preferred Glucose Measurement',
|
||||
items: glucoseMeasurementLabels,
|
||||
onChanged: (value) {
|
||||
if (value != null) {
|
||||
setState(() {
|
||||
_glucoseMeasurementLabelController.text = value;
|
||||
_glucoseMeasurementLabelController.text = value ?? '';
|
||||
});
|
||||
saveSettings();
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
|
Loading…
Reference in New Issue
Block a user