usability tweaks, number form field component, amount for log meals, models for recipe and ingredients

This commit is contained in:
spinel 2021-12-11 02:30:46 +01:00
parent 575130aba0
commit 5e60ea09ce
14 changed files with 775 additions and 202 deletions

35
TODO
View File

@ -1,10 +1,5 @@
BUGFIXES:
MAIN TASKS:
Layout:
✔ 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
@ -12,17 +7,15 @@ MAIN TASKS:
☐ 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 @done(21-12-10 06:12)
Basal/Bolus:
✔ 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)
Recipe:
✔ add model for recipe @done(21-12-11 02:23)
✔ add model for ingredient (relation betweeen recipe and meal) @done(21-12-11 02:23)
☐ recipe list screen
☐ recipe detail screen
☐ add functionality to create a meal from a recipe
Log Entry:
✔ 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 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)
✔ give option to specify quantity @done(21-12-11 01:28)
✔ give option to pick meal from a different log entry (that doesn't have an associated bolus yet and within certain time span) @done(21-12-11 02:22)
Event Types:
☐ add colors as indicators for log entries (and later graphs in reports)
Settings:
@ -38,11 +31,10 @@ FUTURE TASKS:
General/Framework:
☐ setup objectbox sync server
☐ add explanations to each section
☐ find a better way to work with multiple glucose measurements (or disable it?)
✔ find a better way to work with multiple glucose measurements @done(21-12-11 02:23)
☐ evaluate if some fields should be readonly instead of completely hidden
☐ alternate languages
☐ log hba1c
☐ add recipe calculator
Reports:
☐ evaluate what type of reports there should be
Log Overview:
@ -58,6 +50,15 @@ FUTURE TASKS:
☐ option to switch theme
Archive:
✔ make components rounder/nicer/closer to new material style @done(21-12-10 04:10) @project(MAIN TASKS.Layout)
✔ make sure 'null' isn't shown in text fields @done(21-12-10 04:23) @project(MAIN TASKS.General/Framework)
✔ hide details like accuracies etc when picking meals @done(21-12-10 06:12) @project(MAIN TASKS.General/Framework)
✔ add save and close and next buttons on rate creations @done(21-12-10 06:12) @project(MAIN TASKS.Basal/Bolus)
✔ always calculate other glucose measurement from active one and make other one readonly @done(21-12-10 04:33) @project(MAIN TASKS.Basal/Bolus)
✔ add save and close button @done(21-12-10 06:11) @project(MAIN TASKS.Log Entry)
✔ move on to newly created entry after saving @done(21-12-10 06:11) @project(MAIN TASKS.Log Entry)
✔ recalculate bolus upon deactivating 'set manually' option @done(21-12-10 06:18) @project(MAIN TASKS.Log Entry)
✔ account for delayed percentage setting on choosing meals @done(21-12-10 06:39) @project(MAIN TASKS.Log Entry)
✔ fix preloading of dropdown values (appear blank at first as of now) @done(21-12-09 05:31) @project(BUGFIXES.General/Framework)
✔ glucose target isn't displayed correctly anymore @done(21-12-09 05:31) @project(BUGFIXES.Log Entry)
✔ hide dropdown overlay on tapping anywhere else (especially menu) @done(21-12-07 21:04) @project(MAIN TASKS.General/Framework)

View File

@ -23,11 +23,11 @@ class _FormWrapperState extends State<FormWrapper> {
children: [
Column(
children: widget.fields
?.map((e) => Padding(
padding: const EdgeInsets.symmetric(vertical: 5.0),
child: e))
.toList() ??
[],
?.map((e) => Padding(
padding: const EdgeInsets.symmetric(vertical: 5.0),
child: e))
.toList() ??
[],
),
Container(
padding: const EdgeInsets.only(top: 10.0),
@ -164,3 +164,76 @@ class _TimeOfDayFormFieldState extends State<TimeOfDayFormField> {
);
}
}
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),
),
],
);
}
}

View File

@ -0,0 +1,33 @@
import 'package:diameter/main.dart';
import 'package:diameter/models/meal.dart';
import 'package:diameter/models/recipe.dart';
import 'package:objectbox/objectbox.dart';
import 'package:diameter/objectbox.g.dart' show Ingredient_, Recipe_;
@Entity(uid: 6950311793136068892)
@Sync()
class Ingredient {
static final Box<Ingredient> box = objectBox.store.box<Ingredient>();
// properties
int id;
bool deleted;
double amount;
// relations
final recipe = ToOne<Recipe>();
final ingredient = ToOne<Meal>();
// constructor
Ingredient({
this.id = 0,
this.deleted = false,
required this.amount,
});
static List<Ingredient> getAllForRecipe(int id) {
QueryBuilder<Ingredient> builder = box.query(Ingredient_.deleted.equals(false));
builder.link(Ingredient_.recipe, Recipe_.id.equals(id));
return builder.build().find();
}
}

View File

@ -3,7 +3,7 @@ import 'package:diameter/models/bolus.dart';
import 'package:diameter/models/log_entry.dart';
import 'package:diameter/models/log_meal.dart';
import 'package:objectbox/objectbox.dart';
import 'package:diameter/objectbox.g.dart' show LogBolus_, LogEntry_;
import 'package:diameter/objectbox.g.dart' show LogBolus_, LogEntry_, LogMeal_;
@Entity(uid: 8033487006694871160)
@Sync()
@ -67,7 +67,14 @@ class LogBolus {
QueryBuilder<LogBolus> builder = box.query(LogBolus_.deleted
.equals(false)
.and(LogBolus_.mgPerDlCorrection.notNull()));
builder.link(LogBolus_.logEntry, LogEntry_.id.equals(id));
builder.link(LogBolus_.meal, LogMeal_.id.equals(id));
return builder.build().find().isNotEmpty;
}
static bool bolusForMealExists(int id) {
QueryBuilder<LogBolus> builder = box.query(LogBolus_.deleted
.equals(false));
builder.link(LogBolus_.meal, LogMeal_.id.equals(id));
return builder.build().find().isNotEmpty;
}

