import 'package:diameter/components/detail.dart'; import 'package:diameter/components/dialogs.dart'; import 'package:diameter/components/forms.dart'; import 'package:diameter/config.dart'; import 'package:diameter/models/accuracy.dart'; import 'package:diameter/models/log_entry.dart'; import 'package:diameter/models/log_meal.dart'; import 'package:diameter/models/meal.dart'; import 'package:diameter/models/meal_category.dart'; import 'package:diameter/models/meal_portion_type.dart'; import 'package:diameter/models/meal_source.dart'; import 'package:diameter/navigation.dart'; import 'package:diameter/settings.dart'; import 'package:diameter/utils/utils.dart'; import 'package:flutter/material.dart'; class LogMealDetailScreen extends StatefulWidget { static const String routeName = '/log-meal'; final LogEntry logEntry; final LogMeal? logMeal; const LogMealDetailScreen({Key? key, required this.logEntry, this.logMeal}) : super(key: key); @override _LogMealDetailScreenState createState() => _LogMealDetailScreenState(); } class _LogMealDetailScreenState extends State { final GlobalKey _logMealForm = GlobalKey(); final _valueController = TextEditingController(text: ''); final _carbsRatioController = TextEditingController(text: ''); final _portionSizeController = TextEditingController(text: ''); final _carbsPerPortionController = TextEditingController(text: ''); final _bolusController = TextEditingController(text: ''); final _delayedBolusRateController = TextEditingController(text: ''); final _delayedBolusDurationController = TextEditingController(text: ''); final _notesController = TextEditingController(text: ''); Meal? _meal; MealSource? _mealSource; MealCategory? _mealCategory; MealPortionType? _mealPortionType; Accuracy? _portionSizeAccuracy; Accuracy? _carbsRatioAccuracy; List _meals = []; List _mealCategories = []; List _mealPortionTypes = []; List _mealSources = []; List _portionSizeAccuracies = []; List _carbsRatioAccuracies = []; bool _isSaving = false; @override void initState() { super.initState(); _portionSizeAccuracies = Accuracy.getAllForPortionSize(); _carbsRatioAccuracies = Accuracy.getAllForCarbsRatio(); _meals = Meal.getAll(); _mealCategories = MealCategory.getAll(); _mealPortionTypes = MealPortionType.getAll(); _mealSources = MealSource.getAll(); if (widget.logMeal != null) { _valueController.text = widget.logMeal!.value; _carbsRatioController.text = (widget.logMeal!.carbsRatio ?? '').toString(); _portionSizeController.text = (widget.logMeal!.portionSize ?? '').toString(); _carbsPerPortionController.text = (widget.logMeal!.carbsPerPortion ?? '').toString(); _bolusController.text = (widget.logMeal!.bolus ?? '').toString(); _delayedBolusRateController.text = (widget.logMeal!.delayedBolusRate ?? '').toString(); _delayedBolusDurationController.text = (widget.logMeal!.delayedBolusDuration ?? '').toString(); _notesController.text = widget.logMeal!.notes ?? ''; // _meal = widget.logMeal!.meal; // _source = widget.logMeal!.source; // _category = widget.logMeal!.category; // _portionType = widget.logMeal!.portionType; // _portionSizeAccuracy = _portionSizeAccuracies.firstWhere((element) => // element.id == // int.tryParse(widget.logMeal!.portionSizeAccuracy ?? '')); // _carbsRatioAccuracy = _carbsRatioAccuracies.firstWhere((element) => // element.id == int.tryParse(widget.logMeal!.carbsRatioAccuracy ?? '')); // _portionSizeAccuracy = widget.meal!.portionSizeAccuracy; // _carbsRatioAccuracy = widget.meal!.carbsRatioAccuracy; } } Future onSelectMeal(Meal meal) async { setState(() { _meal = meal; _valueController.text = meal.value; if (meal.carbsRatio != null) { _carbsRatioController.text = meal.carbsRatio.toString(); } if (meal.portionSize != null) { _portionSizeController.text = meal.portionSize.toString(); } if (meal.carbsPerPortion != null) { _carbsPerPortionController.text = meal.carbsPerPortion.toString(); } if (meal.delayedBolusRate != null) { _delayedBolusRateController.text = meal.delayedBolusRate.toString(); } if (meal.delayedBolusDuration != null) { _delayedBolusDurationController.text = meal.delayedBolusDuration.toString(); } if (meal.mealSource.hasValue) { _mealSource = meal.mealSource.target; } if (meal.mealCategory.hasValue) { _mealCategory = meal.mealCategory.target; } if (meal.mealPortionType.hasValue) { _mealPortionType = meal.mealPortionType.target; } if (meal.portionSizeAccuracy.hasValue) { _portionSizeAccuracy = meal.portionSizeAccuracy.target; } if (meal.carbsRatioAccuracy.hasValue) { _carbsRatioAccuracy = meal.carbsRatioAccuracy.target; } }); } void handleSaveAction() async { setState(() { _isSaving = true; }); if (_logMealForm.currentState!.validate()) { bool isNew = widget.logMeal == null; // isNew // ? await LogMeal.save( // logEntry: widget.logEntry.objectId!, // meal: _meal, // value: _valueController.text, // source: _mealSource, // category: _category, // portionType: _portionType, // carbsRatio: double.tryParse(_carbsRatioController.text), // portionSize: double.tryParse(_portionSizeController.text), // carbsPerPortion: double.tryParse(_carbsPerPortionController.text), // // portionSizeAccuracy: _portionSizeAccuracy, // // carbsRatioAccuracy: _carbsRatioAccuracy, // portionSizeAccuracy: _portionSizeAccuracy?.id.toString(), // carbsRatioAccuracy: _carbsRatioAccuracy?.id.toString(), // bolus: double.tryParse(_bolusController.text), // delayedBolusDuration: // int.tryParse(_delayedBolusDurationController.text), // delayedBolusRate: // double.tryParse(_delayedBolusRateController.text), // notes: _notesController.text, // ) // : await LogMeal.update( // widget.logMeal!.objectId!, // meal: _meal, // value: _valueController.text, // source: _mealSource, // category: _category, // portionType: _portionType, // carbsRatio: double.tryParse(_carbsRatioController.text), // portionSize: double.tryParse(_portionSizeController.text), // carbsPerPortion: double.tryParse(_carbsPerPortionController.text), // // portionSizeAccuracy: _portionSizeAccuracy, // // carbsRatioAccuracy: _carbsRatioAccuracy, // portionSizeAccuracy: _portionSizeAccuracy?.id.toString(), // carbsRatioAccuracy: _carbsRatioAccuracy?.id.toString(), // bolus: double.tryParse(_bolusController.text), // delayedBolusDuration: // int.tryParse(_delayedBolusDurationController.text), // delayedBolusRate: // double.tryParse(_delayedBolusRateController.text), // notes: _notesController.text, // ); LogMeal logMeal = LogMeal( id: widget.logMeal?.id ?? 0, value: _valueController.text, carbsRatio: double.tryParse(_carbsRatioController.text), portionSize: double.tryParse(_portionSizeController.text), carbsPerPortion: double.tryParse(_carbsPerPortionController.text), bolus: double.tryParse(_bolusController.text), delayedBolusDuration: int.tryParse(_delayedBolusDurationController.text), delayedBolusRate: double.tryParse(_delayedBolusRateController.text), notes: _notesController.text, ); logMeal.meal.target = _meal; logMeal.mealSource.target = _mealSource; logMeal.mealCategory.target = _mealCategory; logMeal.mealPortionType.target = _mealPortionType; logMeal.portionSizeAccuracy.target = _portionSizeAccuracy; logMeal.carbsRatioAccuracy.target = _carbsRatioAccuracy; LogMeal.put(logMeal); Navigator.pop(context, '${isNew ? 'New' : ''} Meal Saved'); } setState(() { _isSaving = false; }); } void handleCancelAction() { bool isNew = widget.logMeal == null; if (showConfirmationDialogOnCancel && ((isNew && (_valueController.text != '' || _meal != null || _mealSource != null || _mealCategory != null || _mealPortionType != null || double.tryParse(_carbsRatioController.text) != null || double.tryParse(_portionSizeController.text) != null || double.tryParse(_carbsPerPortionController.text) != null || _carbsRatioAccuracy != null || _portionSizeAccuracy != null || double.tryParse(_bolusController.text) != null || int.tryParse(_delayedBolusDurationController.text) != null || double.tryParse(_delayedBolusRateController.text) != null || _notesController.text != '')) || (!isNew && (_valueController.text != widget.logMeal!.value || _meal != widget.logMeal!.meal.target || _mealSource != widget.logMeal!.mealSource.target || _mealCategory != widget.logMeal!.mealCategory.target || _mealPortionType != widget.logMeal!.mealPortionType.target || double.tryParse(_carbsRatioController.text) != widget.logMeal!.carbsRatio || double.tryParse(_portionSizeController.text) != widget.logMeal!.portionSize || double.tryParse(_carbsPerPortionController.text) != widget.logMeal!.carbsPerPortion || // _carbsRatioAccuracy != widget.logMeal!.carbsRatioAccuracy || // _portionSizeAccuracy != // widget.logMeal!.portionSizeAccuracy || _carbsRatioAccuracy != widget.logMeal!.carbsRatioAccuracy.target || _portionSizeAccuracy != widget.logMeal!.portionSizeAccuracy.target || double.tryParse(_bolusController.text) != widget.logMeal!.bolus || int.tryParse(_delayedBolusDurationController.text) != widget.logMeal!.delayedBolusDuration || double.tryParse(_delayedBolusRateController.text) != widget.logMeal!.delayedBolusRate || _notesController.text != (widget.logMeal!.notes ?? ''))))) { Dialogs.showCancelConfirmationDialog( context: context, isNew: isNew, onSave: handleSaveAction, ); } else { Navigator.pop(context); } } void calculateThirdMeasurementOfPortionCarbsRelation( {PortionCarbsParameter? parameterToBeCalculated}) { double? carbsRatio; double? portionSize; double? carbsPerPortion; if (parameterToBeCalculated != PortionCarbsParameter.carbsRatio && _carbsRatioController.text != '') { carbsRatio = double.tryParse(_carbsRatioController.text); } if (parameterToBeCalculated != PortionCarbsParameter.portionSize && _portionSizeController.text != '') { portionSize = double.tryParse(_portionSizeController.text); } if (parameterToBeCalculated != PortionCarbsParameter.carbsPerPortion && _carbsRatioController.text != '') { carbsPerPortion = double.tryParse(_carbsPerPortionController.text); } if (carbsRatio != null && portionSize != null && carbsPerPortion == null) { setState(() { _carbsPerPortionController.text = Utils.calculateCarbsPerPortion(carbsRatio!, portionSize!) .toString(); }); } if (carbsRatio == null && portionSize != null && carbsPerPortion != null) { setState(() { _carbsRatioController.text = Utils.calculateCarbsRatio(carbsPerPortion!, portionSize!) .toString(); }); } if (carbsRatio != null && portionSize == null && carbsPerPortion != null) { setState(() { _portionSizeController.text = Utils.calculatePortionSize(carbsRatio!, carbsPerPortion!) .toString(); }); } } @override Widget build(BuildContext context) { bool isNew = widget.logMeal == null; return Scaffold( appBar: AppBar( title: Text(isNew ? 'New Meal' : widget.logMeal!.value), ), drawer: const Navigation(currentLocation: LogMealDetailScreen.routeName), body: SingleChildScrollView( child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ FormWrapper( formState: _logMealForm, fields: [ TextFormField( controller: _valueController, decoration: const InputDecoration( labelText: 'Name', ), validator: (value) { if (value!.trim().isEmpty) { return 'Empty name'; } return null; }, ), LabeledDropdownButton( selectedItem: _meal, label: 'Meal', items: _meals, // getItemValue: (item) => item.objectId, renderItem: (item) => Text(item.value), onChanged: (value) { if (value != null) { onSelectMeal(value); } }, ), LabeledDropdownButton( selectedItem: _mealSource, label: 'Meal Source', items: _mealSources, // getItemValue: (item) => item.objectId, renderItem: (item) => Text(item.value), onChanged: (value) { setState(() { _mealSource = value; }); }, ), LabeledDropdownButton( selectedItem: _mealCategory, label: 'Meal Category', items: _mealCategories, // getItemValue: (item) => item.objectId, renderItem: (item) => Text(item.value), onChanged: (value) { setState(() { _mealCategory = value; }); }, ), LabeledDropdownButton( selectedItem: _mealPortionType, label: 'Meal Portion Type', items: _mealPortionTypes, // getItemValue: (item) => item.objectId, renderItem: (item) => Text(item.value), onChanged: (value) { setState(() { _mealPortionType = value; }); }, ), Row( children: [ Expanded( child: TextFormField( decoration: const InputDecoration( labelText: 'Carbs ratio', suffixText: '%', ), controller: _carbsRatioController, keyboardType: const TextInputType.numberWithOptions( decimal: true), onChanged: (_) => calculateThirdMeasurementOfPortionCarbsRelation(), ), ), IconButton( onPressed: () => calculateThirdMeasurementOfPortionCarbsRelation( parameterToBeCalculated: PortionCarbsParameter.carbsRatio), icon: const Icon(Icons.calculate), ), ], ), Row( mainAxisSize: MainAxisSize.min, children: [ Expanded( child: TextFormField( decoration: InputDecoration( labelText: 'Portion size', suffixText: nutritionMeasurement == NutritionMeasurement.grams ? 'g' : nutritionMeasurement == NutritionMeasurement.ounces ? 'oz' : '', alignLabelWithHint: true, ), controller: _portionSizeController, keyboardType: const TextInputType.numberWithOptions( decimal: true), onChanged: (_) => calculateThirdMeasurementOfPortionCarbsRelation(), ), ), IconButton( onPressed: () => calculateThirdMeasurementOfPortionCarbsRelation( parameterToBeCalculated: PortionCarbsParameter.portionSize), icon: const Icon(Icons.calculate), ), ], ), LabeledDropdownButton( selectedItem: _portionSizeAccuracy, label: 'Portion Size Accuracy', items: _portionSizeAccuracies, // getItemValue: (item) => item.objectId, renderItem: (item) => Text(item.value), onChanged: (value) { setState(() { _portionSizeAccuracy = value; }); }, ), // StyledFutureDropdownButton( // selectedItem: _portionSizeAccuracy, // label: 'Portion Size Accuracy', // items: _portionSizeAccuracies, // getItemValue: (item) => item.objectId, // renderItem: (item) => Text(item.value), // onChanged: (value) { // setState(() { // _portionSizeAccuracy = value; // }); // }, // ), Row( children: [ Expanded( child: TextFormField( decoration: InputDecoration( labelText: 'Carbs per portion', suffixText: nutritionMeasurement == NutritionMeasurement.grams ? 'g' : nutritionMeasurement == NutritionMeasurement.ounces ? 'oz' : '', ), controller: _carbsPerPortionController, keyboardType: const TextInputType.numberWithOptions( decimal: true), onChanged: (_) => calculateThirdMeasurementOfPortionCarbsRelation(), ), ), IconButton( onPressed: () => calculateThirdMeasurementOfPortionCarbsRelation( parameterToBeCalculated: PortionCarbsParameter.carbsPerPortion), icon: const Icon(Icons.calculate), ), ], ), LabeledDropdownButton( selectedItem: _carbsRatioAccuracy, label: 'Carbs Ratio Accuracy', items: _carbsRatioAccuracies, // getItemValue: (item) => item.objectId, renderItem: (item) => Text(item.value), onChanged: (value) { setState(() { _carbsRatioAccuracy = value; }); }, ), // StyledFutureDropdownButton( // selectedItem: _carbsRatioAccuracy, // label: 'Carbs Ratio Accuracy', // items: _carbsRatioAccuracies, // getItemValue: (item) => item.objectId, // renderItem: (item) => Text(item.value), // onChanged: (value) { // setState(() { // _carbsRatioAccuracy = value; // }); // }, // ), TextFormField( decoration: const InputDecoration( labelText: 'Bolus Units', suffixText: ' U', ), controller: _bolusController, keyboardType: const TextInputType.numberWithOptions(decimal: true), ), TextFormField( decoration: const InputDecoration( labelText: 'Delayed Bolus Duration', suffixText: ' min', ), controller: _delayedBolusDurationController, keyboardType: const TextInputType.numberWithOptions(), ), TextFormField( decoration: const InputDecoration( labelText: 'Delayed Bolus Units', suffixText: ' U', alignLabelWithHint: true, ), controller: _delayedBolusRateController, keyboardType: const TextInputType.numberWithOptions(decimal: true), ), TextFormField( controller: _notesController, decoration: const InputDecoration( labelText: 'Notes', alignLabelWithHint: true, ), keyboardType: TextInputType.multiline, ), ], ), ], ), ), bottomNavigationBar: DetailBottomRow( onCancel: handleCancelAction, onSave: _isSaving ? null : handleSaveAction, ), ); } }