2022-03-21 00:07:29 +00:00
|
|
|
import 'dart:math';
|
|
|
|
|
|
|
|
import 'package:diameter/components/detail.dart';
|
|
|
|
import 'package:diameter/components/forms/boolean_form_field.dart';
|
|
|
|
import 'package:diameter/components/forms/number_form_field.dart';
|
2022-07-08 02:08:50 +00:00
|
|
|
import 'package:diameter/localization_keys.dart';
|
2022-03-21 00:07:29 +00:00
|
|
|
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/log_bolus.dart';
|
|
|
|
import 'package:diameter/models/log_entry.dart';
|
|
|
|
import 'package:diameter/models/log_meal.dart';
|
|
|
|
import 'package:diameter/models/settings.dart';
|
|
|
|
import 'package:diameter/navigation.dart';
|
|
|
|
import 'package:diameter/screens/log/log_entry/log_meal_detail.dart';
|
|
|
|
import 'package:diameter/utils/utils.dart';
|
|
|
|
import 'package:flutter/material.dart';
|
2022-07-08 02:08:50 +00:00
|
|
|
import 'package:flutter_translate/flutter_translate.dart';
|
2022-03-21 00:07:29 +00:00
|
|
|
|
|
|
|
enum BolusType {
|
|
|
|
meal,
|
|
|
|
glucose,
|
|
|
|
}
|
|
|
|
|
|
|
|
enum GlucoseParameter {
|
|
|
|
mgdlCurrent,
|
|
|
|
mgdlTarget,
|
|
|
|
mgdlCorrection,
|
|
|
|
mmolCurrent,
|
|
|
|
mmolTarget,
|
|
|
|
mmolCorrection,
|
|
|
|
}
|
|
|
|
|
|
|
|
class LogBolusDetailScreen extends StatefulWidget {
|
|
|
|
static const String routeName = '/log-bolus';
|
|
|
|
|
|
|
|
final int logEntryId;
|
|
|
|
final int id;
|
|
|
|
|
|
|
|
const LogBolusDetailScreen({Key? key, this.logEntryId = 0, this.id = 0})
|
|
|
|
: super(key: key);
|
|
|
|
|
|
|
|
@override
|
|
|
|
_LogBolusDetailScreenState createState() => _LogBolusDetailScreenState();
|
|
|
|
}
|
|
|
|
|
|
|
|
class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
|
|
|
|
LogEntry? _logEntry;
|
|
|
|
LogBolus? _logBolus;
|
|
|
|
|
|
|
|
bool _isNew = true;
|
|
|
|
bool _isSaving = false;
|
|
|
|
|
|
|
|
final GlobalKey<FormState> _logBolusForm = GlobalKey<FormState>();
|
|
|
|
final ScrollController _scrollController = ScrollController();
|
|
|
|
|
|
|
|
final _unitsController = TextEditingController(text: '');
|
|
|
|
final _carbsController = TextEditingController(text: '');
|
|
|
|
final _mgPerDlCurrentController = TextEditingController(text: '');
|
|
|
|
final _mgPerDlTargetController = TextEditingController(text: '');
|
|
|
|
final _mgPerDlCorrectionController = TextEditingController(text: '');
|
|
|
|
final _mmolPerLCurrentController = TextEditingController(text: '');
|
|
|
|
final _mmolPerLTargetController = TextEditingController(text: '');
|
|
|
|
final _mmolPerLCorrectionController = TextEditingController(text: '');
|
|
|
|
final _delayController = TextEditingController(text: '');
|
|
|
|
final _notesController = TextEditingController(text: '');
|
|
|
|
|
|
|
|
final _delayedUnitsController = TextEditingController(text: '');
|
|
|
|
final _immediateUnitsController = TextEditingController(text: '');
|
|
|
|
|
|
|
|
final _mealController = TextEditingController(text: '');
|
|
|
|
|
|
|
|
bool _setManually = false;
|
|
|
|
BolusType _bolusType = BolusType.meal;
|
|
|
|
LogMeal? _meal;
|
|
|
|
Bolus? _rate;
|
|
|
|
double _delayPercentage = 0;
|
|
|
|
|
|
|
|
List<LogMeal> _logMeals = [];
|
|
|
|
|
|
|
|
@override
|
|
|
|
void initState() {
|
|
|
|
super.initState();
|
|
|
|
reload();
|
|
|
|
|
|
|
|
_logEntry = LogEntry.get(widget.logEntryId);
|
|
|
|
_logMeals = LogMeal.getRecentWithoutBolus(widget.logEntryId);
|
|
|
|
|
|
|
|
if (widget.id != 0) {
|
|
|
|
_carbsController.text = (_logBolus!.carbs ?? '').toString();
|
|
|
|
_delayController.text = (_logBolus!.delay ?? '').toString();
|
|
|
|
_notesController.text = _logBolus!.notes ?? '';
|
|
|
|
_setManually = _logBolus!.setManually;
|
|
|
|
_meal = _logBolus!.meal.target;
|
|
|
|
_mealController.text = (_meal ?? '').toString();
|
|
|
|
_rate = _logBolus!.rate.target;
|
|
|
|
}
|
|
|
|
|
|
|
|
_rate ??= Bolus.getRateForTime(_logEntry?.time);
|
|
|
|
|
|
|
|
_mgPerDlCurrentController.text = (_logBolus?.mgPerDlCurrent ??
|
|
|
|
(LogEntry.hasUncorrectedGlucose(widget.logEntryId)
|
|
|
|
? _logEntry?.mgPerDl ?? 0
|
|
|
|
: 0))
|
|
|
|
.toString();
|
|
|
|
_mgPerDlTargetController.text =
|
|
|
|
(_logBolus?.mgPerDlTarget ?? Settings.targetMgPerDl).toString();
|
|
|
|
_mgPerDlCorrectionController.text = (_logBolus?.mgPerDlCorrection ??
|
|
|
|
max(
|
|
|
|
(int.tryParse(_mgPerDlCurrentController.text) ?? 0) -
|
|
|
|
(int.tryParse(_mgPerDlTargetController.text) ?? 0),
|
|
|
|
0))
|
|
|
|
.toString();
|
|
|
|
_mmolPerLCurrentController.text = (_logBolus?.mmolPerLCurrent ??
|
|
|
|
(LogEntry.hasUncorrectedGlucose(widget.logEntryId)
|
|
|
|
? _logEntry?.mmolPerL ?? 0
|
|
|
|
: 0))
|
|
|
|
.toString();
|
|
|
|
_mmolPerLTargetController.text =
|
|
|
|
(_logBolus?.mmolPerLTarget ?? Settings.targetMmolPerL).toString();
|
|
|
|
_mmolPerLCorrectionController.text = (_logBolus?.mmolPerLCorrection ??
|
|
|
|
max(
|
|
|
|
(double.tryParse(_mmolPerLCurrentController.text) ?? 0) -
|
|
|
|
(double.tryParse(_mmolPerLTargetController.text) ?? 0),
|
|
|
|
0))
|
|
|
|
.toString();
|
|
|
|
|
|
|
|
_unitsController.text = (_logBolus?.units ??
|
|
|
|
(_rate != null && !_setManually
|
|
|
|
? ((int.tryParse(_mgPerDlCorrectionController.text) ?? 0) /
|
|
|
|
((_rate!.mgPerDl ?? 0) / _rate!.units))
|
|
|
|
: 0))
|
|
|
|
.toString();
|
|
|
|
|
|
|
|
if (widget.id == 0 && LogEntry.hasUncorrectedGlucose(widget.logEntryId)) {
|
|
|
|
_bolusType = BolusType.glucose;
|
|
|
|
}
|
|
|
|
|
|
|
|
calculateBolus();
|
|
|
|
}
|
|
|
|
|
|
|
|
@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}) {
|
|
|
|
if (widget.id != 0) {
|
|
|
|
setState(() {
|
|
|
|
_logBolus = LogBolus.get(widget.id);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
_isNew = _logBolus == null;
|
|
|
|
|
|
|
|
setState(() {
|
|
|
|
if (message != null) {
|
|
|
|
var snackBar = SnackBar(
|
|
|
|
content: Text(message),
|
|
|
|
duration: const Duration(seconds: 2),
|
|
|
|
);
|
|
|
|
ScaffoldMessenger.of(context)
|
|
|
|
..removeCurrentSnackBar()
|
|
|
|
..showSnackBar(snackBar);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
void updateLogMeal(LogMeal? value) {
|
|
|
|
setState(() {
|
|
|
|
_meal = value;
|
|
|
|
_mealController.text = (_meal ?? '').toString();
|
|
|
|
});
|
|
|
|
if (_meal != null) {
|
|
|
|
if (_meal!.totalCarbs != null) {
|
|
|
|
_carbsController.text = (_meal!.totalCarbs).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 onSelectMeal(LogMeal? meal) {
|
|
|
|
updateLogMeal(meal);
|
|
|
|
if (meal != null && meal.totalCarbs != null) {
|
|
|
|
setState(() {
|
|
|
|
_carbsController.text = meal.totalCarbs.toString();
|
|
|
|
calculateBolus();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void calculateBolus() {
|
|
|
|
if (_rate != null && !_setManually) {
|
|
|
|
double? units;
|
|
|
|
|
|
|
|
if (_bolusType == BolusType.glucose) {
|
|
|
|
if (Settings.glucoseMeasurement == GlucoseMeasurement.mgPerDl) {
|
|
|
|
units = (int.tryParse(_mgPerDlCorrectionController.text) ?? 0) /
|
|
|
|
(_rate!.mgPerDl ?? 1) /
|
|
|
|
_rate!.units;
|
|
|
|
}
|
|
|
|
if (Settings.glucoseMeasurement == GlucoseMeasurement.mmolPerL) {
|
|
|
|
units = (int.tryParse(_mmolPerLCorrectionController.text) ?? 0) /
|
|
|
|
(_rate!.mmolPerL ?? 1) /
|
|
|
|
_rate!.units;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (_bolusType == BolusType.meal) {
|
|
|
|
units = (double.tryParse(_carbsController.text) ?? 0) /
|
|
|
|
(_rate!.carbs / _rate!.units);
|
|
|
|
}
|
|
|
|
|
|
|
|
updateDelayedRatio(totalUnitsUpdate: units);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void onChangeGlucose() {
|
|
|
|
int? mgPerDlCurrent;
|
|
|
|
int? mgPerDlTarget;
|
|
|
|
int? mgPerDlCorrection;
|
|
|
|
|
|
|
|
double? mmolPerLCurrent;
|
|
|
|
double? mmolPerLTarget;
|
|
|
|
double? mmolPerLCorrection;
|
|
|
|
|
|
|
|
if (Settings.glucoseMeasurement == GlucoseMeasurement.mgPerDl &&
|
|
|
|
_mgPerDlCurrentController.text != '' &&
|
|
|
|
_mgPerDlTargetController.text != '') {
|
|
|
|
mgPerDlCurrent = int.tryParse(_mgPerDlCurrentController.text);
|
|
|
|
mgPerDlTarget = int.tryParse(_mgPerDlTargetController.text);
|
|
|
|
mgPerDlCorrection = max((mgPerDlCurrent ?? 0) - (mgPerDlTarget ?? 0), 0);
|
|
|
|
}
|
|
|
|
if (Settings.glucoseMeasurement == GlucoseMeasurement.mmolPerL &&
|
|
|
|
_mmolPerLCurrentController.text != '') {
|
|
|
|
mmolPerLCurrent = double.tryParse(_mmolPerLCurrentController.text);
|
|
|
|
mmolPerLTarget = double.tryParse(_mmolPerLTargetController.text);
|
|
|
|
mmolPerLCorrection =
|
|
|
|
max((mmolPerLCurrent ?? 0) - (mmolPerLTarget ?? 0), 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((mgPerDlCurrent != null && mmolPerLCurrent == null) ||
|
|
|
|
(mgPerDlTarget != null && mmolPerLTarget == null) ||
|
|
|
|
(mgPerDlCorrection != null && mmolPerLCorrection == null)) {
|
|
|
|
setState(() {
|
|
|
|
_mgPerDlCorrectionController.text = (mgPerDlCorrection ?? 0).toString();
|
|
|
|
_mmolPerLCurrentController.text =
|
|
|
|
Utils.convertMgPerDlToMmolPerL(mgPerDlCurrent ?? 0).toString();
|
|
|
|
_mmolPerLTargetController.text =
|
|
|
|
Utils.convertMgPerDlToMmolPerL(mgPerDlTarget ?? 0).toString();
|
|
|
|
_mmolPerLCorrectionController.text =
|
|
|
|
Utils.convertMgPerDlToMmolPerL(mgPerDlCorrection ?? 0).toString();
|
|
|
|
calculateBolus();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
if ((mmolPerLCurrent != null && mgPerDlCurrent == null) ||
|
|
|
|
(mmolPerLTarget != null && mgPerDlTarget == null) ||
|
|
|
|
(mmolPerLCorrection != null && mgPerDlCorrection == null)) {
|
|
|
|
setState(() {
|
|
|
|
_mmolPerLCurrentController.text = (mmolPerLCorrection ?? 0).toString();
|
|
|
|
_mgPerDlCurrentController.text =
|
|
|
|
Utils.convertMmolPerLToMgPerDl(mmolPerLCurrent ?? 0).toString();
|
|
|
|
_mgPerDlTargetController.text =
|
|
|
|
Utils.convertMmolPerLToMgPerDl(mmolPerLTarget ?? 0).toString();
|
|
|
|
_mgPerDlCorrectionController.text =
|
|
|
|
Utils.convertMmolPerLToMgPerDl(mmolPerLCorrection ?? 0).toString();
|
|
|
|
calculateBolus();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void updateDelayedRatio({
|
|
|
|
double? totalUnitsUpdate,
|
|
|
|
double? delayedUnitsUpdate,
|
|
|
|
double? immediateUnitsUpdate,
|
|
|
|
double? percentageUpdate
|
|
|
|
}) {
|
|
|
|
int precision = Utils.getFractionDigitsLength(Settings.insulinSteps);
|
|
|
|
|
|
|
|
double? totalUnits;
|
|
|
|
double? delayedUnits;
|
|
|
|
double? immediateUnits;
|
|
|
|
|
|
|
|
if (totalUnitsUpdate != null) {
|
|
|
|
totalUnits = Utils.roundToMultipleOfBase(totalUnitsUpdate, Settings.insulinSteps);
|
|
|
|
} else if (double.tryParse(_unitsController.text) != null) {
|
|
|
|
totalUnits = Utils.roundToMultipleOfBase(double.tryParse(_unitsController.text)!, Settings.insulinSteps);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (delayedUnitsUpdate != null) {
|
|
|
|
delayedUnits = Utils.roundToMultipleOfBase(delayedUnitsUpdate, Settings.insulinSteps);
|
|
|
|
} else if (double.tryParse(_delayedUnitsController.text) != null) {
|
|
|
|
delayedUnits = Utils.roundToMultipleOfBase(double.tryParse(_delayedUnitsController.text)!, Settings.insulinSteps);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (immediateUnitsUpdate != null) {
|
|
|
|
immediateUnits = Utils.roundToMultipleOfBase(immediateUnitsUpdate, Settings.insulinSteps);
|
|
|
|
} else if (double.tryParse(_immediateUnitsController.text) != null) {
|
|
|
|
immediateUnits = Utils.roundToMultipleOfBase(double.tryParse(_immediateUnitsController.text)!, Settings.insulinSteps);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (totalUnits == null) {
|
|
|
|
if (percentageUpdate != null) {
|
|
|
|
if (immediateUnits != null) {
|
|
|
|
totalUnits = immediateUnits / (100 - percentageUpdate) * 100;
|
|
|
|
} else if (delayedUnits != null) {
|
|
|
|
totalUnits = delayedUnits / percentageUpdate * 100;
|
|
|
|
}
|
|
|
|
} else if (delayedUnits != null && immediateUnits != null) {
|
|
|
|
totalUnits = Utils.addDoublesWithPrecision(
|
|
|
|
delayedUnits, immediateUnits, precision);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (totalUnits != null) {
|
|
|
|
totalUnits =
|
|
|
|
Utils.roundToMultipleOfBase(totalUnits, Settings.insulinSteps);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
setState(() {
|
|
|
|
_unitsController.text = Utils.toStringMatchingTemplateFractionPrecision(
|
|
|
|
totalUnits ?? 0, Settings.insulinSteps);
|
|
|
|
});
|
|
|
|
|
|
|
|
if (totalUnits != null) {
|
|
|
|
double percentage = percentageUpdate ?? _delayPercentage;
|
|
|
|
|
|
|
|
if (totalUnitsUpdate != null || percentageUpdate != null) {
|
|
|
|
immediateUnits = Utils.roundToMultipleOfBase(
|
|
|
|
totalUnits * (100 - percentage) / 100, Settings.insulinSteps);
|
|
|
|
} else if (delayedUnitsUpdate != null) {
|
|
|
|
immediateUnits = totalUnits - delayedUnits!;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (immediateUnits != null) {
|
|
|
|
delayedUnits = Utils.addDoublesWithPrecision(
|
|
|
|
totalUnits, -immediateUnits, precision);
|
|
|
|
|
|
|
|
setState(() {
|
|
|
|
_immediateUnitsController.text =
|
|
|
|
Utils.toStringMatchingTemplateFractionPrecision(
|
|
|
|
immediateUnits!, Settings.insulinSteps);
|
|
|
|
_delayedUnitsController.text =
|
|
|
|
Utils.toStringMatchingTemplateFractionPrecision(
|
|
|
|
delayedUnits!, Settings.insulinSteps);
|
|
|
|
if (totalUnits != 0) {
|
|
|
|
_delayPercentage = delayedUnits / totalUnits! * 100;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void handleSaveAction() async {
|
|
|
|
setState(() {
|
|
|
|
_isSaving = true;
|
|
|
|
});
|
|
|
|
if (_logBolusForm.currentState!.validate()) {
|
|
|
|
LogBolus logBolus;
|
|
|
|
LogBolus? delayedBolus;
|
|
|
|
|
|
|
|
if ((int.tryParse(_delayController.text) ?? 0) != 0 &&
|
|
|
|
_delayPercentage != 0 &&
|
|
|
|
_delayPercentage != 100) {
|
|
|
|
logBolus = LogBolus(
|
|
|
|
id: widget.id,
|
|
|
|
units: double.tryParse(_immediateUnitsController.text) ?? 0,
|
|
|
|
setManually: _setManually,
|
|
|
|
notes: _notesController.text,
|
|
|
|
);
|
|
|
|
delayedBolus = LogBolus(
|
|
|
|
delay: int.tryParse(_delayController.text),
|
|
|
|
units: double.tryParse(_delayedUnitsController.text) ?? 0,
|
|
|
|
setManually: _setManually,
|
|
|
|
notes: _notesController.text,
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
logBolus = LogBolus(
|
|
|
|
id: widget.id,
|
|
|
|
units: double.tryParse(_unitsController.text) ?? 0,
|
|
|
|
delay: _delayPercentage == 100
|
|
|
|
? int.tryParse(_delayController.text)
|
|
|
|
: null,
|
|
|
|
setManually: _setManually,
|
|
|
|
notes: _notesController.text,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (_bolusType == BolusType.meal) {
|
|
|
|
logBolus.carbs = double.tryParse(_carbsController.text);
|
|
|
|
if (delayedBolus != null) {
|
|
|
|
delayedBolus.carbs = double.tryParse(_carbsController.text);
|
|
|
|
}
|
|
|
|
logBolus.mgPerDlCurrent = null;
|
|
|
|
logBolus.mmolPerLCurrent = null;
|
|
|
|
} else {
|
|
|
|
logBolus.carbs = null;
|
|
|
|
logBolus.mgPerDlCurrent = int.tryParse(_mgPerDlCurrentController.text);
|
|
|
|
logBolus.mmolPerLCurrent =
|
|
|
|
double.tryParse(_mmolPerLCurrentController.text);
|
|
|
|
logBolus.mgPerDlTarget = int.tryParse(_mgPerDlTargetController.text);
|
|
|
|
logBolus.mmolPerLTarget =
|
|
|
|
double.tryParse(_mmolPerLTargetController.text);
|
|
|
|
logBolus.mgPerDlCorrection =
|
|
|
|
int.tryParse(_mgPerDlCorrectionController.text);
|
|
|
|
logBolus.mmolPerLCorrection =
|
|
|
|
double.tryParse(_mmolPerLCorrectionController.text);
|
|
|
|
if (delayedBolus != null) {
|
|
|
|
delayedBolus.mgPerDlCurrent =
|
|
|
|
int.tryParse(_mgPerDlCurrentController.text);
|
|
|
|
delayedBolus.mmolPerLCurrent =
|
|
|
|
double.tryParse(_mmolPerLCurrentController.text);
|
|
|
|
delayedBolus.mgPerDlTarget =
|
|
|
|
int.tryParse(_mgPerDlTargetController.text);
|
|
|
|
delayedBolus.mmolPerLTarget =
|
|
|
|
double.tryParse(_mmolPerLTargetController.text);
|
|
|
|
delayedBolus.mgPerDlCorrection =
|
|
|
|
int.tryParse(_mgPerDlCorrectionController.text);
|
|
|
|
delayedBolus.mmolPerLCorrection =
|
|
|
|
double.tryParse(_mmolPerLCorrectionController.text);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
logBolus.logEntry.target = _logEntry;
|
|
|
|
logBolus.meal.target = _meal;
|
|
|
|
logBolus.rate.target = _rate;
|
|
|
|
LogBolus.put(logBolus);
|
|
|
|
|
|
|
|
if (delayedBolus != null) {
|
|
|
|
delayedBolus.logEntry.target = _logEntry;
|
|
|
|
delayedBolus.meal.target = _meal;
|
|
|
|
delayedBolus.rate.target = _rate;
|
|
|
|
LogBolus.put(delayedBolus);
|
|
|
|
}
|
|
|
|
|
|
|
|
Navigator.pop(context,
|
2022-07-08 02:08:50 +00:00
|
|
|
[translate(LocalizationKeys.log_detail_tabs_bolus_saved, args: {
|
|
|
|
"status": _isNew ? LocalizationKeys.log_detail_tabs_bolus_new : ''
|
|
|
|
}), logBolus, delayedBolus]);
|
2022-03-21 00:07:29 +00:00
|
|
|
}
|
|
|
|
setState(() {
|
|
|
|
_isSaving = false;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
void handleCancelAction() {
|
|
|
|
if (Settings.get().showConfirmationDialogOnCancel &&
|
|
|
|
((_isNew &&
|
|
|
|
(_carbsController.text != '' ||
|
|
|
|
(_bolusType == BolusType.glucose &&
|
|
|
|
(_mgPerDlCurrentController.text !=
|
|
|
|
(_logEntry?.mgPerDl.toString() ?? '') ||
|
|
|
|
_mmolPerLCurrentController.text !=
|
|
|
|
(_logEntry?.mmolPerL.toString() ?? ''))) ||
|
|
|
|
_mgPerDlTargetController.text !=
|
|
|
|
Settings.targetMgPerDl.toString() ||
|
|
|
|
_mmolPerLTargetController.text !=
|
|
|
|
Settings.targetMmolPerL.toString() ||
|
|
|
|
_delayController.text != '' ||
|
|
|
|
_setManually ||
|
|
|
|
_notesController.text != '')) ||
|
|
|
|
(!_isNew &&
|
|
|
|
(double.tryParse(_unitsController.text) != _logBolus!.units ||
|
|
|
|
double.tryParse(_carbsController.text) !=
|
|
|
|
_logBolus!.carbs ||
|
|
|
|
int.tryParse(_mgPerDlCurrentController.text) !=
|
|
|
|
_logBolus!.mgPerDlCurrent ||
|
|
|
|
int.tryParse(_mgPerDlTargetController.text) !=
|
|
|
|
_logBolus!.mgPerDlTarget ||
|
|
|
|
int.tryParse(_mgPerDlCorrectionController.text) !=
|
|
|
|
_logBolus!.mgPerDlCorrection ||
|
|
|
|
double.tryParse(_mmolPerLCurrentController.text) !=
|
|
|
|
_logBolus!.mmolPerLCurrent ||
|
|
|
|
double.tryParse(_mmolPerLTargetController.text) !=
|
|
|
|
_logBolus!.mmolPerLTarget ||
|
|
|
|
double.tryParse(_mmolPerLCorrectionController.text) !=
|
|
|
|
_logBolus!.mmolPerLCorrection ||
|
|
|
|
int.tryParse(_delayController.text) != _logBolus!.delay ||
|
|
|
|
_setManually != _logBolus!.setManually ||
|
|
|
|
_notesController.text != (_logBolus!.notes ?? ''))))) {
|
|
|
|
DialogUtils.showCancelConfirmationDialog(
|
|
|
|
context: context,
|
|
|
|
isNew: _isNew,
|
|
|
|
onSave: handleSaveAction,
|
|
|
|
);
|
|
|
|
} else {
|
|
|
|
Navigator.pop(context);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@override
|
|
|
|
Widget build(BuildContext context) {
|
|
|
|
return Scaffold(
|
|
|
|
appBar: AppBar(
|
2022-07-08 02:08:50 +00:00
|
|
|
title: Text(translate(LocalizationKeys.log_detail_tabs_bolus_detail_title, args: {
|
|
|
|
"status": _isNew ? LocalizationKeys.log_detail_tabs_bolus_new : LocalizationKeys.general_edit
|
|
|
|
})),
|
2022-03-21 00:07:29 +00:00
|
|
|
),
|
|
|
|
drawer: const Navigation(currentLocation: LogBolusDetailScreen.routeName),
|
|
|
|
body: Scrollbar(
|
|
|
|
controller: _scrollController,
|
|
|
|
child: SingleChildScrollView(
|
|
|
|
controller: _scrollController,
|
|
|
|
child: Column(
|
|
|
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
|
|
children: <Widget>[
|
|
|
|
FormWrapper(
|
|
|
|
formState: _logBolusForm,
|
|
|
|
fields: [
|
|
|
|
Row(
|
|
|
|
children: [
|
|
|
|
Expanded(
|
|
|
|
child: NumberFormField(
|
|
|
|
label: 'Bolus Units',
|
|
|
|
suffix: ' U',
|
|
|
|
controller: _unitsController,
|
|
|
|
step: Settings.insulinSteps,
|
|
|
|
autoRoundToMultipleOfStep: true,
|
|
|
|
onChanged: (value) {
|
|
|
|
setState(() {
|
|
|
|
_setManually = true;
|
|
|
|
});
|
|
|
|
updateDelayedRatio(totalUnitsUpdate: value);
|
|
|
|
},
|
|
|
|
),
|
|
|
|
),
|
|
|
|
Expanded(
|
|
|
|
child: BooleanFormField(
|
|
|
|
contentPadding: const EdgeInsets.only(
|
|
|
|
left: 10.0, right: 10.0, top: 10.0),
|
|
|
|
value: _setManually,
|
2022-07-08 02:08:50 +00:00
|
|
|
label: translate(LocalizationKeys.log_detail_tabs_bolus_detail_fields_setManually),
|
2022-03-21 00:07:29 +00:00
|
|
|
onChanged: (value) {
|
|
|
|
setState(() {
|
|
|
|
_setManually = value;
|
|
|
|
calculateBolus();
|
|
|
|
});
|
|
|
|
},
|
|
|
|
),
|
|
|
|
),
|
|
|
|
],
|
|
|
|
),
|
|
|
|
Row(
|
|
|
|
children: [
|
|
|
|
Expanded(
|
|
|
|
child: RadioListTile(
|
2022-07-08 02:08:50 +00:00
|
|
|
title: Text(translate(LocalizationKeys.log_detail_tabs_bolus_detail_fields_forGlucose)),
|
2022-03-21 00:07:29 +00:00
|
|
|
groupValue: _bolusType,
|
|
|
|
value: BolusType.glucose,
|
|
|
|
onChanged: (_) {
|
|
|
|
setState(() {
|
|
|
|
_bolusType = BolusType.glucose;
|
|
|
|
calculateBolus();
|
|
|
|
});
|
|
|
|
}),
|
|
|
|
),
|
|
|
|
Expanded(
|
|
|
|
child: RadioListTile(
|
2022-07-08 02:08:50 +00:00
|
|
|
title: Text(translate(LocalizationKeys.log_detail_tabs_bolus_detail_fields_forMeal)),
|
2022-03-21 00:07:29 +00:00
|
|
|
groupValue: _bolusType,
|
|
|
|
value: BolusType.meal,
|
|
|
|
onChanged: (value) {
|
|
|
|
setState(() {
|
|
|
|
_bolusType = BolusType.meal;
|
|
|
|
calculateBolus();
|
|
|
|
});
|
|
|
|
}),
|
|
|
|
),
|
|
|
|
],
|
|
|
|
),
|
|
|
|
Column(
|
|
|
|
children: _bolusType == BolusType.glucose
|
|
|
|
? [
|
|
|
|
Row(
|
|
|
|
children: Settings.glucoseMeasurement ==
|
|
|
|
GlucoseMeasurement.mgPerDl ||
|
|
|
|
[
|
|
|
|
GlucoseDisplayMode.both,
|
|
|
|
GlucoseDisplayMode.bothForDetail
|
|
|
|
].contains(Settings.glucoseDisplayMode)
|
|
|
|
? [
|
|
|
|
Expanded(
|
|
|
|
child: Padding(
|
|
|
|
padding:
|
|
|
|
const EdgeInsets.only(right: 5.0),
|
|
|
|
child: NumberFormField(
|
2022-07-08 02:08:50 +00:00
|
|
|
label: translate(LocalizationKeys.log_detail_tabs_bolus_detail_fields_current),
|
|
|
|
suffix: Settings.glucoseMeasurementSuffix,
|
2022-03-21 00:07:29 +00:00
|
|
|
controller:
|
|
|
|
_mgPerDlCurrentController,
|
|
|
|
onChanged: (_) => onChangeGlucose(),
|
|
|
|
showSteppers: false,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
Expanded(
|
|
|
|
child: Padding(
|
|
|
|
padding: const EdgeInsets.symmetric(
|
|
|
|
horizontal: 5.0),
|
|
|
|
child: NumberFormField(
|
2022-07-08 02:08:50 +00:00
|
|
|
label: translate(LocalizationKeys.log_detail_tabs_bolus_detail_fields_target),
|
|
|
|
suffix: Settings.glucoseMeasurementSuffix,
|
2022-03-21 00:07:29 +00:00
|
|
|
controller:
|
|
|
|
_mgPerDlTargetController,
|
|
|
|
onChanged: (_) => onChangeGlucose(),
|
|
|
|
showSteppers: false,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
Expanded(
|
|
|
|
child: Padding(
|
|
|
|
padding:
|
|
|
|
const EdgeInsets.only(left: 5.0),
|
|
|
|
child: TextFormField(
|
2022-07-08 02:08:50 +00:00
|
|
|
decoration: InputDecoration(
|
|
|
|
labelText: translate(LocalizationKeys.log_detail_tabs_bolus_detail_fields_correction),
|
|
|
|
suffixText: Settings.glucoseMeasurementSuffix,
|
2022-03-21 00:07:29 +00:00
|
|
|
),
|
|
|
|
controller:
|
|
|
|
_mgPerDlCorrectionController,
|
|
|
|
readOnly: true,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
]
|
|
|
|
: [],
|
|
|
|
),
|
|
|
|
Padding(
|
|
|
|
padding: EdgeInsets.only(
|
|
|
|
top: [
|
|
|
|
GlucoseDisplayMode.both,
|
|
|
|
GlucoseDisplayMode.bothForDetail
|
|
|
|
].contains(Settings.glucoseDisplayMode)
|
|
|
|
? 10.0
|
|
|
|
: 0.0),
|
|
|
|
child: Row(
|
|
|
|
children: Settings.glucoseMeasurement ==
|
|
|
|
GlucoseMeasurement.mmolPerL ||
|
|
|
|
[
|
|
|
|
GlucoseDisplayMode.both,
|
|
|
|
GlucoseDisplayMode.bothForDetail
|
|
|
|
].contains(Settings.glucoseDisplayMode)
|
|
|
|
? [
|
|
|
|
Expanded(
|
|
|
|
child: Padding(
|
|
|
|
padding: const EdgeInsets.only(
|
|
|
|
right: 5.0),
|
|
|
|
child: NumberFormField(
|
2022-07-08 02:08:50 +00:00
|
|
|
label: translate(LocalizationKeys.log_detail_tabs_bolus_detail_fields_current),
|
|
|
|
suffix: Settings.glucoseMeasurementSuffix,
|
2022-03-21 00:07:29 +00:00
|
|
|
controller:
|
|
|
|
_mmolPerLCurrentController,
|
|
|
|
onChanged: (_) =>
|
|
|
|
onChangeGlucose(),
|
|
|
|
showSteppers: false,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
Expanded(
|
|
|
|
child: Padding(
|
|
|
|
padding: const EdgeInsets.symmetric(
|
|
|
|
horizontal: 5.0),
|
|
|
|
child: NumberFormField(
|
2022-07-08 02:08:50 +00:00
|
|
|
label: translate(LocalizationKeys.log_detail_tabs_bolus_detail_fields_target),
|
|
|
|
suffix: Settings.glucoseMeasurementSuffix,
|
2022-03-21 00:07:29 +00:00
|
|
|
controller:
|
|
|
|
_mmolPerLTargetController,
|
|
|
|
onChanged: (_) =>
|
|
|
|
onChangeGlucose(),
|
|
|
|
showSteppers: false,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
Expanded(
|
|
|
|
child: Padding(
|
|
|
|
padding: const EdgeInsets.only(
|
|
|
|
left: 5.0),
|
|
|
|
child: TextFormField(
|
2022-07-08 02:08:50 +00:00
|
|
|
decoration: InputDecoration(
|
|
|
|
labelText: translate(LocalizationKeys.log_detail_tabs_bolus_detail_fields_correction),
|
|
|
|
suffixText: Settings.glucoseMeasurementSuffix,
|
2022-03-21 00:07:29 +00:00
|
|
|
),
|
|
|
|
controller:
|
|
|
|
_mmolPerLCorrectionController,
|
|
|
|
readOnly: true,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
]
|
|
|
|
: [],
|
|
|
|
),
|
|
|
|
),
|
|
|
|
]
|
|
|
|
: [
|
|
|
|
Row(
|
|
|
|
children: [
|
|
|
|
Expanded(
|
|
|
|
child: AutoCompleteDropdownButton<LogMeal>(
|
|
|
|
controller: _mealController,
|
|
|
|
selectedItem: _meal,
|
2022-07-08 02:08:50 +00:00
|
|
|
label: translate(LocalizationKeys.log_detail_tabs_bolus_detail_fields_meal),
|
2022-03-21 00:07:29 +00:00
|
|
|
items: _logMeals,
|
|
|
|
onChanged: onSelectMeal,
|
|
|
|
),
|
|
|
|
),
|
|
|
|
IconButton(
|
|
|
|
onPressed: () {
|
|
|
|
Navigator.push(
|
|
|
|
context,
|
|
|
|
MaterialPageRoute(
|
|
|
|
builder: (context) => _meal == null
|
|
|
|
? const LogMealDetailScreen()
|
|
|
|
: LogMealDetailScreen(
|
|
|
|
id: _meal!.id),
|
|
|
|
),
|
|
|
|
).then((result) {
|
|
|
|
updateLogMeal(result?[1]);
|
|
|
|
reload(message: result?[0]);
|
|
|
|
});
|
|
|
|
},
|
|
|
|
icon: Icon(
|
|
|
|
_meal == null ? Icons.add : Icons.edit),
|
|
|
|
),
|
|
|
|
],
|
|
|
|
),
|
|
|
|
Padding(
|
|
|
|
padding: const EdgeInsets.only(top: 10.0),
|
|
|
|
child: NumberFormField(
|
|
|
|
label: 'Carbs',
|
|
|
|
suffix: Settings.nutritionMeasurementSuffix,
|
|
|
|
controller: _carbsController,
|
|
|
|
step: Settings.nutritionSteps,
|
|
|
|
onChanged: (value) {
|
|
|
|
_carbsController.text =
|
|
|
|
(value ?? 0).toString();
|
|
|
|
calculateBolus();
|
|
|
|
},
|
|
|
|
),
|
|
|
|
),
|
|
|
|
],
|
|
|
|
),
|
|
|
|
Row(
|
|
|
|
children: [
|
|
|
|
Expanded(
|
|
|
|
child: TextFormField(
|
2022-07-08 02:08:50 +00:00
|
|
|
decoration: InputDecoration(
|
|
|
|
labelText: translate(LocalizationKeys.log_detail_tabs_bolus_detail_fields_delayedBolusDuration),
|
|
|
|
suffixText: translate(LocalizationKeys.general_suffixes_mins),
|
2022-03-21 00:07:29 +00:00
|
|
|
),
|
|
|
|
controller: _delayController,
|
|
|
|
onChanged: (value) => setState(() {}),
|
|
|
|
keyboardType: const TextInputType.numberWithOptions(),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
Expanded(
|
|
|
|
child: Slider(
|
|
|
|
label: '${_delayPercentage.floor().toString()}%',
|
|
|
|
divisions: 100,
|
|
|
|
value: _delayPercentage,
|
|
|
|
min: 0,
|
|
|
|
max: 100,
|
|
|
|
onChanged: (value) {
|
|
|
|
updateDelayedRatio(percentageUpdate: value);
|
|
|
|
},
|
|
|
|
),
|
|
|
|
),
|
|
|
|
const Text('%', textScaleFactor: 1.5),
|
|
|
|
],
|
|
|
|
),
|
|
|
|
Row(
|
|
|
|
children: (int.tryParse(_delayController.text) ?? 0) != 0
|
|
|
|
? [
|
|
|
|
Expanded(
|
|
|
|
child: Padding(
|
|
|
|
padding: const EdgeInsets.only(right: 5.0),
|
|
|
|
child: NumberFormField(
|
2022-07-08 02:08:50 +00:00
|
|
|
label: translate(LocalizationKeys.log_detail_tabs_bolus_detail_fields_immediateBolus),
|
|
|
|
suffix: translate(LocalizationKeys.general_suffixes_units),
|
2022-03-21 00:07:29 +00:00
|
|
|
controller: _immediateUnitsController,
|
|
|
|
max: double.tryParse(_unitsController.text),
|
|
|
|
step: Settings.insulinSteps,
|
|
|
|
readOnly: true,
|
|
|
|
onChanged: (value) => updateDelayedRatio(
|
|
|
|
immediateUnitsUpdate: value),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
Expanded(
|
|
|
|
child: Padding(
|
|
|
|
padding: const EdgeInsets.only(left: 5.0),
|
|
|
|
child: NumberFormField(
|
2022-07-08 02:08:50 +00:00
|
|
|
label: translate(LocalizationKeys.log_detail_tabs_bolus_detail_fields_delayedBolus),
|
|
|
|
suffix: translate(LocalizationKeys.general_suffixes_units),
|
2022-03-21 00:07:29 +00:00
|
|
|
controller: _delayedUnitsController,
|
|
|
|
max: double.tryParse(_unitsController.text),
|
|
|
|
step: Settings.insulinSteps,
|
|
|
|
readOnly: true,
|
|
|
|
onChanged: (value) => {
|
|
|
|
updateDelayedRatio(
|
|
|
|
delayedUnitsUpdate: value),
|
|
|
|
}),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
]
|
|
|
|
: [],
|
|
|
|
),
|
|
|
|
TextFormField(
|
|
|
|
controller: _notesController,
|
|
|
|
decoration: const InputDecoration(
|
|
|
|
labelText: 'Notes',
|
|
|
|
),
|
|
|
|
keyboardType: TextInputType.multiline,
|
|
|
|
minLines: 2,
|
|
|
|
maxLines: 5,
|
|
|
|
),
|
|
|
|
],
|
|
|
|
),
|
|
|
|
],
|
|
|
|
),
|
|
|
|
),
|
|
|
|
),
|
|
|
|
bottomNavigationBar: DetailBottomRow(
|
|
|
|
onCancel: handleCancelAction,
|
|
|
|
onAction: _isSaving ? null : handleSaveAction,
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|