View File

@ -1,4 +1,5 @@
import 'package:diameter/main.dart';
import 'package:diameter/models/log_bolus.dart';
import 'package:diameter/models/log_entry.dart';
import 'package:diameter/models/meal.dart';
import 'package:diameter/models/meal_category.dart';
@ -19,9 +20,10 @@ class LogMeal {
String value;
double? carbsRatio;
double? portionSize;
double? carbsPerPortion;
double? totalCarbs;
String? notes;
double? bolus;
double amount;
// relations
final logEntry = ToOne<LogEntry>();
@ -37,33 +39,47 @@ class LogMeal {
this.id = 0,
this.deleted = false,
this.value = '',
this.amount = 1,
this.carbsRatio,
this.portionSize,
this.carbsPerPortion,
this.totalCarbs,
this.notes,
});
// methods
static LogMeal? get(int id) => box.get(id);
static void put(LogMeal logMeal) => box.put(logMeal);
static void remove(int id) {
static void remove(int id) {
final item = box.get(id);
if (item != null) {
item.deleted = true;
box.put(item);
}
}
static List<LogMeal> getAllForEntry(int id) {
QueryBuilder<LogMeal> builder = box.query(LogMeal_.deleted.equals(false));
builder.link(LogMeal_.logEntry, LogEntry_.id.equals(id));
return builder.build().find();
}
static List<LogMeal> getRecentWithoutBolus(int id) {
final dateTime = LogEntry.get(id)?.time ?? DateTime.now();
QueryBuilder<LogMeal> builder = box.query(LogMeal_.deleted.equals(false));
builder.link(LogMeal_.logEntry);
List<LogMeal> results = builder.build().find();
results.retainWhere((logMeal) {
final entryTime = logMeal.logEntry.target!.time;
return entryTime.isAfter(dateTime.subtract(const Duration(hours: 12))) &&
entryTime.isBefore(dateTime.add(const Duration(hours: 12))) && !LogBolus.bolusForMealExists(logMeal.id);
});
return results;
}
static double getTotalCarbsForEntry(int id) {
QueryBuilder<LogMeal> builder = box.query(LogMeal_.deleted.equals(false));
builder.link(LogMeal_.logEntry, LogEntry_.id.equals(id));
return builder.build().property(LogMeal_.carbsPerPortion).sum();
return builder.build().property(LogMeal_.totalCarbs).sum();
}
@override

59
lib/models/recipe.dart Normal file
View File

@ -0,0 +1,59 @@
import 'package:diameter/main.dart';
import 'package:diameter/models/meal.dart';
import 'package:objectbox/objectbox.dart';
import 'package:diameter/objectbox.g.dart' show Recipe_;
@Entity(uid: 6497942314956341514)
@Sync()
class Recipe {
static final Box<Recipe> box = objectBox.store.box<Recipe>();
// properties
int id;
bool deleted;
String name;
double? carbsRatio;
double? portionSize;
double? carbsPerPortion;
int? delayedBolusDuration;
double? delayedBolusPercentage;
String? notes;
// relations
final portion = ToOne<Meal>();
// constructor
Recipe({
this.id = 0,
this.deleted = false,
this.name = '',
this.carbsRatio,
this.portionSize,
this.carbsPerPortion,
this.delayedBolusDuration,
this.delayedBolusPercentage,
this.notes,
});
// methods
static Recipe? get(int id) => box.get(id);
static void put(Recipe recipe) => box.put(recipe);
static List<Recipe> getAll() {
QueryBuilder<Recipe> builder = box.query(Recipe_.deleted.equals(false))..order(Recipe_.name);
return builder.build().find();
}
static void remove(int id) {
final item = box.get(id);
if (item != null) {
item.deleted = true;
box.put(item);
}
}
@override
String toString() {
return name;
}
}

View File

@ -346,7 +346,7 @@
},
{
"id": "9:411177866700467286",
"lastPropertyId": "17:7341439841011629937",
"lastPropertyId": "19:8965198821438347033",
"name": "LogMeal",
"flags": 2,
"properties": [
@ -371,11 +371,6 @@
"name": "portionSize",
"type": 8
},
{
"id": "5:2215708755581938580",
"name": "carbsPerPortion",
"type": 8
},
{
"id": "6:8074052538574863399",
"name": "bolus",
@ -446,6 +441,16 @@
"id": "17:7341439841011629937",
"name": "deleted",
"type": 1
},
{
"id": "18:7405129785654054238",
"name": "amount",
"type": 8
},
{
"id": "19:8965198821438347033",
"name": "totalCarbs",
"type": 8
}
],
"relations": []
@ -935,10 +940,114 @@
}
],
"relations": []
},
{
"id": "18:6497942314956341514",
"lastPropertyId": "10:4370359747396560337",
"name": "Recipe",
"flags": 2,
"properties": [
{
"id": "1:6426741154282018946",
"name": "id",
"type": 6,
"flags": 1
},
{
"id": "2:1167304402395485629",
"name": "deleted",
"type": 1
},
{
"id": "3:1244733840071626966",
"name": "name",
"type": 9
},
{
"id": "4:241621230513128588",
"name": "carbsRatio",
"type": 8
},
{
"id": "5:4678123663117222609",
"name": "portionSize",
"type": 8
},
{
"id": "6:780211923138281722",
"name": "carbsPerPortion",
"type": 8
},
{
"id": "7:763575433624979013",
"name": "delayedBolusDuration",
"type": 6
},
{
"id": "8:1225271130099322691",
"name": "delayedBolusPercentage",
"type": 8
},
{
"id": "9:8593446427752839266",
"name": "notes",
"type": 9
},
{
"id": "10:4370359747396560337",
"name": "portionId",
"type": 11,
"flags": 520,
"indexId": "29:5110151182694376118",
"relationTarget": "Meal"
}
],
"relations": []
},
{
"id": "19:6950311793136068892",
"lastPropertyId": "5:6495065881132428893",
"name": "Ingredient",
"flags": 2,
"properties": [
{
"id": "1:7766569281758551418",
"name": "id",
"type": 6,
"flags": 1
},
{
"id": "2:3830559702655088692",
"name": "deleted",
"type": 1
},
{
"id": "3:602057803225843875",
"name": "amount",
"type": 8
},
{
"id": "4:26686399245586953",
"name": "recipeId",
"type": 11,
"flags": 520,
"indexId": "30:5492781242713788590",
"relationTarget": "Recipe"
},
{
"id": "5:6495065881132428893",
"name": "ingredientId",
"type": 11,
"flags": 520,
"indexId": "31:3277019237664417023",
"relationTarget": "Meal"
}
],
"relations": []
}
],
"lastEntityId": "17:5041265995704044399",
"lastIndexId": "28:4563029809754152081",
"lastEntityId": "19:6950311793136068892",
"lastIndexId": "31:3277019237664417023",
"lastRelationId": "0:0",
"lastSequenceId": "0:0",
"modelVersion": 5,
@ -975,7 +1084,8 @@
7638848982383620744,
3282706593658092097,
596980591281311896,
3633551763915044903
3633551763915044903,
2215708755581938580
],
"retiredRelationUids": [],
"version": 1

View File

@ -15,6 +15,7 @@ import 'models/basal_profile.dart';
import 'models/bolus.dart';
import 'models/bolus_profile.dart';
import 'models/glucose_target.dart';
import 'models/ingredient.dart';
import 'models/log_bolus.dart';
import 'models/log_entry.dart';
import 'models/log_event.dart';
@ -24,6 +25,7 @@ import 'models/meal.dart';
import 'models/meal_category.dart';
import 'models/meal_portion_type.dart';
import 'models/meal_source.dart';
import 'models/recipe.dart';
import 'models/settings.dart';
export 'package:objectbox/objectbox.dart'; // so that callers only have to import this file
@ -359,7 +361,7 @@ final _entities = <ModelEntity>[
ModelEntity(
id: const IdUid(9, 411177866700467286),
name: 'LogMeal',
lastPropertyId: const IdUid(17, 7341439841011629937),
lastPropertyId: const IdUid(19, 8965198821438347033),
flags: 2,
properties: <ModelProperty>[
ModelProperty(
@ -382,11 +384,6 @@ final _entities = <ModelEntity>[
name: 'portionSize',
type: 8,
flags: 0),
ModelProperty(
id: const IdUid(5, 2215708755581938580),
name: 'carbsPerPortion',
type: 8,
flags: 0),
ModelProperty(
id: const IdUid(6, 8074052538574863399),
name: 'bolus',
@ -450,6 +447,16 @@ final _entities = <ModelEntity>[
id: const IdUid(17, 7341439841011629937),
name: 'deleted',
type: 1,
flags: 0),
ModelProperty(
id: const IdUid(18, 7405129785654054238),
name: 'amount',
type: 8,
flags: 0),
ModelProperty(
id: const IdUid(19, 8965198821438347033),
name: 'totalCarbs',
type: 8,
flags: 0)
],
relations: <ModelRelation>[],
@ -919,6 +926,105 @@ final _entities = <ModelEntity>[
flags: 0)
],
relations: <ModelRelation>[],
backlinks: <ModelBacklink>[]),
ModelEntity(
id: const IdUid(18, 6497942314956341514),
name: 'Recipe',
lastPropertyId: const IdUid(10, 4370359747396560337),
flags: 2,
properties: <ModelProperty>[
ModelProperty(
id: const IdUid(1, 6426741154282018946),
name: 'id',
type: 6,
flags: 1),
ModelProperty(
id: const IdUid(2, 1167304402395485629),
name: 'deleted',
type: 1,
flags: 0),
ModelProperty(
id: const IdUid(3, 1244733840071626966),
name: 'name',
type: 9,
flags: 0),
ModelProperty(
id: const IdUid(4, 241621230513128588),
name: 'carbsRatio',
type: 8,
flags: 0),
ModelProperty(
id: const IdUid(5, 4678123663117222609),
name: 'portionSize',
type: 8,
flags: 0),
ModelProperty(
id: const IdUid(6, 780211923138281722),
name: 'carbsPerPortion',
type: 8,
flags: 0),
ModelProperty(
id: const IdUid(7, 763575433624979013),
name: 'delayedBolusDuration',
type: 6,
flags: 0),
ModelProperty(
id: const IdUid(8, 1225271130099322691),
name: 'delayedBolusPercentage',
type: 8,
flags: 0),
ModelProperty(
id: const IdUid(9, 8593446427752839266),
name: 'notes',
type: 9,
flags: 0),
ModelProperty(
id: const IdUid(10, 4370359747396560337),
name: 'portionId',
type: 11,
flags: 520,
indexId: const IdUid(29, 5110151182694376118),
relationTarget: 'Meal')
],
relations: <ModelRelation>[],
backlinks: <ModelBacklink>[]),
ModelEntity(
id: const IdUid(19, 6950311793136068892),
name: 'Ingredient',
lastPropertyId: const IdUid(5, 6495065881132428893),
flags: 2,
properties: <ModelProperty>[
ModelProperty(
id: const IdUid(1, 7766569281758551418),
name: 'id',
type: 6,
flags: 1),
ModelProperty(
id: const IdUid(2, 3830559702655088692),
name: 'deleted',
type: 1,
flags: 0),
ModelProperty(
id: const IdUid(3, 602057803225843875),
name: 'amount',
type: 8,
flags: 0),
ModelProperty(
id: const IdUid(4, 26686399245586953),
name: 'recipeId',
type: 11,
flags: 520,
indexId: const IdUid(30, 5492781242713788590),
relationTarget: 'Recipe'),
ModelProperty(
id: const IdUid(5, 6495065881132428893),
name: 'ingredientId',
type: 11,
flags: 520,
indexId: const IdUid(31, 3277019237664417023),
relationTarget: 'Meal')
],
relations: <ModelRelation>[],
backlinks: <ModelBacklink>[])
];
@ -942,8 +1048,8 @@ Future<Store> openStore(
ModelDefinition getObjectBoxModel() {
final model = ModelInfo(
entities: _entities,
lastEntityId: const IdUid(17, 5041265995704044399),
lastIndexId: const IdUid(28, 4563029809754152081),
lastEntityId: const IdUid(19, 6950311793136068892),
lastIndexId: const IdUid(31, 3277019237664417023),
lastRelationId: const IdUid(0, 0),
lastSequenceId: const IdUid(0, 0),
retiredEntityUids: const [3095978685310268382],
@ -973,7 +1079,8 @@ ModelDefinition getObjectBoxModel() {
7638848982383620744,
3282706593658092097,
596980591281311896,
3633551763915044903
3633551763915044903,
2215708755581938580
],
retiredRelationUids: const [],
modelVersion: 5,
@ -1311,12 +1418,11 @@ ModelDefinition getObjectBoxModel() {
final valueOffset = fbb.writeString(object.value);
final notesOffset =
object.notes == null ? null : fbb.writeString(object.notes!);
fbb.startTable(18);
fbb.startTable(20);
fbb.addInt64(0, object.id);
fbb.addOffset(1, valueOffset);
fbb.addFloat64(2, object.carbsRatio);
fbb.addFloat64(3, object.portionSize);
fbb.addFloat64(4, object.carbsPerPortion);
fbb.addFloat64(5, object.bolus);
fbb.addOffset(8, notesOffset);
fbb.addInt64(9, object.logEntry.targetId);
@ -1327,6 +1433,8 @@ ModelDefinition getObjectBoxModel() {
fbb.addInt64(14, object.portionSizeAccuracy.targetId);
fbb.addInt64(15, object.carbsRatioAccuracy.targetId);
fbb.addBool(16, object.deleted);
fbb.addFloat64(17, object.amount);
fbb.addFloat64(18, object.totalCarbs);
fbb.finish(fbb.endTable());
return object.id;
},
@ -1340,12 +1448,14 @@ ModelDefinition getObjectBoxModel() {
.vTableGet(buffer, rootOffset, 36, false),
value:
const fb.StringReader().vTableGet(buffer, rootOffset, 6, ''),
amount:
const fb.Float64Reader().vTableGet(buffer, rootOffset, 38, 0),
carbsRatio: const fb.Float64Reader()
.vTableGetNullable(buffer, rootOffset, 8),
portionSize: const fb.Float64Reader()
.vTableGetNullable(buffer, rootOffset, 10),
carbsPerPortion: const fb.Float64Reader()
.vTableGetNullable(buffer, rootOffset, 12),
totalCarbs: const fb.Float64Reader()
.vTableGetNullable(buffer, rootOffset, 40),
notes: const fb.StringReader()
.vTableGetNullable(buffer, rootOffset, 20))
..bolus = const fb.Float64Reader()
@ -1799,6 +1909,96 @@ ModelDefinition getObjectBoxModel() {
const fb.Int64Reader().vTableGet(buffer, rootOffset, 16, 0));
return object;
}),
Recipe: EntityDefinition<Recipe>(
model: _entities[16],
toOneRelations: (Recipe object) => [object.portion],
toManyRelations: (Recipe object) => {},
getId: (Recipe object) => object.id,
setId: (Recipe object, int id) {
object.id = id;
},
objectToFB: (Recipe object, fb.Builder fbb) {
final nameOffset = fbb.writeString(object.name);
final notesOffset =
object.notes == null ? null : fbb.writeString(object.notes!);
fbb.startTable(11);
fbb.addInt64(0, object.id);
fbb.addBool(1, object.deleted);
fbb.addOffset(2, nameOffset);
fbb.addFloat64(3, object.carbsRatio);
fbb.addFloat64(4, object.portionSize);
fbb.addFloat64(5, object.carbsPerPortion);
fbb.addInt64(6, object.delayedBolusDuration);
fbb.addFloat64(7, object.delayedBolusPercentage);
fbb.addOffset(8, notesOffset);
fbb.addInt64(9, object.portion.targetId);
fbb.finish(fbb.endTable());
return object.id;
},
objectFromFB: (Store store, ByteData fbData) {
final buffer = fb.BufferContext(fbData);
final rootOffset = buffer.derefObject(0);
final object = Recipe(
id: const fb.Int64Reader().vTableGet(buffer, rootOffset, 4, 0),
deleted:
const fb.BoolReader().vTableGet(buffer, rootOffset, 6, false),
name:
const fb.StringReader().vTableGet(buffer, rootOffset, 8, ''),
carbsRatio: const fb.Float64Reader()
.vTableGetNullable(buffer, rootOffset, 10),
portionSize: const fb.Float64Reader()
.vTableGetNullable(buffer, rootOffset, 12),
carbsPerPortion: const fb.Float64Reader()
.vTableGetNullable(buffer, rootOffset, 14),
delayedBolusDuration: const fb.Int64Reader()
.vTableGetNullable(buffer, rootOffset, 16),
delayedBolusPercentage: const fb.Float64Reader()
.vTableGetNullable(buffer, rootOffset, 18),
notes: const fb.StringReader()
.vTableGetNullable(buffer, rootOffset, 20));
object.portion.targetId =
const fb.Int64Reader().vTableGet(buffer, rootOffset, 22, 0);
object.portion.attach(store);
return object;
}),
Ingredient: EntityDefinition<Ingredient>(
model: _entities[17],
toOneRelations: (Ingredient object) =>
[object.recipe, object.ingredient],
toManyRelations: (Ingredient object) => {},
getId: (Ingredient object) => object.id,
setId: (Ingredient object, int id) {
object.id = id;
},
objectToFB: (Ingredient object, fb.Builder fbb) {
fbb.startTable(6);
fbb.addInt64(0, object.id);
fbb.addBool(1, object.deleted);
fbb.addFloat64(2, object.amount);
fbb.addInt64(3, object.recipe.targetId);
fbb.addInt64(4, object.ingredient.targetId);
fbb.finish(fbb.endTable());
return object.id;
},
objectFromFB: (Store store, ByteData fbData) {
final buffer = fb.BufferContext(fbData);
final rootOffset = buffer.derefObject(0);
final object = Ingredient(
id: const fb.Int64Reader().vTableGet(buffer, rootOffset, 4, 0),
deleted:
const fb.BoolReader().vTableGet(buffer, rootOffset, 6, false),
amount:
const fb.Float64Reader().vTableGet(buffer, rootOffset, 8, 0));
object.recipe.targetId =
const fb.Int64Reader().vTableGet(buffer, rootOffset, 10, 0);
object.recipe.attach(store);
object.ingredient.targetId =
const fb.Int64Reader().vTableGet(buffer, rootOffset, 12, 0);
object.ingredient.attach(store);
return object;
})
};
@ -2035,47 +2235,51 @@ class LogMeal_ {
static final portionSize =
QueryDoubleProperty<LogMeal>(_entities[7].properties[3]);
/// see [LogMeal.carbsPerPortion]
static final carbsPerPortion =
QueryDoubleProperty<LogMeal>(_entities[7].properties[4]);
/// see [LogMeal.bolus]
static final bolus = QueryDoubleProperty<LogMeal>(_entities[7].properties[5]);
static final bolus = QueryDoubleProperty<LogMeal>(_entities[7].properties[4]);
/// see [LogMeal.notes]
static final notes = QueryStringProperty<LogMeal>(_entities[7].properties[6]);
static final notes = QueryStringProperty<LogMeal>(_entities[7].properties[5]);
/// see [LogMeal.logEntry]
static final logEntry =
QueryRelationToOne<LogMeal, LogEntry>(_entities[7].properties[7]);
QueryRelationToOne<LogMeal, LogEntry>(_entities[7].properties[6]);
/// see [LogMeal.meal]
static final meal =
QueryRelationToOne<LogMeal, Meal>(_entities[7].properties[8]);
QueryRelationToOne<LogMeal, Meal>(_entities[7].properties[7]);
/// see [LogMeal.mealSource]
static final mealSource =
QueryRelationToOne<LogMeal, MealSource>(_entities[7].properties[9]);
QueryRelationToOne<LogMeal, MealSource>(_entities[7].properties[8]);
/// see [LogMeal.mealCategory]
static final mealCategory =
QueryRelationToOne<LogMeal, MealCategory>(_entities[7].properties[10]);
QueryRelationToOne<LogMeal, MealCategory>(_entities[7].properties[9]);
/// see [LogMeal.mealPortionType]
static final mealPortionType =
QueryRelationToOne<LogMeal, MealPortionType>(_entities[7].properties[11]);
QueryRelationToOne<LogMeal, MealPortionType>(_entities[7].properties[10]);
/// see [LogMeal.portionSizeAccuracy]
static final portionSizeAccuracy =
QueryRelationToOne<LogMeal, Accuracy>(_entities[7].properties[12]);
QueryRelationToOne<LogMeal, Accuracy>(_entities[7].properties[11]);
/// see [LogMeal.carbsRatioAccuracy]
static final carbsRatioAccuracy =
QueryRelationToOne<LogMeal, Accuracy>(_entities[7].properties[13]);
QueryRelationToOne<LogMeal, Accuracy>(_entities[7].properties[12]);
/// see [LogMeal.deleted]
static final deleted =
QueryBooleanProperty<LogMeal>(_entities[7].properties[14]);
QueryBooleanProperty<LogMeal>(_entities[7].properties[13]);
/// see [LogMeal.amount]
static final amount =
QueryDoubleProperty<LogMeal>(_entities[7].properties[14]);
/// see [LogMeal.totalCarbs]
static final totalCarbs =
QueryDoubleProperty<LogMeal>(_entities[7].properties[15]);
}
/// [Meal] entity fields to define ObjectBox queries.
@ -2392,3 +2596,66 @@ class GlucoseTarget_ {
static final color =
QueryIntegerProperty<GlucoseTarget>(_entities[15].properties[6]);
}
/// [Recipe] entity fields to define ObjectBox queries.
class Recipe_ {
/// see [Recipe.id]
static final id = QueryIntegerProperty<Recipe>(_entities[16].properties[0]);
/// see [Recipe.deleted]
static final deleted =
QueryBooleanProperty<Recipe>(_entities[16].properties[1]);
/// see [Recipe.name]
static final name = QueryStringProperty<Recipe>(_entities[16].properties[2]);
/// see [Recipe.carbsRatio]
static final carbsRatio =
QueryDoubleProperty<Recipe>(_entities[16].properties[3]);
/// see [Recipe.portionSize]
static final portionSize =
QueryDoubleProperty<Recipe>(_entities[16].properties[4]);
/// see [Recipe.carbsPerPortion]
static final carbsPerPortion =
QueryDoubleProperty<Recipe>(_entities[16].properties[5]);
/// see [Recipe.delayedBolusDuration]
static final delayedBolusDuration =
QueryIntegerProperty<Recipe>(_entities[16].properties[6]);
/// see [Recipe.delayedBolusPercentage]
static final delayedBolusPercentage =
QueryDoubleProperty<Recipe>(_entities[16].properties[7]);
/// see [Recipe.notes]
static final notes = QueryStringProperty<Recipe>(_entities[16].properties[8]);
/// see [Recipe.portion]
static final portion =
QueryRelationToOne<Recipe, Meal>(_entities[16].properties[9]);
}
/// [Ingredient] entity fields to define ObjectBox queries.
class Ingredient_ {
/// see [Ingredient.id]
static final id =
QueryIntegerProperty<Ingredient>(_entities[17].properties[0]);
/// see [Ingredient.deleted]
static final deleted =
QueryBooleanProperty<Ingredient>(_entities[17].properties[1]);
/// see [Ingredient.amount]
static final amount =
QueryDoubleProperty<Ingredient>(_entities[17].properties[2]);
/// see [Ingredient.recipe]
static final recipe =
QueryRelationToOne<Ingredient, Recipe>(_entities[17].properties[3]);
/// see [Ingredient.ingredient]
static final ingredient =
QueryRelationToOne<Ingredient, Meal>(_entities[17].properties[4]);
}

View File

@ -81,7 +81,7 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
reload();
_logEntry = LogEntry.get(widget.logEntryId);
_logMeals = LogMeal.getAllForEntry(widget.logEntryId);
_logMeals = LogMeal.getRecentWithoutBolus(widget.logEntryId);
if (widget.id != 0) {
_carbsController.text = (_logBolus!.carbs ?? '').toString();
@ -163,8 +163,8 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
_mealController.text = (_meal ?? '').toString();
});
if (_meal != null) {
if (_meal!.carbsPerPortion != null) {
_carbsController.text = (_meal!.carbsPerPortion).toString();
if (_meal!.totalCarbs != null) {
_carbsController.text = (_meal!.totalCarbs).toString();
}
if (_meal!.meal.hasValue) {
if (_meal!.meal.target!.delayedBolusDuration != null) {
@ -198,9 +198,9 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
void onSelectMeal(LogMeal? meal) {
updateLogMeal(meal);
if (meal != null && meal.carbsPerPortion != null) {
if (meal != null && meal.totalCarbs != null) {
setState(() {
_carbsController.text = meal.carbsPerPortion.toString();
_carbsController.text = meal.totalCarbs.toString();
calculateBolus();
});
}

View File

@ -214,8 +214,7 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
onDiscard: (context) => Navigator.pushReplacementNamed(context, '/log'),
);
} else {
Navigator.pushReplacementNamed(context, '/log',
arguments: '${_isNew ? 'New' : ''} Log Entry Saved');
Navigator.pop(context);
}
}

View File

@ -37,13 +37,16 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
bool _isSaving = false;
bool _isExpanded = false;
double _amount = 1;
final GlobalKey<FormState> _logMealForm = GlobalKey<FormState>();
final ScrollController _scrollController = ScrollController();
final _valueController = TextEditingController(text: '');
final _amountController = TextEditingController(text: '');
final _carbsRatioController = TextEditingController(text: '');
final _portionSizeController = TextEditingController(text: '');
final _carbsPerPortionController = TextEditingController(text: '');
final _totalCarbsController = TextEditingController(text: '');
final _notesController = TextEditingController(text: '');
Meal? _meal;
@ -81,10 +84,10 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
if (widget.id != 0) {
_valueController.text = _logMeal!.value;
_amountController.text = _logMeal!.amount.toString();
_carbsRatioController.text = (_logMeal!.carbsRatio ?? '').toString();
_portionSizeController.text = (_logMeal!.portionSize ?? '').toString();
_carbsPerPortionController.text =
(_logMeal!.carbsPerPortion ?? '').toString();
_totalCarbsController.text = (_logMeal!.totalCarbs ?? '').toString();
_notesController.text = _logMeal!.notes ?? '';
_meal = _logMeal!.meal.target;
@ -102,6 +105,10 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
_carbsRatioAccuracyController.text =
(_carbsRatioAccuracy ?? '').toString();
}
if (_amountController.text == '') {
_amountController.text = '1';
}
}
void reload({String? message}) {
@ -165,35 +172,18 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
Future<void> onSelectMeal(Meal? meal) async {
setState(() {
_meal = meal;
_valueController.text = _mealController.text = (_meal ?? '').toString();
_mealController.text = (_meal ?? '').toString();
_valueController.text = _mealController.text;
_carbsRatioController.text = (meal?.carbsRatio ?? '').toString();
_amountController.text = '1';
_portionSizeController.text = (meal?.portionSize ?? '').toString();
_totalCarbsController.text = (meal?.carbsPerPortion ?? '').toString();
});
if (meal != null) {
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.mealSource.hasValue) {
updateMealSource(meal.mealSource.target);
}
if (meal.mealCategory.hasValue) {
updateMealCategory(meal.mealCategory.target);
}
if (meal.mealPortionType.hasValue) {
updateMealPortionType(meal.mealPortionType.target);
}
if (meal.portionSizeAccuracy.hasValue) {
updatePortionSizeAccuracy(meal.portionSizeAccuracy.target);
}
if (meal.carbsRatioAccuracy.hasValue) {
updateCarbsRatioAccuracy(meal.carbsRatioAccuracy.target);
}
}
updateMealSource(meal?.mealSource.target);
updateMealCategory(meal?.mealCategory.target);
updateMealPortionType(meal?.mealPortionType.target);
updatePortionSizeAccuracy(meal?.portionSizeAccuracy.target);
updateCarbsRatioAccuracy(meal?.carbsRatioAccuracy.target);
}
void handleSaveAction() async {
@ -206,7 +196,7 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
value: _valueController.text,
carbsRatio: double.tryParse(_carbsRatioController.text),
portionSize: double.tryParse(_portionSizeController.text),
carbsPerPortion: double.tryParse(_carbsPerPortionController.text),
totalCarbs: double.tryParse(_totalCarbsController.text),
);
logMeal.logEntry.targetId = widget.logEntryId;
logMeal.meal.target = _meal;
@ -234,7 +224,7 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
_mealPortionType != null ||
double.tryParse(_carbsRatioController.text) != null ||
double.tryParse(_portionSizeController.text) != null ||
double.tryParse(_carbsPerPortionController.text) != null ||
double.tryParse(_totalCarbsController.text) != null ||
_carbsRatioAccuracy != null ||
_portionSizeAccuracy != null ||
_notesController.text != '')) ||
@ -248,8 +238,8 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
_logMeal!.carbsRatio ||
double.tryParse(_portionSizeController.text) !=
_logMeal!.portionSize ||
double.tryParse(_carbsPerPortionController.text) !=
_logMeal!.carbsPerPortion ||
double.tryParse(_totalCarbsController.text) !=
_logMeal!.totalCarbs ||
_carbsRatioAccuracy !=
_logMeal!.carbsRatioAccuracy.target ||
_portionSizeAccuracy !=
@ -265,37 +255,70 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
}
}
void calculateThirdMeasurementOfPortionCarbsRelation(
{PortionCarbsParameter? parameterToBeCalculated}) {
void updateAmount(double? amount) {
double? previousAmount;
double? portionSize;
double? carbsRatio;
previousAmount = _amount;
setState(() {
_amountController.text = (amount ?? '').toString();
_amount = amount ?? 1;
});
if (_carbsRatioController.text != '') {
carbsRatio = double.tryParse(_carbsRatioController.text);
}
if (_portionSizeController.text != '') {
portionSize = double.tryParse(_portionSizeController.text);
}
if (amount != null && portionSize != null) {
setState(() {
portionSize = portionSize! / (previousAmount ?? 1) * amount;
_portionSizeController.text = portionSize.toString();
});
if (carbsRatio != null) {
setState(() {
_totalCarbsController.text =
Utils.calculateCarbs(carbsRatio!, portionSize!).toString();
});
}
}
}
void calculateThirdMeasurementOfPortionCarbsRelation() {
int? amount;
double? carbsRatio;
double? portionSize;
double? carbsPerPortion;
if (parameterToBeCalculated != PortionCarbsParameter.carbsRatio &&
_carbsRatioController.text != '') {
if (_amountController.text != '') {
amount = int.tryParse(_amountController.text);
}
if (_carbsRatioController.text != '') {
carbsRatio = double.tryParse(_carbsRatioController.text);
}
if (parameterToBeCalculated != PortionCarbsParameter.portionSize &&
_portionSizeController.text != '') {
if (_portionSizeController.text != '') {
portionSize = double.tryParse(_portionSizeController.text);
}
if (parameterToBeCalculated != PortionCarbsParameter.carbsPerPortion &&
_carbsRatioController.text != '') {
carbsPerPortion = double.tryParse(_carbsPerPortionController.text);
if (_totalCarbsController.text != '') {
carbsPerPortion = double.tryParse(_totalCarbsController.text);
}
if (carbsRatio != null && portionSize != null && carbsPerPortion == null) {
setState(() {
_carbsPerPortionController.text =
Utils.calculateCarbsPerPortion(carbsRatio!, portionSize!)
_totalCarbsController.text =
Utils.calculateCarbs(carbsRatio!, portionSize! * (amount ?? 1))
.toString();
});
}
if (carbsRatio == null && portionSize != null && carbsPerPortion != null) {
setState(() {
_carbsRatioController.text =
Utils.calculateCarbsRatio(carbsPerPortion!, portionSize!)
.toString();
_carbsRatioController.text = Utils.calculateCarbsRatio(
carbsPerPortion!, portionSize! * (amount ?? 1))
.toString();
});
}
if (carbsRatio != null && portionSize == null && carbsPerPortion != null) {
@ -311,7 +334,7 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(_isNew ? 'New Meal' : _logMeal!.value),
title: Text(_isNew ? 'New Meal for Log Entry' : _logMeal!.value),
),
drawer: const Navigation(currentLocation: LogMealDetailScreen.routeName),
body: Scrollbar(
@ -344,11 +367,7 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
selectedItem: _meal,
label: 'Meal',
items: _meals,
onChanged: (value) {
if (value != null) {
onSelectMeal(value);
}
},
onChanged: onSelectMeal,
),
),
IconButton(
@ -369,34 +388,14 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
),
],
),
Row(
children: [
Expanded(
child: TextFormField(
decoration: const InputDecoration(
labelText: 'Carbs ratio',
suffixText: '%',
),
controller: _carbsRatioController,
keyboardType: const TextInputType.numberWithOptions(
decimal: true),
onChanged: (_) async {
await Future.delayed(const Duration(seconds: 1));
calculateThirdMeasurementOfPortionCarbsRelation();
},
),
),
IconButton(
onPressed: () =>
calculateThirdMeasurementOfPortionCarbsRelation(
parameterToBeCalculated:
PortionCarbsParameter.carbsRatio),
icon: const Icon(Icons.calculate),
),
],
NumberFormField(
controller: _amountController,
label: 'Amount',
suffix: _mealPortionType?.value,
min: 1,
onChanged: updateAmount,
),
Row(
mainAxisSize: MainAxisSize.min,
children: [
Expanded(
child: TextFormField(
@ -413,24 +412,14 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
},
),
),
IconButton(
onPressed: () =>
calculateThirdMeasurementOfPortionCarbsRelation(
parameterToBeCalculated:
PortionCarbsParameter.portionSize),
icon: const Icon(Icons.calculate),
),
],
),
Row(
children: [
const SizedBox(width: 10),
Expanded(
child: TextFormField(
decoration: InputDecoration(
labelText: 'Carbs per portion',
suffixText: Settings.nutritionMeasurementSuffix,
decoration: const InputDecoration(
labelText: 'Carbs ratio',
suffixText: '%',
),
controller: _carbsPerPortionController,
controller: _carbsRatioController,
keyboardType: const TextInputType.numberWithOptions(
decimal: true),
onChanged: (_) async {
@ -439,12 +428,21 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
},
),
),
IconButton(
onPressed: () =>
calculateThirdMeasurementOfPortionCarbsRelation(
parameterToBeCalculated:
PortionCarbsParameter.carbsPerPortion),
icon: const Icon(Icons.calculate),
const SizedBox(width: 10),
Expanded(
child: TextFormField(
decoration: InputDecoration(
labelText: 'Total carbs',
suffixText: Settings.nutritionMeasurementSuffix,
),
controller: _totalCarbsController,
keyboardType: const TextInputType.numberWithOptions(
decimal: true),
onChanged: (_) async {
await Future.delayed(const Duration(seconds: 1));
calculateThirdMeasurementOfPortionCarbsRelation();
},
),
),
],
),
@ -480,11 +478,13 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
children: _isExpanded
? [
Padding(
padding: const EdgeInsets.symmetric(vertical: 5.0),
padding:
const EdgeInsets.symmetric(vertical: 5.0),
child: Row(
children: [
Expanded(
child: AutoCompleteDropdownButton<MealSource>(
child:
AutoCompleteDropdownButton<MealSource>(
controller: _mealSourceController,
selectedItem: _mealSource,
label: 'Meal Source',
@ -497,11 +497,11 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
_mealSource == null
? const MealSourceDetailScreen()
: MealSourceDetailScreen(
id: _mealSource!.id),
builder: (context) => _mealSource ==
null
? const MealSourceDetailScreen()
: MealSourceDetailScreen(
id: _mealSource!.id),
),
).then((result) {
updateMealSource(result?[1]);
@ -516,12 +516,13 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 5.0),
padding:
const EdgeInsets.symmetric(vertical: 5.0),
child: Row(
children: [
Expanded(
child:
AutoCompleteDropdownButton<MealCategory>(
child: AutoCompleteDropdownButton<
MealCategory>(
controller: _mealCategoryController,
selectedItem: _mealCategory,
label: 'Meal Category',
@ -553,7 +554,8 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 5.0),
padding:
const EdgeInsets.symmetric(vertical: 5.0),
child: Row(
children: [
Expanded(
@ -590,12 +592,14 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 5.0),
padding:
const EdgeInsets.symmetric(vertical: 5.0),
child: Row(
children: [
Expanded(
child: AutoCompleteDropdownButton<Accuracy>(
controller: _portionSizeAccuracyController,
controller:
_portionSizeAccuracyController,
selectedItem: _portionSizeAccuracy,
label: 'Portion Size Accuracy',
items: _portionSizeAccuracies,
@ -607,10 +611,12 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => _portionSizeAccuracy == null
? const AccuracyDetailScreen()
: AccuracyDetailScreen(
id: _portionSizeAccuracy!.id),
builder: (context) =>
_portionSizeAccuracy == null
? const AccuracyDetailScreen()
: AccuracyDetailScreen(
id: _portionSizeAccuracy!
.id),
),
).then((result) {
updatePortionSizeAccuracy(result?[1]);
@ -625,7 +631,8 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 5.0),
padding:
const EdgeInsets.symmetric(vertical: 5.0),
child: Row(
children: [
Expanded(
@ -642,10 +649,12 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => _carbsRatioAccuracy == null
? const AccuracyDetailScreen()
: AccuracyDetailScreen(
id: _carbsRatioAccuracy!.id),
builder: (context) =>
_carbsRatioAccuracy == null
? const AccuracyDetailScreen()
: AccuracyDetailScreen(
id: _carbsRatioAccuracy!
.id),
),
).then((result) {
updateCarbsRatioAccuracy(result?[1]);

View File

@ -89,9 +89,9 @@ class _LogMealListScreenState extends State<LogMealListScreen> {
)),
Expanded(
child: Column(
children: ((meal.carbsPerPortion ?? 0) > 0)
children: ((meal.totalCarbs ?? 0) > 0)
? [
Text(meal.carbsPerPortion!.toStringAsPrecision(3)),
Text(meal.totalCarbs!.toStringAsPrecision(3)),
Text(
'${Settings.nutritionMeasurementSuffix} carbs',
textScaleFactor: 0.75),

View File

@ -246,8 +246,7 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
}
}
void calculateThirdMeasurementOfPortionCarbsRelation(
{PortionCarbsParameter? changedParameter}) {
void calculateThirdMeasurementOfPortionCarbsRelation() {
double? carbsRatio;
double? portionSize;
double? carbsPerPortion;
@ -265,7 +264,7 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
if (carbsRatio != null && portionSize != null && carbsPerPortion == null) {
setState(() {
_carbsPerPortionController.text =
Utils.calculateCarbsPerPortion(carbsRatio!, portionSize!)
Utils.calculateCarbs(carbsRatio!, portionSize!)
.toString();
});
}

View File

@ -14,7 +14,7 @@ class Utils {
return (mmolPerL * 18.018).round();
}
static double calculateCarbsPerPortion(
static double calculateCarbs(
double carbsRatio, double portionSize) {
return Utils.roundToDecimalPlaces(carbsRatio * portionSize / 100, 2);
}