translations, set name props as unique, set child data as deleted on deleting parents

This commit is contained in:
spinel 2022-07-08 04:08:50 +02:00
parent 38f43a48ec
commit 0182bf463b
81 changed files with 4199 additions and 1215 deletions

52
TODO
View File

@ -1,48 +1,45 @@
MAIN TASKS:
Database:
☐ set name properties as unique (and add checks to forms)
☐ implement users
☐ check objectbox docs on how to make users
☐ tie all data to users
☐ add user filters to all getters
☐ change settings to not be a singleton, but only one settings instance per user and one default entry
☐ add login and authentification
☐ enable restoring data from sync
☐ find a solution for storage
☐ hosting
☐ objectbox sync (commercial use is not free)
☐ implement alternative data export for now?
☐ create default datasets for configuration (meal categories, portion types, accuracies, event types, possibly meal source)
☐ cleanup unneeded models (and make sure the app still runs)
Features:
☐ app icon
☐ add explanations to each section
☐ german language support
☐ indicate nested creation process (creating from dropdown etc)
☐ indicate read only fields
☐ indicate nested creation process (creating from dropdown etc)
☐ app info/credits screen
☐ add explanations to each section
☐ app icon
Components/Framework:
☐ check through all detail forms and set required fields/according messages
☐ show indicator and make all fields readonly if user somehow gets to a deleted record detail view
☐ check for changes before navigating as well (not just on cancel)
☐ fix bug when navigating while creating/editing a record
☐ change placement of delete and floating button because its very easy to accidentally hit delete
☐ dropdown tweaks
☐ edit item -> cancel: shouldn't clear dropdwon
☐ account for deleted/disabled elements
☐ change app id from "com.example.diameter" to "at.sarahziesel.diameter"
☐ come up with new concept for duration component
☐ update duration fields to use corresponding component
☐ log event type detail (reminder duration)
☐ log event detail (reminder duration)
☐ meal (bolus delay)
☐ log bolus (delay)
☐ check through all detail forms and set required fields/according messages
☐ change placement of delete and floating button because its very easy to accidentally hit delete
☐ check for changes before navigating as well (not just on cancel)
☐ show indicator and make all fields readonly if user somehow gets to a deleted record detail view
☐ dropdown tweaks
☐ edit item -> cancel: shouldn't clear dropdwon
☐ account for deleted/disabled elements
Log:
☐ add filters
☐ check if there is still an active bolus when suggesting glucose bolus
☐ remember/display setting for correction boli correctly
Log Events:
☐ add filters
☐ don't show warning for existing event if it's the one being edited
☐ add create button to event type dropdown
Categorization:
☐ add colors to event types as indicators for log entries and graphs in reports
☐ implement reminders as push notifications
Settings:
☐ export functionality
☐ implement loading from google drive
☐ find out pricing for google apis
☐ add some options (which data to export, including dead records...)
☐ option to switch theme
☐ add fields for glucose target tiers (as map of cutoff glucose and colors)
☐ add field for active insulin duration
@ -50,6 +47,15 @@ MAIN TASKS:
☐ add functionality to delete dead records (meaning: set deleted flag and no relations to undeleted records)
Archive:
✔ set child data as deleted on deleting parent (ie. log boli for log entry etc) @done(22-06-19 23:44) @project(MAIN TASKS.Database)
✔ set name properties as unique @done(22-06-17 01:34) @project(MAIN TASKS.Database)
✔ german language support @done(22-06-17 01:19) @project(MAIN TASKS.Features)
✔ add translations for all sections @done(22-06-17 01:19) @project(MAIN TASKS.Features)
✔ add data source field to all models @done(22-04-15 00:24) @project(MAIN TASKS.Database)
✔ add export methods to all models @done(22-04-12 21:38) @project(MAIN TASKS.Database)
✔ add import methods to all models @done(22-04-15 00:24) @project(MAIN TASKS.Database)
✔ implement export to json @done(22-04-12 21:43) @project(MAIN TASKS.Settings)
✔ implement saving export to google drive @done(22-04-13 15:01) @project(MAIN TASKS.Settings)
✔ switch day on swipe @done(22-03-19 23:20) @project(MAIN TASKS.Log)
✔ switch day on swipe @done(22-03-19 23:23) @project(MAIN TASKS.Log Events)
✔ switch day on swipe @done(22-03-19 23:20) @project(MAIN TASKS.Reports)

View File

@ -0,0 +1 @@
{"installed":{"client_id":"988592836243-pmdkvghnvd6fdeo4qm0sjgstcqvrbhqs.apps.googleusercontent.com","project_id":"coastal-fiber-347020","auth_uri":"https://accounts.google.com/o/oauth2/auth","token_uri":"https://oauth2.googleapis.com/token","auth_provider_x509_cert_url":"https://www.googleapis.com/oauth2/v1/certs"}}

501
assets/i18n/de.json Normal file
View File

@ -0,0 +1,501 @@
{
"accuracy": {
"confirmDelete": "Willst du diese Präzision wirklich löschen?",
"deleted": "Präzision gelöscht",
"detail": {
"title": "{status} Präzision"
},
"empty": "Du hast noch keine Präzisionen erstellt!",
"fields": {
"confidenceRating": "Reihung",
"fürCarbsRatio": "für KH-Verhältnis",
"fürPortionSize": "für Portionsgröße",
"name": "Name",
"notes": "Bemerkung",
"validators": {
"name": "Name ist leer"
}
},
"new": "Neue",
"saved": {
"1": "{status}Präzision gespeichert",
"else": "{status}Präzisionen gespeichert"
},
"title": "Präzision"
},
"basal": {
"confirmDelete": "Willst du diese Basalrate wirklich löschen?",
"deleted": "Basalrate gelöscht",
"empty": "Du hast noch keine Basalraten erstellt!",
"fields": {
"endTime": "Endzeit",
"startTime": "Startzeit",
"units": "Einheiten"
},
"new": "Neue",
"saved": {
"1": "{status}Basalrate gespeichert",
"else": "{status}Basalraten gespeichert"
},
"title": "{status} Basalrate für {profileName}",
"warnings": {
"duplicate": "Es gibt bereits eine Rate mit dieser Startzeit.",
"endTimeLast": "Letzter Basal des Tages muss um 00:00 enden",
"gap": "Lücke zwischen dieser und der vorigen Rate",
"overlap": "Zeitraum der Rate überlappt mit einer anderen",
"startTimeFirst": "Erster Basal des Tages muss um 00:00 beginnen"
}
},
"basalProfile": {
"activated": "{profileName} wurde als aktives Profil ausgewählt",
"active": "Derzeit aktives Profil",
"confirmDelete": "Willst du dieseBasal Profile?",
"copied": "Kopie von {profileName} hinzugefügt",
"copyOf": "Kopie von {profileName}",
"default": "Standard-Profil",
"deleted": "Basalprofil gelöscht",
"detail": {
"tabs": {
"profile": "Profil",
"rates": "Raten"
},
"title": "{status} Basalprofil {profileName}"
},
"empty": "Du hast noch kein Basalprofil erstellt!",
"fields": {
"active": "aktiv",
"name": "Name",
"notes": "Bemerkung",
"validators": {
"name": "Name ist leer"
}
},
"new": "Neues",
"saved": "{status}Basalprofil gespeichert",
"title": "Basalprofile",
"warnings": {
"activeAlreadySet": "Es gibt bereits ein oder mehrere aktive Profile. Was möchtest du tun?",
"multipleActive": "Mehr als ein aktives Profil.",
"noActive": "Kein aktives Basalprofil.",
"noActiveOnCreate": "Im Moment ist kein Profil aktiv. Möchtest du dieses aktivieren?",
"resolve": {
"activate": "Profil aktivieren",
"activateCurrent": "Dieses Profil aktivieren",
"create": "Profil erstellen",
"createInstead": "Stattdessen neues Profil erstellen",
"deactivateOthers": "Alle anderen deaktivieren",
"deactivateProfile": "{profileName} deaktivieren",
"ignore": "Ignorierem",
"pick": "Wähle ein Profil"
}
}
},
"bolus": {
"confirmDelete": "Willst du diese Bolusrate wirklich löschen?",
"deleted": "Bolusrate gelöscht",
"empty": "Du hast noch keine Bolusraten erstellt!",
"fields": {
"endTime": "Endzeit",
"perCarbs": "pro KH",
"perGlucose": "pro {glucoseMeasurementSuffix}",
"startTime": "Startzeit",
"units": "Einheiten"
},
"new": "Neue",
"saved": {
"1": "{status}Bolusrate gespeichert",
"else": "{status}Bolusraten gespeichert"
},
"title": "{status} Bolusrate für {profileName}",
"warnings": {
"duplicate": "Es gibt bereits eine Rate mit dieser Startzeit.",
"endTimeLast": "Letzter Bolus des Tages muss um 00:00 enden",
"gap": "Lücke zwischen dieser und der vorigen Rate",
"overlap": "Zeitraum der Rate überlappt mit einer anderen",
"startTimeFirst": "Erster Bolus des Tages muss um 00:00 beginnen"
}
},
"bolusProfile": {
"activated": "{profileName} wurde als aktives Profil ausgewählt",
"active": "Derzeit aktives Profil",
"confirmDelete": "Willst du dieses Boluspofil wirklich löschen?",
"copied": "Kopie von {profileName} hinzugefügt",
"copyOf": "Kopie von {profileName}",
"default": "Standardprofil",
"deleted": "Bolusprofil gelöscht",
"detail": {
"tabs": {
"profile": "Profil",
"rates": "Raten"
},
"title": "{status} Bolusprofil {profileName}"
},
"empty": "Du hast noch keine Bolusprofile erstellt!",
"fields": {
"active": "aktiv",
"name": "Name",
"notes": "Bemerkung",
"validators": {
"name": "Bezeichnung ist leer"
}
},
"new": "Neues",
"saved": "{status}Bolusprofil gespeichert",
"title": "Bolusprofile",
"warnings": {
"activeAlreadySet": "Es gibt bereits ein oder mehrere aktive Profile. Was möchtest du tun?",
"multipleActive": "Mehr als ein aktives Profil.",
"noActive": "Kein aktives Bolusprofil.",
"noActiveOnCreate": "Im Moment ist kein Profil aktiv. Möchtest du dieses aktivieren?",
"resolve": {
"activate": "Profil aktivieren",
"activateCurrent": "Dieses Profil aktivieren",
"create": "Profil erstellen",
"createInstead": "Stattdessen neues Profil erstellen",
"deactivateOthers": "Alle anderen deaktivieren",
"deactivateProfile": "{profileName} deaktivieren",
"ignore": "Ignorierem",
"pick": "Wähle ein Profil"
}
}
},
"categories": "Kategorien",
"event": {
"confirmDelete": "Willst du dieses Ereignis wirklich löschen?",
"confirmEnd": "Willst du dieses Ereignis wirklich beenden?",
"deleted": "Log-Ereignis gelöscht",
"detail": {
"title": "{status} Log-Ereignis {name}"
},
"empty": "Kkeine Ereigniss für dieses Datum!",
"emptyActive": "Keine aktiven Ereignisse!",
"end": "Ereignis beenden",
"ended": "Log-Ereignis beendet",
"fields": {
"basalProfile": "Basalprofil",
"bolusProfile": "Bolusprofil",
"date": "Datum",
"endDate": "Enddatum",
"endTime": "Endzeit",
"eventType": "Ereignis-Typ",
"hasEndTime": "hat Endzeitpunkt",
"notes": "Bemerkung",
"reminderDuration": "Dauer für Erinnerung",
"startDate": "Startdatum",
"startTime": "Startzeit",
"time": "Zeit"
},
"new": "Neues",
"saved": "{status}Log-Ereignis gespeichert",
"title": "Log-Ereignisse",
"titleActive": "Aktive Ereignisse",
"warnings": {
"duplicate": "Im angegebenen Zeitraum gibt es bereits ein aktives Ereignis vom selben Typ. Was möchtest du tun?"
}
},
"eventType": {
"deleted": "Log-Ereignis-Typ gelöscht",
"detail": {
"title": "{status} Log-Ereignis-Typ {name}"
},
"empty": "Du hast noch keine Log-Ereignis-Typen erstellt!",
"fields": {
"basalProfile": "Basalprofil",
"bolusProfile": "Bolusprofil",
"defaultReminderDuration": "Standarddauer für Erinnerung",
"hasEndTime": "hat Endzeitpunkt",
"name": "Name",
"notes": "Bemerkung",
"validators": {
"name": "Name ist leer"
}
},
"new": "Neuer",
"saved": "{status}Log-Ereignis-Typ gespeichert",
"title": "Log-Ereignis-Typen"
},
"export": {
"error": "Error beim Import der folgenden Daten: {data}"
},
"general": {
"apply": "Anwenden",
"cancel": "Abbrechen",
"close": "Schließen",
"confirm": "Bestätigen",
"confirmDelete": "Willst du diesen Eintrag wirklich löschen?",
"confirmDiscard": "Änderungen wurden bereits vorgenommen. Eingaben verwerfen?",
"discard": "Verwerfen",
"edit": "Bearbeiten",
"example": "Beispiel",
"keepEditing": "Weiter bearbeiten",
"next": "Weiter",
"per": "pro",
"save": "Speichern",
"saveAndClose": "Speichern & Schließen",
"saveAsIs": "Aktuellen Stand speichern",
"suffixes": {
"carbs": "{nutritionMeasurement} KH",
"carbsPerU": "{nutritionMeasurementSuffix} KH pro E",
"mins": " min",
"perDay": "am Tag",
"units": "E",
"uPerBreadUnit": "E pro BE",
"uPerGlucose": "{glucoseMeasurementSuffix} pro Einheit"
}
},
"log": {
"confirmDelete": "Willst du diesen Logeintrag wirklich löschen?",
"deleted": "Logeintrag gelöscht",
"empty": "Du hast noch keine Logeinträge für dieses Datum erstellt!",
"fields": {
"date": "Datum",
"glucose": "Blutzucker",
"time": "Zeit"
},
"filter": {
"endDate": "Endzeit",
"maxGlucose": "max. {glucoseMeasurement}",
"meal": "Mahlzeit",
"mealNameContains": "Name der Mahlzeit enthält",
"minGlucose": "min. {glucoseMeasurement}",
"noteContains": "Bemerkung enthält",
"startDate": "Startzeit"
},
"saved": "{status}Logeintrag gespeichert",
"tabs": {
"bolus": {
"confirmDelete": "Willst du diesen Bolus wirklich löschen?",
"delayedBy": "(um {delay} verzögert)",
"deleted": "Bolus gelöscht",
"detail": {
"fields": {
"carbs": "KH",
"correction": "Korrektur",
"current": "Aktuell",
"delayedBolus": "Verzögerter Bolus",
"delayedBolusDuration": "Dauer Verzögerter Bolus",
"forGlucose": "für Blutzucker",
"forMeal": "für Mahlzeit",
"immediateBolus": "Sofortiger Bolus",
"meal": "Mahlzeit",
"setManually": "manuall einstellen",
"target": "Zielwert",
"units": "Bolus-Einheiten"
},
"title": "{status} Bolus"
},
"empty": "Du hast noch keine Boli für diesen Logeintrag erstellt!",
"forMeal": "für {meal}",
"new": "Neuer",
"saved": "{status}Bolus gespeichert",
"title": "Boli",
"toCorrect": "zur Korrektur von {correction}"
},
"general": "Allgemein",
"meal": {
"confirmDelete": "Willst du diese Mahlzeit wirklich löschen?",
"deleted": "Mahlzeit gelöscht",
"detail": {
"fields": {
"amount": "Menge",
"carbsRatio": "KH-Verhältnis",
"carbsRatioAccuracy": "Präzision KH-Verhältnis",
"meal": "Mahlzeit",
"mealCategory": "Kategorie",
"mealPortionType": "Portionstyp",
"mealSource": "Herkunft",
"name": "Name",
"notes": "Bemerkung",
"portionSize": "Portionsgröße",
"portionSizeAccuracy": "Präzision Portionsgröße",
"setManually": "KH-Verhältnis manuell einstellen",
"validators": {
"name": "Name ist leer"
}
},
"title": "{status} Mahlzeit"
},
"empty": "Du hast noch keine Mahlzeiten für diesen Logeintrag erstellt!",
"new": "Neue",
"saved": "{status}Mahlzeit gespeichert",
"title": "Mahlzeiten"
}
},
"title": "Logeinträge"
},
"meal": {
"confirmDelete": "Willst du diese Mahlzeit wirklich löschen?",
"deleted": "Mahlzeit gelöscht",
"detail": {
"title": "{status} Mahlzeit {name}"
},
"empty": "Du hast noch keine Mahlzeiten erstellt!",
"fields": {
"additional": {
"carbsRatioAccuracy": "Präzision KH-Verhältnis",
"mealCategory": "Kategorie",
"portionSizeAccuracy": "Präzision Portionsgröße",
"title": "Zusätzliche Felder"
},
"carbsPerPortion": "KH pro Portion",
"carbsRatio": "KH-Verhältnis",
"delay": {
"duration": "Dauer",
"title": "Verzögerter Bolus"
},
"mealPortionType": "Portionstyp",
"mealSource": "Herkunft",
"name": "Name",
"notes": "Bemerkung",
"portionSize": "Portionsgröße",
"setManually": "KH-Verhältnis manuell einstellen",
"validators": {
"name": "Name ist leer"
}
},
"new": "Neue",
"saved": "{status}Mahlzeit gespeichert",
"title": "Mahlzeiten"
},
"mealCategory": {
"deleted": "Mahlzeiten-Kategorie gelöscht",
"detail": {
"title": "{status} Mahlzeiten-Kategorie {name}"
},
"empty": "Du hast noch keine Mahlzeiten-Kategorien erstellt!",
"fields": {
"name": "Name",
"notes": "Bemerkung",
"validators": {
"name": "Name ist leer"
}
},
"new": "New",
"saved": "{status}Mahlzeiten-Kategorie gespeichert",
"title": "Mahlzeiten-Kategorien"
},
"mealSource": {
"deleted": "Mahlzeiten-Herkunft gelöscht",
"detail": {
"title": "{status} Mahlzeiten-Herkunft {name}"
},
"empty": "Du hast noch keine Mahlzeiten-Herkünfte erstellt!",
"fields": {
"defaultCarbsRatioAccuracy": "Standard-Präzision KH-Verhältnis",
"defaultMealCategory": "Standard Mahlzeiten-Kategorie",
"defaultMealPortionType": "Standard-Portionstyp",
"defaultPortionSizeAccuracy": "Standard-Präzision Portionsgröße",
"name": "Name",
"notes": "Bemerkung",
"validators": {
"name": "Name ist leer"
}
},
"new": "New",
"saved": "{status}Mahlzeiten-Herkunft gespeichert",
"title": "Mahlzeiten-Herkünfte"
},
"navigation": {
"basalProfiles": "Basalprofile",
"bolusProfiles": "Bolusprofile",
"categorization": "Kategorisierung",
"log": "Log",
"logEvent": "Log-Ereignisse",
"meals": "Mahlzeiten",
"reports": "Berichte",
"settings": "Einstellungen"
},
"portionType": {
"deleted": "Mahlzeiten-Portionstyp gelöscht",
"detail": {
"title": "{status} Mahlzeiten-Portionstyp {name}"
},
"empty": "Du hast noch keine Mahlzeiten-Portionstypen erstellt!",
"fields": {
"name": "Name",
"notes": "Bemerkung",
"validators": {
"name": "Name ist leer"
}
},
"new": "New",
"saved": "{status}Mahlzeiten-Portionstyp gespeichert",
"title": "Mahlzeiten-Portionstypen"
},
"reports": {
"dailyChart": {
"empty": "Du hast noch keine Logeinträge für dieses Datum erstellt!",
"showBasal": "zeige Basal",
"showBolus": "zeige Bolus",
"showChart": "zeige Grafik",
"showMeals": "zeige Mahlzeiten",
"title": "Tagesgrafik"
},
"export": {
"date": "Datum",
"delayedBy": "(um {delay} verzögert)",
"endDate": "Enddatum",
"export": "exportieren",
"range": "Zeitraum",
"showBasal": "zeige Basal",
"showBolus": "zeige Bolus",
"showChart": "zeige Grafik",
"showMahlzeits": "zeige Mahlzeiten",
"showTable": "zeige Tabelle",
"singleDate": "Bestimmtes Datum",
"tableHeaders": {
"bolus": "Bolus",
"carbs": "Kohlenhydrate",
"glucose": "Blutzucker",
"mealBolus": "Mahlzeit Bolus",
"mealBemerkung": "Mahlzeit Bemerkung",
"meals": "Mahlzeit",
"notes": "Bemerkung",
"portionSize": "Portionsgröße",
"time": "Zeit"
},
"title": "Log-Übersicht"
},
"ids": {
"basal": "Basal",
"bolus": "Bolus",
"carbs": "Kohlenhydrate",
"glucose": "Blutzucker"
},
"sections": {
"dailyReport": "Tagesbericht",
"pdfReport": "PDF-Übersicht"
},
"title": "Berichte"
},
"settings": {
"confirmReset": "Bist du sicher, dass du alle Einstellungen zurücksetzen möchtest?",
"fields": {
"confirmOnCancel": "beim Abbrechen der Bearbeitung eines Eintrags, wenn bereits Änderungen vorgenommen wurden",
"confirmOnDelete": "beim Löschen eines Eintrags",
"confirmOnEndEreignis": "beim Beenden eines Ereignisses",
"dateformat": "Datumsformat",
"displayBothDetail": "zeige beide Blutzucker-Maßeinheiten in der Detailansicht",
"displayBothList": "display both glucose measurements in list view",
"glucoseMeasurement": "Bevorzugte Maßeinheit für Blutzuckerwerte",
"insulinIncrement": "Insulin-Dosierungsschritte",
"longDateformat": "Datumsformat lang",
"longTimeformat": "Zeitformat lang",
"mmolLIncrement": "Schritte Mmol/l",
"nutritionIncrement": "Schritte Maßeinheit für Mahlzeiten",
"nutritionMeasurement": "Bevorzugte Maßeinheit für Mahlzeiten",
"onlyDisplayActive": "zeige nur aktive Blutzucker-Maßeinheit",
"targetGlucose": "Ziel-Blutzucker",
"timeformat": "Zeitformat"
},
"reset": "Einstellungen wurden auf Standardeinstellungen zurückgesetzt",
"resetAll": "Alle zurücksetzen",
"sections": {
"confirmation": "Bestätigungen",
"dateTimeformat": "Zeit- und Datumsformat",
"measurements": "Maßeinheiten"
},
"title": "Applikationseinstellungen",
"updated": "Einstellungen gespeichert"
}
}

501
assets/i18n/en.json Normal file
View File

@ -0,0 +1,501 @@
{
"accuracy": {
"confirmDelete": "Are you sure you want to delete this Accuracy?",
"deleted": "Accuracy deleted",
"detail": {
"title": "{status} Accuracy"
},
"empty": "You have not created any Accuracies yet!",
"fields": {
"confidenceRating": "Confidence Rating",
"forCarbsRatio": "for Carbs Ratio",
"forPortionSize": "for Portion Size",
"name": "Name",
"notes": "Notes",
"validators": {
"name": "Empty Name"
}
},
"new": "New",
"saved": {
"1": "{status}Accuracy saved",
"else": "{status}Accuracy saved"
},
"title": "Accuracies"
},
"basal": {
"confirmDelete": "Are you sure you want to delete this Basal Rate?",
"deleted": "Basal Rate deleted",
"empty": "You have not created any Basal Rates yet!",
"fields": {
"endTime": "End Time",
"startTime": "Start Time",
"units": "Units"
},
"new": "New",
"saved": {
"1": "{status}Basal Rate saved",
"else": "{status}Basal Rates saved"
},
"title": "{status} Basal Rate for {profileName}",
"warnings": {
"duplicate": "There's already a rate with this start time.",
"endTimeLast": "Last Basal of the day needs to end at 00:00",
"gap": "There's a time gap between this and the previous rate",
"overlap": "This rate's time period overlaps with another one.",
"startTimeFirst": "First Basal of the day needs to start at 00:00"
}
},
"basalProfile": {
"activated": "{profileName} has been set as your active Profile",
"active": "Current Active Profile",
"confirmDelete": "Are you sure you want to delete this Basal Profile?",
"copied": "Added copy of {profileName}",
"copyOf": "Copy of {profileName}",
"default": "Default Profile",
"deleted": "Basal Profile deleted",
"detail": {
"tabs": {
"profile": "Profile",
"rates": "Rates"
},
"title": "{status} Basal Profile {profileName}"
},
"empty": "You have not created any Basal Profiles yet!",
"fields": {
"active": "active",
"name": "Name",
"notes": "Notes",
"validators": {
"name": "Empty title"
}
},
"new": "New",
"saved": "{status}Basal Profile saved",
"title": "Basal Profiles",
"warnings": {
"activeAlreadySet": "There are already one or more active profiles. What would you like to do?",
"multipleActive": "More than one active Basal Profile has been found.",
"noActive": "You currently do not have an active Basal Profile.",
"noActiveOnCreate": "There is currently no active profile. Would you like to set this one as active?",
"resolve": {
"activate": "Activate a Profile",
"activateCurrent": "Activate This Profile",
"create": "Create a Profile",
"createInstead": "Create a New Profile Instead",
"deactivateOthers": "Deactivate All Others",
"deactivateProfile": "Deactivate {profileName}",
"ignore": "Ignore",
"pick": "Pick a Profile"
}
}
},
"bolus": {
"confirmDelete": "Are you sure you want to delete this Bolus Rate?",
"deleted": "Bolus Rate deleted",
"empty": "You have not created any Bolus Rates yet!",
"fields": {
"endTime": "End Time",
"perCarbs": "per carns",
"perGlucose": "per {glucoseMeasurementSuffix}",
"startTime": "Start Time",
"units": "Units"
},
"new": "New",
"saved": {
"1": "{status}Bolus Rate saved",
"else": "{status}Bolus Rates saved"
},
"title": "{status} Bolus Rate for {profileName}",
"warnings": {
"duplicate": "There's already a rate with this start time.",
"endTimeLast": "Last Bolus of the day needs to end at 00:00",
"gap": "There's a time gap between this and the previous rate",
"overlap": "This rate's time period overlaps with another one.",
"startTimeFirst": "First Bolus of the day needs to start at 00:00"
}
},
"bolusProfile": {
"activated": "{profileName} has been set as your active Profile",
"active": "Current Active Profile",
"confirmDelete": "Are you sure you want to delete this Bolus Profile?",
"copied": "Added copy of {profileName}",
"copyOf": "Copy of {profileName}",
"default": "Default Profile",
"deleted": "Bolus Profile deleted",
"detail": {
"tabs": {
"profile": "Profile",
"rates": "Rates"
},
"title": "{status} Bolus Profile {profileName}"
},
"empty": "You have not created any Bolus Profiles yet!",
"fields": {
"active": "active",
"name": "Name",
"notes": "Notes",
"validators": {
"name": "Empty title"
}
},
"new": "New",
"saved": "{status}Bolus Profile saved",
"title": "Bolus Profiles",
"warnings": {
"activeAlreadySet": "There are already one or more active profiles. What would you like to do?",
"multipleActive": "More than one active Bolus Profile has been found.",
"noActive": "You currently do not have an active Bolus Profile.",
"noActiveOnCreate": "There is currently no active profile. Would you like to set this one as active?",
"resolve": {
"activate": "Activate a Profile",
"activateCurrent": "Activate This Profile",
"create": "Create a Profile",
"createInstead": "Create a New Profile Instead",
"deactivateOthers": "Deactivate All Others",
"deactivateProfile": "Deactivate {profileName}",
"ignore": "Ignore",
"pick": "Pick a Profile"
}
}
},
"categories": "Categories",
"event": {
"confirmDelete": "Are you sure you want to delete this Log Event?",
"confirmEnd": "Are you sure you want to end this Event?",
"deleted": "Log Event deleted",
"detail": {
"title": "{status} Log Event {name}"
},
"empty": "There are no Events for that date!",
"emptyActive": "There are no Active Events!",
"end": "End Event",
"ended": "Log Event ended",
"fields": {
"basalProfile": "Basal Profile",
"bolusProfile": "Bolus Profile",
"date": "Date",
"endDate": "End Date",
"endTime": "End Time",
"eventType": "Event Type",
"hasEndTime": "has end time",
"notes": "Notes",
"reminderDuration": "Reminder Duration",
"startDate": "Start Date",
"startTime": "Start Time",
"time": "Time"
},
"new": "New",
"saved": "{status}Log Event saved",
"title": "Log Events",
"titleActive": "Active Events",
"warnings": {
"duplicate": "An Event of this type is already active within the set time frame. What would you like to do?"
}
},
"eventType": {
"deleted": "Log Event Type deleted",
"detail": {
"title": "{status} Log Event Type {name}"
},
"empty": "You have not created any Log Event Types yet!",
"fields": {
"basalProfile": "Basal Profile",
"bolusProfile": "Bolus Profile",
"defaultReminderDuration": "Default Reminder Duration",
"hasEndTime": "has end time",
"name": "Name",
"notes": "Notes",
"validators": {
"name": "Empty name"
}
},
"new": "New",
"saved": "{status}Log Event Type saved",
"title": "Log Event Types"
},
"export": {
"error": "Error importing the following data: {data}"
},
"general": {
"apply": "Apply",
"cancel": "Cancel",
"close": "Close",
"confirm": "Confirm",
"confirmDelete": "Are you sure you want to delete this record?",
"confirmDiscard": "You already made some changes. Discard your input?",
"discard": "Discard",
"edit": "Edit",
"example": "Example",
"keepEditing": "Keep Editing",
"next": "Next",
"per": "per",
"save": "Save",
"saveAndClose": "Save & Close",
"saveAsIs": "Save As Is",
"suffixes": {
"carbs": "{nutritionMeasurement} carbs",
"carbsPerU": "{nutritionMeasurementSuffix} carbs per U",
"mins": " min",
"perDay": "per day",
"units": "U",
"uPerBreadUnit": "U per bread unit",
"uPerGlucose": "{glucoseMeasurementSuffix} per unit"
}
},
"log": {
"confirmDelete": "Are you sure you want to delete this Log Entry?",
"deleted": "Log Entry deleted",
"empty": "You have not created any Log Entries for this date yet!",
"fields": {
"date": "Date",
"glucose": "Blood Glucose",
"time": "Time"
},
"filter": {
"endDate": "End Date",
"maxGlucose": "max {glucoseMeasurement}",
"meal": "Meal",
"mealNameContains": "Meal Name Contains",
"minGlucose": "min {glucoseMeasurement}",
"noteContains": "Note Contains",
"startDate": "Start Date"
},
"saved": "{status}Log Entry saved",
"tabs": {
"bolus": {
"confirmDelete": "Are you sure you want to delete this Bolus?",
"delayedBy": "(delayed by {delay})",
"deleted": "Bolus deleted",
"detail": {
"fields": {
"carbs": "Carbs",
"correction": "Correction",
"current": "Current",
"delayedBolus": "Delayed Bolus",
"delayedBolusDuration": "Delayed Bolus Duration",
"forGlucose": "for glucose",
"forMeal": "for meal",
"immediateBolus": "Immediate Bolus",
"meal": "Meal",
"setManually": "set manually",
"target": "Target",
"units": "Bolus Units"
},
"title": "{status} Bolus"
},
"empty": "You have not added any Boli to this Log Entry yet!",
"forMeal": "for {meal}",
"new": "New",
"saved": "{status}Bolus saved",
"title": "Boli",
"toCorrect": "to correct {correction}"
},
"general": "General",
"meal": {
"confirmDelete": "Are you sure you want to delete this Meal?",
"deleted": "Meal deleted",
"detail": {
"fields": {
"amount": "Amount",
"carbsRatio": "Carbs Ratio",
"carbsRatioAccuracy": "Carbs Ratio Accuracy",
"meal": "Meal",
"mealCategory": "Meal Category",
"mealPortionType": "Meal Portion Type",
"mealSource": "Meal Source",
"name": "Name",
"notes": "Notes",
"portionSize": "Portion Size",
"portionSizeAccuracy": "Portion Size Accuracy",
"setManually": "set carbs ratio manually",
"validators": {
"name": "Empty Name"
}
},
"title": "{status} Meal"
},
"empty": "You have not added any Meals to this Log Entry yet!",
"new": "New",
"saved": "{status}Meal saved",
"title": "Meals"
}
},
"title": "Log Entries"
},
"meal": {
"confirmDelete": "Are you sure you want to delete this Meal?",
"deleted": "Meal deleted",
"detail": {
"title": "{status} Meal {name}"
},
"empty": "You have not created any Meals yet!",
"fields": {
"additional": {
"carbsRatioAccuracy": "Carbs Ratio Accuracy",
"mealCategory": "Meal Category",
"portionSizeAccuracy": "Portion Size Accuracy",
"title": "Additional Fields"
},
"carbsPerPortion": "Carbs Per Portion",
"carbsRatio": "Carbs Ratio",
"delay": {
"duration": "Duration",
"title": "Bolus Delay"
},
"mealPortionType": "Meal Portion Type",
"mealSource": "Meal Source",
"name": "Name",
"notes": "Notes",
"portionSize": "Portion Size",
"setManually": "set carbs ratio manually",
"validators": {
"name": "Empty Name"
}
},
"new": "New",
"saved": "{status}Meal saved",
"title": "Meals"
},
"mealCategory": {
"deleted": "Meal Category deleted",
"detail": {
"title": "{status} Meal Category {name}"
},
"empty": "You have not created any Meal Categories yet!",
"fields": {
"name": "Name",
"notes": "Notes",
"validators": {
"name": "Empty name"
}
},
"new": "New",
"saved": "{status}Meal Category saved",
"title": "Meal Categories"
},
"mealSource": {
"deleted": "Meal Source deleted",
"detail": {
"title": "{status} Meal Source {name}"
},
"empty": "You have not created any Meal Sources yet!",
"fields": {
"defaultCarbsRatioAccuracy": "Default Carbs Ratio Accuracy",
"defaultMealCategory": "Default Meal Category",
"defaultMealPortionType": "Default Meal Portion Type",
"defaultPortionSizeAccuracy": "Default Portion Size Accuracy",
"name": "Name",
"notes": "Notes",
"validators": {
"name": "Empty name"
}
},
"new": "New",
"saved": "{status}Meal Source saved",
"title": "Meal Sources"
},
"navigation": {
"basalProfiles": "Basal Profiles",
"bolusProfiles": "Bolus Profiles",
"categorization": "Categorization",
"log": "Log",
"logEvents": "Log Events",
"meals": "Meals",
"reports": "Reports",
"settings": "Settings"
},
"portionType": {
"deleted": "Meal Portion Type deleted",
"detail": {
"title": "{status} Meal Portion Type {name}"
},
"empty": "You have not created any Meal Portion Types yet!",
"fields": {
"name": "Name",
"notes": "Notes",
"validators": {
"name": "Empty name"
}
},
"new": "New",
"saved": "{status}Meal Portion Type saved",
"title": "Meal Portion Types"
},
"reports": {
"dailyChart": {
"empty": "You have not created any Log Entries for this date yet!",
"showBasal": "show Basal",
"showBolus": "show Bolus",
"showChart": "show Chart",
"showMeals": "show Meals",
"title": "Daily Chart"
},
"export": {
"date": "Date",
"delayedBy": "(delayed by {delay})",
"endDate": "End Date",
"export": "export",
"range": "Range",
"showBasal": "show Basal",
"showBolus": "show Bolus",
"showChart": "show Chart",
"showMeals": "show Meals",
"showTable": "show Table",
"singleDate": "Single Date",
"tableHeaders": {
"bolus": "Bolus",
"carbs": "Carbohydrates",
"glucose": "Glucose",
"mealBolus": "Meal Bolus",
"mealNotes": "Meal Notes",
"meals": "Meals",
"notes": "Notes",
"portionSize": "Portion Size",
"time": "Time"
},
"title": "Log Report"
},
"ids": {
"basal": "Basal",
"bolus": "Bolus",
"carbs": "Carbohydrates",
"glucose": "Glucose"
},
"sections": {
"dailyReport": "Daily Report",
"pdfReport": "PDF Report"
},
"title": "Reports"
},
"settings": {
"confirmReset": "Are you sure you want to reset all settings?",
"fields": {
"confirmOnCancel": "on cancelling edit or creation of a record if changes have already been made",
"confirmOnDelete": "on deleting a record",
"confirmOnEndEvent": "on stopping (ending) an event",
"dateFormat": "Date Format",
"displayBothDetail": "display both glucose measurements in detail view",
"displayBothList": "display both glucose measurements in list view",
"glucoseMeasurement": "Preferred Glucose Measurement",
"insulinIncrement": "Insulin Increment",
"longDateFormat": "Long Date Format",
"longTimeFormat": "Long Time Format",
"mmolLIncrement": "Mmol/l Increment",
"nutritionIncrement": "Nutrition Increment",
"nutritionMeasurement": "Preferred Nutrition Measurement",
"onlyDisplayActive": "only display active glucose measurement",
"targetGlucose": "Target Glucose",
"timeFormat": "Time Format"
},
"reset": "Settings have been reset to default",
"resetAll": "Reset All",
"sections": {
"confirmation": "Confirmation Prompts",
"dateTimeFormat": "Time & Date Format",
"measurements": "Measurements"
},
"title": "Application Settings",
"updated": "Settings updated"
}
}

6
build.yaml Normal file
View File

@ -0,0 +1,6 @@
targets:
$default:
sources:
include:
- assets/i18n/**
- lib/**

View File

@ -291,7 +291,7 @@
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.example.tide;
PRODUCT_BUNDLE_IDENTIFIER = com.example.diameter;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
@ -415,7 +415,7 @@
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.example.tide;
PRODUCT_BUNDLE_IDENTIFIER = com.example.diameter;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
@ -434,7 +434,7 @@
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.example.tide;
PRODUCT_BUNDLE_IDENTIFIER = com.example.diameter;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;

View File

@ -11,7 +11,7 @@
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>tide</string>
<string>diameter</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>

View File

@ -5,7 +5,7 @@ class AppTheme {
AppTheme._();
static ThemeData lightTheme = FlexColorScheme.light(
surfaceStyle: FlexSurface.medium,
// surfaceMode: FlexSurfaceMode.level,
scheme: FlexScheme.aquaBlue,
fontFamily: 'Roboto',
).toTheme;

View File

@ -1,11 +1,13 @@
import 'package:diameter/localization_keys.dart';
import 'package:flutter/material.dart';
import 'package:flutter_translate/flutter_translate.dart';
class DetailBottomRow extends StatefulWidget {
final void Function()? onCancel;
final void Function()? onAction;
final void Function()? onMiddleAction;
final String actionText;
final String middleActionText;
final String actionTextKey;
final String middleActionTextKey;
final IconData actionIcon;
final IconData middleActionIcon;
@ -14,9 +16,9 @@ class DetailBottomRow extends StatefulWidget {
required this.onCancel,
required this.onAction,
this.onMiddleAction,
this.actionText = 'SAVE',
this.actionTextKey = LocalizationKeys.general_save,
this.actionIcon = Icons.save,
this.middleActionText = 'SAVE & CLOSE',
this.middleActionTextKey = LocalizationKeys.general_saveAndClose,
this.middleActionIcon = Icons.done})
: super(key: key);
@ -39,7 +41,7 @@ class _DetailBottomRowState<T> extends State<DetailBottomRow> {
Icons.close,
size: 18.0,
),
label: const Text('CANCEL'),
label: Text(translate(LocalizationKeys.general_cancel).toUpperCase()),
),
widget.onMiddleAction != null
? ElevatedButton.icon(
@ -48,7 +50,7 @@ class _DetailBottomRowState<T> extends State<DetailBottomRow> {
widget.middleActionIcon,
size: 18.0,
),
label: Text(widget.middleActionText),
label: Text(translate(widget.middleActionTextKey)),
)
: const Spacer(),
ElevatedButton.icon(
@ -57,7 +59,7 @@ class _DetailBottomRowState<T> extends State<DetailBottomRow> {
widget.actionIcon,
size: 18.0,
),
label: Text(widget.actionText),
label: Text(translate(widget.actionTextKey)),
),
],
),

View File

@ -6,13 +6,15 @@ class TimeOfDayFormField extends StatefulWidget {
final TextEditingController controller;
final String label;
final void Function(TimeOfDay?) onChanged;
final String? Function(String?)? validator;
const TimeOfDayFormField(
{Key? key,
required this.time,
required this.controller,
required this.label,
required this.onChanged})
required this.onChanged,
this.validator})
: super(key: key);
@override
@ -35,6 +37,7 @@ class _TimeOfDayFormFieldState extends State<TimeOfDayFormField> {
);
widget.onChanged(newTime);
},
validator: widget.validator,
);
}
}

366
lib/data_export.dart Normal file
View File

@ -0,0 +1,366 @@
import 'dart:convert';
import 'dart:io';
import 'package:diameter/localization_keys.dart';
import 'package:diameter/models/accuracy.dart';
import 'package:diameter/models/basal.dart';
import 'package:diameter/models/basal_profile.dart';
import 'package:diameter/models/bolus.dart';
import 'package:diameter/models/bolus_profile.dart';
import 'package:diameter/models/glucose_target.dart';
import 'package:diameter/models/log_bolus.dart';
import 'package:diameter/models/log_entry.dart';
import 'package:diameter/models/log_event.dart';
import 'package:diameter/models/log_event_type.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/models/settings.dart';
import 'package:flutter_translate/flutter_translate.dart';
// import 'package:flutter/material.dart';
import 'package:google_sign_in/google_sign_in.dart';
import 'package:intl/intl.dart';
import 'package:path_provider/path_provider.dart';
import 'package:http/http.dart' as http;
import 'package:googleapis/drive/v3.dart' as drive;
class GoogleAuthClient extends http.BaseClient {
final Map<String, String> _headers;
final http.Client _client = http.Client();
GoogleAuthClient(this._headers);
@override
Future<http.StreamedResponse> send(http.BaseRequest request) {
return _client.send(request..headers.addAll(_headers));
}
}
class DataExport {
static Map<String, dynamic> appDataToJson(DateTime timestamp) {
final Map<String, dynamic> data = <String, dynamic>{};
data['exportDate'] = timestamp.toString();
data['accuracies'] =
Accuracy.box.getAll().map((accuracy) => accuracy.toJson()).toList();
data['basalProfiles'] = BasalProfile.box
.getAll()
.map((basalProfile) => basalProfile.toJson())
.toList();
data['basalRates'] =
Basal.box.getAll().map((basal) => basal.toJson()).toList();
data['bolusProfiles'] = BolusProfile.box
.getAll()
.map((bolusProfile) => bolusProfile.toJson())
.toList();
data['bolusRates'] =
Bolus.box.getAll().map((bolusRates) => bolusRates.toJson()).toList();
data['glucoseTargets'] = GlucoseTarget.box
.getAll()
.map((glucoseTarget) => glucoseTarget.toJson())
.toList();
data['logBoli'] =
LogBolus.box.getAll().map((logBolus) => logBolus.toJson()).toList();
data['logEntries'] =
LogEntry.box.getAll().map((logEntry) => logEntry.toJson()).toList();
data['logEventTypes'] = LogEventType.box
.getAll()
.map((logEventType) => logEventType.toJson())
.toList();
data['logEvents'] =
LogEvent.box.getAll().map((logEvent) => logEvent.toJson()).toList();
data['logMeals'] =
LogMeal.box.getAll().map((logMeal) => logMeal.toJson()).toList();
data['mealCategories'] = MealCategory.box
.getAll()
.map((mealCategory) => mealCategory.toJson())
.toList();
data['mealPortionTypes'] = MealPortionType.box
.getAll()
.map((mealPortionType) => mealPortionType.toJson())
.toList();
data['mealSources'] = MealSource.box
.getAll()
.map((mealSource) => mealSource.toJson())
.toList();
data['meals'] = Meal.box.getAll().map((meal) => meal.toJson()).toList();
data['settings'] = Settings.toJson();
return data;
}
List<String> appDataFromJson(File file,
{String source = '', bool overrideExisting = true, bool skipDeleted = false}) {
final Map<String, dynamic> data = json.decode(file.openRead().toString());
final List<String> errors = [];
final exportDate = DateTime.tryParse(data['exportDate']);
if (exportDate != null && Settings.lastExportTimeStamp != null && exportDate.isAfter(Settings.lastExportTimeStamp!)) {
if (data.keys.contains('accuracies')) {
for (var entry in data['accuracies']) {
if (!skipDeleted || entry['deleted'] == false) {
final error = Accuracy.putFromJson(entry, overrideExisting, source);
if (error != null) {
errors.add(translate(LocalizationKeys.export_error, args: {"data": entry}));
}
}
}
}
if (data.keys.contains('basalProfiles')) {
for (var entry in data['basalProfiles']) {
if (!skipDeleted || entry['deleted'] == false) {
final error = BasalProfile.putFromJson(entry, overrideExisting, source);
if (error != null) {
errors.add(translate(LocalizationKeys.export_error, args: {"data": entry}));
}
}
}
}
if (data.keys.contains('basalRates')) {
for (var entry in data['basalRates']) {
if (!skipDeleted || entry['deleted'] == false) {
final error = Basal.putFromJson(entry, overrideExisting, source);
if (error != null) {
errors.add(translate(LocalizationKeys.export_error, args: {"data": entry}));
}
}
}
}
if (data.keys.contains('bolusRates')) {
for (var entry in data['bolusRates']) {
if (!skipDeleted || entry['deleted'] == false) {
final error = Bolus.putFromJson(entry, overrideExisting, source);
if (error != null) {
errors.add(translate(LocalizationKeys.export_error, args: {"data": entry}));
}
}
}
}
if (data.keys.contains('glucoseTargets')) {
for (var entry in data['glucoseTargets']) {
if (!skipDeleted || entry['deleted'] == false) {
final error = GlucoseTarget.putFromJson(entry, overrideExisting, source);
if (error != null) {
errors.add(translate(LocalizationKeys.export_error, args: {"data": entry}));
}
}
}
}
if (data.keys.contains('logBoli')) {
for (var entry in data['logBoli']) {
if (!skipDeleted || entry['deleted'] == false) {
final error = LogBolus.putFromJson(entry, overrideExisting, source);
if (error != null) {
errors.add(translate(LocalizationKeys.export_error, args: {"data": entry}));
}
}
}
}
if (data.keys.contains('logEntries')) {
for (var entry in data['logEntries']) {
if (!skipDeleted || entry['deleted'] == false) {
final error = LogEntry.putFromJson(entry, overrideExisting, source);
if (error != null) {
errors.add(translate(LocalizationKeys.export_error, args: {"data": entry}));
}
}
}
}
if (data.keys.contains('logEvents')) {
for (var entry in data['logEvents']) {
if (!skipDeleted || entry['deleted'] == false) {
final error = LogEvent.putFromJson(entry, overrideExisting, source);
if (error != null) {
errors.add(translate(LocalizationKeys.export_error, args: {"data": entry}));
}
}
}
}
if (data.keys.contains('logMeals')) {
for (var entry in data['logMeals']) {
if (!skipDeleted || entry['deleted'] == false) {
final error = LogMeal.putFromJson(entry, overrideExisting, source);
if (error != null) {
errors.add(translate(LocalizationKeys.export_error, args: {"data": entry}));
}
}
}
}
if (data.keys.contains('mealCategories')) {
for (var entry in data['mealCategories']) {
if (!skipDeleted || entry['deleted'] == false) {
final error = MealCategory.putFromJson(entry, overrideExisting, source);
if (error != null) {
errors.add(translate(LocalizationKeys.export_error, args: {"data": entry}));
}
}
}
}
if (data.keys.contains('mealPortionTypes')) {
for (var entry in data['mealPortionTypes']) {
if (!skipDeleted || entry['deleted'] == false) {
final error = MealPortionType.putFromJson(entry, overrideExisting, source);
if (error != null) {
errors.add(translate(LocalizationKeys.export_error, args: {"data": entry}));
}
}
}
}
if (data.keys.contains('mealSources')) {
for (var entry in data['mealSources']) {
if (!skipDeleted || entry['deleted'] == false) {
final error = MealSource.putFromJson(entry, overrideExisting, source);
if (error != null) {
errors.add(translate(LocalizationKeys.export_error, args: {"data": entry}));
}
}
}
}
if (data.keys.contains('meals')) {
for (var entry in data['meals']) {
if (!skipDeleted || entry['deleted'] == false) {
final error = Meal.putFromJson(entry, overrideExisting, source);
if (error != null) {
errors.add(translate(LocalizationKeys.export_error, args: {"data": entry}));
}
}
}
}
} else {
// show confirmation dialog because export is newer than last saved timestamp
}
return errors;
}
static Future<File> exportDataToFile(DateTime timestamp) async {
final appDocDir = await getApplicationDocumentsDirectory();
final appDocPath = appDocDir.path;
final DateFormat formatter = DateFormat(DateFormat.YEAR_MONTH_DAY);
final date = DateTime.now();
final file = File('$appDocPath/diameter_${formatter.format(date)}.json');
file.writeAsStringSync(json.encode(appDataToJson(timestamp)));
return file;
}
static Future<void> exportToGoogleDrive() async {
final login = GoogleSignIn.standard(scopes: [drive.DriveApi.driveScope]);
final GoogleSignInAccount? account = await login.signIn();
if (account != null) {
final authenticateClient = GoogleAuthClient(await account.authHeaders);
final driveApi = drive.DriveApi(authenticateClient);
final timestamp = DateTime.now();
var localFile = await DataExport.exportDataToFile(timestamp);
var media = drive.Media(localFile.openRead(), localFile.lengthSync());
final settings = Settings.get();
settings.lastExportTimestamp = timestamp;
Settings.put(settings);
drive.File driveFile = drive.File()..name = 'diameter.json';
await driveApi.files.create(driveFile, uploadMedia: media);
}
}
static Future<void> importFromGoogleDrive() async {
final login = GoogleSignIn.standard(scopes: [drive.DriveApi.driveScope]);
final GoogleSignInAccount? account = await login.signIn();
if (account != null) {
final authenticateClient = GoogleAuthClient(await account.authHeaders);
final driveApi = drive.DriveApi(authenticateClient);
final timestamp = DateTime.now();
var localFile = await DataExport.exportDataToFile(timestamp);
var media = drive.Media(localFile.openRead(), localFile.lengthSync());
final settings = Settings.get();
settings.lastExportTimestamp = timestamp;
Settings.put(settings);
drive.File driveFile = drive.File()..name = 'diameter.json';
await driveApi.files.create(driveFile, uploadMedia: media);
}
}
}
// class DataExportDialog extends StatefulWidget {
// static const String routeName = '/data-export';
// const DataExportDialog({Key? key}) : super(key: key);
// @override
// _DataExportDialogState createState() => _DataExportDialogState();
// }
// class _DataExportDialogState extends State<DataExportDialog> {
// final ScrollController _scrollController = ScrollController();
// bool _isSaving = false;
// @override
// void dispose() {
// _scrollController.dispose();
// super.dispose();
// }
// @override
// void initState() {
// super.initState();
// }
// void onExport(BuildContext context) async {
// setState(() {
// _isSaving = true;
// });
// Navigator.pop(context);
// setState(() {
// _isSaving = false;
// });
// }
// @override
// Widget build(BuildContext context) {
// return AlertDialog(
// content: Column(
// mainAxisSize: MainAxisSize.min,
// crossAxisAlignment: CrossAxisAlignment.center,
// children: const [
// // CheckboxListTile(
// // value: showChart,
// // onChanged: (_) => setState(() => showChart = !showChart),
// // title: const Text('show Chart'),
// // controlAffinity: ListTileControlAffinity.leading,
// // ),
// ],
// ),
// actions: <Widget>[
// TextButton(
// onPressed: () => Navigator.pop(context),
// child: const Text('CANCEL'),
// ),
// ElevatedButton(
// onPressed: !_isSaving ? () => onExport(context) : null,
// child: const Text('EXPORT'),
// ),
// ]);
// }
// }

453
lib/localization_keys.dart Normal file
View File

@ -0,0 +1,453 @@
// ignore_for_file: constant_identifier_names
class LocalizationKeys {
static const String general = "general";
static const String general_apply = "$general.apply";
static const String general_cancel = "$general.cancel";
static const String general_confirm = "$general.confirm";
static const String general_discard = "$general.discard";
static const String general_confirmDiscard = "$general.confirmDiscard";
static const String general_delete = "$general.delete";
static const String general_confirmDelete = "$general.confirmDelete";
static const String general_save = "$general.save";
static const String general_close = "$general.close";
static const String general_saveAndClose = "$general.saveAndClose";
static const String general_saveAsIs = "$general.saveAsIs";
static const String general_keepEditing = "$general.keepEditing";
static const String general_next = "$general.next";
static const String general_edit = "$general.edit";
static const String general_per = "$general.per";
static const String general_example = "$general.example";
static const String general_suffixes = "$general.suffixes";
static const String general_suffixes_units = "$general_suffixes.units";
static const String general_suffixes_carbs = "$general_suffixes.carbs";
static const String general_suffixes_mins = "$general_suffixes.mins";
static const String general_suffixes_carbsPerU = "$general_suffixes.carbsPerU";
static const String general_suffixes_uPerBreadUnit = "$general_suffixes.uPerBreadUnit";
static const String general_suffixes_uPerGlucose = "$general_suffixes.uPerGlucose";
static const String general_suffixes_perDay = "$general_suffixes.perDay";
static const String navigation = "navigation";
static const String navigation_log = "$navigation.log";
static const String navigation_logEvents = "$navigation.logEvents";
static const String navigation_reports = "$navigation.reports";
static const String navigation_meals = "$navigation.meals";
static const String navigation_basalProfiles = "$navigation.basalProfiles";
static const String navigation_bolusProfiles = "$navigation.bolusProfiles";
static const String navigation_categorization = "$navigation.categorization";
static const String navigation_settings = "$navigation.settings";
static const String accuracy = "accuracy";
static const String accuracy_empty = "$accuracy.empty";
static const String accuracy_saved = "$accuracy.saved";
static const String accuracy_saved_1 = "$accuracy_saved.1";
static const String accuracy_saved_else = "$accuracy_saved.else";
static const String accuracy_deleted = "$accuracy.deleted";
static const String accuracy_confirmDelete = "$accuracy.confirmDelete";
static const String accuracy_new = "$accuracy.new";
static const String accuracy_fields = "$accuracy.fields";
static const String accuracy_fields_name = "$accuracy_fields.name";
static const String accuracy_fields_forPortionSize = "$accuracy_fields.forPortionSize";
static const String accuracy_fields_forCarbsRatio = "$accuracy_fields.forCarbsRatio";
static const String accuracy_fields_confidenceRating = "$accuracy_fields.confidenceRating";
static const String accuracy_fields_notes = "$accuracy_fields.notes";
static const String accuracy_fields_validators = "$accuracy_fields.validators";
static const String accuracy_fields_validators_name = "$accuracy_fields_validators.name";
static const String accuracy_title = "$accuracy.title";
static const String accuracy_detail = "$accuracy.detail";
static const String accuracy_detail_title = "$accuracy_detail.title";
static const String basal = "basal";
static const String basal_empty = "$basal.empty";
static const String basal_warnings = "$basal.warnings";
static const String basal_warnings_duplicate = "$basal_warnings.duplicate";
static const String basal_warnings_overlap = "$basal_warnings.overlap";
static const String basal_warnings_startTimeFirst = "$basal_warnings.startTimeFirst";
static const String basal_warnings_gap = "$basal_warnings.gap";
static const String basal_warnings_endTimeLast = "$basal_warnings.endTimeLast";
static const String basal_saved = "$basal.saved";
static const String basal_saved_1 = "$basal_saved.1";
static const String basal_saved_else = "$basal_saved.else";
static const String basal_deleted = "$basal.deleted";
static const String basal_confirmDelete = "$basal.confirmDelete";
static const String basal_new = "$basal.new";
static const String basal_fields = "$basal.fields";
static const String basal_fields_startTime = "$basal_fields.startTime";
static const String basal_fields_endTime = "$basal_fields.endTime";
static const String basal_fields_units = "$basal_fields.units";
static const String basal_title = "$basal.title";
static const String basalProfile = "basalProfile";
static const String basalProfile_empty = "$basalProfile.empty";
static const String basalProfile_warnings = "$basalProfile.warnings";
static const String basalProfile_warnings_noActive = "$basalProfile_warnings.noActive";
static const String basalProfile_warnings_noActiveOnCreate = "$basalProfile_warnings.noActiveOnCreate";
static const String basalProfile_warnings_multipleActive = "$basalProfile_warnings.multipleActive";
static const String basalProfile_warnings_activeAlreadySet = "$basalProfile_warnings.activeAlreadySet";
static const String basalProfile_warnings_resolve = "$basalProfile_warnings.resolve";
static const String basalProfile_warnings_resolve_activate = "$basalProfile_warnings_resolve.activate";
static const String basalProfile_warnings_resolve_activateCurrent = "$basalProfile_warnings_resolve.activateCurrent";
static const String basalProfile_warnings_resolve_create = "$basalProfile_warnings_resolve.create";
static const String basalProfile_warnings_resolve_createInstead = "$basalProfile_warnings_resolve.createInstead";
static const String basalProfile_warnings_resolve_pick = "$basalProfile_warnings_resolve.pick";
static const String basalProfile_warnings_resolve_ignore = "$basalProfile_warnings_resolve.ignore";
static const String basalProfile_warnings_resolve_deactivateProfile = "$basalProfile_warnings_resolve.deactivateProfile";
static const String basalProfile_warnings_resolve_deactivateOthers = "$basalProfile_warnings_resolve.deactivateOthers";
static const String basalProfile_saved = "$basalProfile.saved";
static const String basalProfile_default = "$basalProfile.default";
static const String basalProfile_active = "$basalProfile.active";
static const String basalProfile_copied = "$basalProfile.copied";
static const String basalProfile_copyOf = "$basalProfile.copyOf";
static const String basalProfile_deleted = "$basalProfile.deleted";
static const String basalProfile_confirmDelete = "$basalProfile.confirmDelete";
static const String basalProfile_activated = "$basalProfile.activated";
static const String basalProfile_new = "$basalProfile.new";
static const String basalProfile_fields = "$basalProfile.fields";
static const String basalProfile_fields_name = "$basalProfile_fields.name";
static const String basalProfile_fields_notes = "$basalProfile_fields.notes";
static const String basalProfile_fields_active = "$basalProfile_fields.active";
static const String basalProfile_fields_validators = "$basalProfile_fields.validators";
static const String basalProfile_fields_validators_name = "$basalProfile_fields_validators.name";
static const String basalProfile_title = "$basalProfile.title";
static const String basalProfile_detail = "$basalProfile.detail";
static const String basalProfile_detail_title = "$basalProfile_detail.title";
static const String basalProfile_detail_tabs = "$basalProfile_detail.tabs";
static const String basalProfile_detail_tabs_profile = "$basalProfile_detail_tabs.profile";
static const String basalProfile_detail_tabs_rates = "$basalProfile_detail_tabs.rates";
static const String bolus = "bolus";
static const String bolus_empty = "$bolus.empty";
static const String bolus_warnings = "$bolus.warnings";
static const String bolus_warnings_duplicate = "$bolus_warnings.duplicate";
static const String bolus_warnings_overlap = "$bolus_warnings.overlap";
static const String bolus_warnings_startTimeFirst = "$bolus_warnings.startTimeFirst";
static const String bolus_warnings_gap = "$bolus_warnings.gap";
static const String bolus_warnings_endTimeLast = "$bolus_warnings.endTimeLast";
static const String bolus_saved = "$bolus.saved";
static const String bolus_saved_1 = "$bolus_saved.1";
static const String bolus_saved_else = "$bolus_saved.else";
static const String bolus_deleted = "$bolus.deleted";
static const String bolus_confirmDelete = "$bolus.confirmDelete";
static const String bolus_new = "$bolus.new";
static const String bolus_fields = "$bolus.fields";
static const String bolus_fields_startTime = "$bolus_fields.startTime";
static const String bolus_fields_endTime = "$bolus_fields.endTime";
static const String bolus_fields_units = "$bolus_fields.units";
static const String bolus_fields_perCarbs = "$bolus_fields.perCarbs";
static const String bolus_fields_perGlucose = "$bolus_fields.perGlucose";
static const String bolus_title = "$bolus.title";
static const String bolusProfile = "bolusProfile";
static const String bolusProfile_empty = "$bolusProfile.empty";
static const String bolusProfile_warnings = "$bolusProfile.warnings";
static const String bolusProfile_warnings_noActive = "$bolusProfile_warnings.noActive";
static const String bolusProfile_warnings_noActiveOnCreate = "$bolusProfile_warnings.noActiveOnCreate";
static const String bolusProfile_warnings_multipleActive = "$bolusProfile_warnings.multipleActive";
static const String bolusProfile_warnings_activeAlreadySet = "$bolusProfile_warnings.activeAlreadySet";
static const String bolusProfile_warnings_resolve = "$bolusProfile_warnings.resolve";
static const String bolusProfile_warnings_resolve_activate = "$bolusProfile_warnings_resolve.activate";
static const String bolusProfile_warnings_resolve_activateCurrent = "$bolusProfile_warnings_resolve.activateCurrent";
static const String bolusProfile_warnings_resolve_create = "$bolusProfile_warnings_resolve.create";
static const String bolusProfile_warnings_resolve_createInstead = "$bolusProfile_warnings_resolve.createInstead";
static const String bolusProfile_warnings_resolve_pick = "$bolusProfile_warnings_resolve.pick";
static const String bolusProfile_warnings_resolve_ignore = "$bolusProfile_warnings_resolve.ignore";
static const String bolusProfile_warnings_resolve_deactivateProfile = "$bolusProfile_warnings_resolve.deactivateProfile";
static const String bolusProfile_warnings_resolve_deactivateOthers = "$bolusProfile_warnings_resolve.deactivateOthers";
static const String bolusProfile_saved = "$bolusProfile.saved";
static const String bolusProfile_default = "$bolusProfile.default";
static const String bolusProfile_active = "$bolusProfile.active";
static const String bolusProfile_copied = "$bolusProfile.copied";
static const String bolusProfile_copyOf = "$bolusProfile.copyOf";
static const String bolusProfile_deleted = "$bolusProfile.deleted";
static const String bolusProfile_confirmDelete = "$bolusProfile.confirmDelete";
static const String bolusProfile_activated = "$bolusProfile.activated";
static const String bolusProfile_new = "$bolusProfile.new";
static const String bolusProfile_fields = "$bolusProfile.fields";
static const String bolusProfile_fields_name = "$bolusProfile_fields.name";
static const String bolusProfile_fields_notes = "$bolusProfile_fields.notes";
static const String bolusProfile_fields_active = "$bolusProfile_fields.active";
static const String bolusProfile_fields_validators = "$bolusProfile_fields.validators";
static const String bolusProfile_fields_validators_name = "$bolusProfile_fields_validators.name";
static const String bolusProfile_title = "$bolusProfile.title";
static const String bolusProfile_detail = "$bolusProfile.detail";
static const String bolusProfile_detail_title = "$bolusProfile_detail.title";
static const String bolusProfile_detail_tabs = "$bolusProfile_detail.tabs";
static const String bolusProfile_detail_tabs_profile = "$bolusProfile_detail_tabs.profile";
static const String bolusProfile_detail_tabs_rates = "$bolusProfile_detail_tabs.rates";
static const String categories = "categories";
static const String log = "log";
static const String log_title = "$log.title";
static const String log_empty = "$log.empty";
static const String log_confirmDelete = "$log.confirmDelete";
static const String log_saved = "$log.saved";
static const String log_deleted = "$log.deleted";
static const String log_new = "$log.new";
static const String log_fields = "$log.fields";
static const String log_fields_date = "$log_fields.date";
static const String log_fields_time = "$log_fields.time";
static const String log_fields_glucose = "$log_fields.glucose";
static const String log_fields_notes = "$log_fields.notes";
static const String log_filter = "$log.filter";
static const String log_filter_startDate = "$log_filter.startDate";
static const String log_filter_endDate = "$log_filter.endDate";
static const String log_filter_minGlucose = "$log_filter.minGlucose";
static const String log_filter_maxGlucose = "$log_filter.maxGlucose";
static const String log_filter_meal = "$log_filter.meal";
static const String log_filter_mealNameContains = "$log_filter.mealNameContains";
static const String log_filter_noteContains = "$log_filter.noteContains";
static const String log_detail = "$log.detail";
static const String log_detail_title = "$log_detail.title";
static const String log_detail_tabs = "$log_detail.tabs";
static const String log_detail_tabs_general = "$log_detail_tabs.general";
static const String log_detail_tabs_meal = "$log_detail_tabs.meal";
static const String log_detail_tabs_meal_title = "$log_detail_tabs_meal.title";
static const String log_detail_tabs_meal_saved = "$log_detail_tabs_meal.saved";
static const String log_detail_tabs_meal_new = "$log_detail_tabs_meal.new";
static const String log_detail_tabs_meal_empty = "$log_detail_tabs_meal.empty";
static const String log_detail_tabs_meal_confirmDelete = "$log_detail_tabs_meal.confirmDelete";
static const String log_detail_tabs_meal_deleted = "$log_detail_tabs_meal.deleted";
static const String log_detail_tabs_meal_detail = "$log_detail_tabs_meal.detail";
static const String log_detail_tabs_meal_detail_title = "$log_detail_tabs_meal_detail.title";
static const String log_detail_tabs_meal_detail_additionalFields = "$log_detail_tabs_meal_detail.additionalFields";
static const String log_detail_tabs_meal_detail_fields = "$log_detail_tabs_meal_detail.fields";
static const String log_detail_tabs_meal_detail_fields_meal = "$log_detail_tabs_meal_detail_fields.meal";
static const String log_detail_tabs_meal_detail_fields_name = "$log_detail_tabs_meal_detail_fields.name";
static const String log_detail_tabs_meal_detail_fields_amount = "$log_detail_tabs_meal_detail_fields.amount";
static const String log_detail_tabs_meal_detail_fields_portionSize = "$log_detail_tabs_meal_detail_fields.portionSize";
static const String log_detail_tabs_meal_detail_fields_carbsRatio = "$log_detail_tabs_meal_detail_fields.carbsRatio";
static const String log_detail_tabs_meal_detail_fields_totalCarbs = "$log_detail_tabs_meal_detail_fields.totalCarbs";
static const String log_detail_tabs_meal_detail_fields_setManually = "$log_detail_tabs_meal_detail_fields.setManually";
static const String log_detail_tabs_meal_detail_fields_notes = "$log_detail_tabs_meal_detail_fields.notes";
static const String log_detail_tabs_meal_detail_fields_mealSource = "$log_detail_tabs_meal_detail_fields.mealSource";
static const String log_detail_tabs_meal_detail_fields_mealCategory = "$log_detail_tabs_meal_detail_fields.mealCategory";
static const String log_detail_tabs_meal_detail_fields_mealPortionType = "$log_detail_tabs_meal_detail_fields.mealPortionType";
static const String log_detail_tabs_meal_detail_fields_portionSizeAccuracy = "$log_detail_tabs_meal_detail_fields.portionSizeAccuracy";
static const String log_detail_tabs_meal_detail_fields_carbsRatioAccuracy = "$log_detail_tabs_meal_detail_fields.carbsRatioAccuracy";
static const String log_detail_tabs_meal_detail_fields_validators = "$log_detail_tabs_meal_detail_fields.validators";
static const String log_detail_tabs_meal_detail_fields_validators_name = "$log_detail_tabs_meal_detail_fields_validators.name";
static const String log_detail_tabs_bolus = "$log_detail_tabs.bolus";
static const String log_detail_tabs_bolus_title = "$log_detail_tabs_bolus.title";
static const String log_detail_tabs_bolus_saved = "$log_detail_tabs_bolus.saved";
static const String log_detail_tabs_bolus_new = "$log_detail_tabs_bolus.new";
static const String log_detail_tabs_bolus_deleted = "$log_detail_tabs_bolus.deleted";
static const String log_detail_tabs_bolus_confirmDelete = "$log_detail_tabs_bolus.confirmDelete";
static const String log_detail_tabs_bolus_delayedBy = "$log_detail_tabs_bolus.delayedBy";
static const String log_detail_tabs_bolus_forMeal = "$log_detail_tabs_bolus.forMeal";
static const String log_detail_tabs_bolus_toCorrect = "$log_detail_tabs_bolus.toCorrect";
static const String log_detail_tabs_bolus_empty = "$log_detail_tabs_bolus.empty";
static const String log_detail_tabs_bolus_detail = "$log_detail_tabs_bolus.detail";
static const String log_detail_tabs_bolus_detail_title = "$log_detail_tabs_bolus_detail.title";
static const String log_detail_tabs_bolus_detail_fields = "$log_detail_tabs_bolus_detail.fields";
static const String log_detail_tabs_bolus_detail_fields_units = "$log_detail_tabs_bolus_detail_fields.units";
static const String log_detail_tabs_bolus_detail_fields_setManually = "$log_detail_tabs_bolus_detail_fields.setManually";
static const String log_detail_tabs_bolus_detail_fields_forGlucose = "$log_detail_tabs_bolus_detail_fields.forGlucose";
static const String log_detail_tabs_bolus_detail_fields_forMeal = "$log_detail_tabs_bolus_detail_fields.forMeal";
static const String log_detail_tabs_bolus_detail_fields_current = "$log_detail_tabs_bolus_detail_fields.current";
static const String log_detail_tabs_bolus_detail_fields_target = "$log_detail_tabs_bolus_detail_fields.target";
static const String log_detail_tabs_bolus_detail_fields_correction = "$log_detail_tabs_bolus_detail_fields.correction";
static const String log_detail_tabs_bolus_detail_fields_meal = "$log_detail_tabs_bolus_detail_fields.meal";
static const String log_detail_tabs_bolus_detail_fields_carbs = "$log_detail_tabs_bolus_detail_fields.carbs";
static const String log_detail_tabs_bolus_detail_fields_delayedBolusDuration = "$log_detail_tabs_bolus_detail_fields.delayedBolusDuration";
static const String log_detail_tabs_bolus_detail_fields_immediateBolus = "$log_detail_tabs_bolus_detail_fields.immediateBolus";
static const String log_detail_tabs_bolus_detail_fields_delayedBolus = "$log_detail_tabs_bolus_detail_fields.delayedBolus";
static const String meal = "meal";
static const String meal_title = "$meal.title";
static const String meal_empty = "$meal.empty";
static const String meal_confirmDelete = "$meal.confirmDelete";
static const String meal_saved = "$meal.saved";
static const String meal_deleted = "$meal.deleted";
static const String meal_new = "$meal.new";
static const String meal_fields = "$meal.fields";
static const String meal_fields_name = "$meal_fields.name";
static const String meal_fields_mealSource = "$meal_fields.mealSource";
static const String meal_fields_mealPortionType = "$meal_fields.mealPortionType";
static const String meal_fields_notes = "$meal_fields.notes";
static const String meal_fields_carbsRatio = "$meal_fields.carbsRatio";
static const String meal_fields_setManually = "$meal_fields.setManually";
static const String meal_fields_portionSize = "$meal_fields.portionSize";
static const String meal_fields_carbsPerPortion = "$meal_fields.carbsPerPortion";
static const String meal_fields_delay = "$meal_fields.delay";
static const String meal_fields_delay_title = "$meal_fields_delay.title";
static const String meal_fields_delay_duration = "$meal_fields_delay.duration";
static const String meal_fields_additional = "$meal_fields.additional";
static const String meal_fields_additional_title = "$meal_fields_additional.title";
static const String meal_fields_additional_mealCategory = "$meal_fields_additional.mealCategory";
static const String meal_fields_additional_portionSizeAccuracy = "$meal_fields_additional.portionSizeAccuracy";
static const String meal_fields_additional_carbsRatioAccuracy = "$meal_fields_additional.carbsRatioAccuracy";
static const String meal_fields_validators = "$meal_fields.validators";
static const String meal_fields_validators_name = "$meal_fields_validators.name";
static const String meal_detail = "$meal.detail";
static const String meal_detail_title = "$meal_detail.title";
static const String mealSource = "mealSource";
static const String mealSource_title = "$mealSource.title";
static const String mealSource_empty = "$mealSource.empty";
static const String mealSource_confirmDelete = "$mealSource.confirmDelete";
static const String mealSource_saved = "$mealSource.saved";
static const String mealSource_deleted = "$mealSource.deleted";
static const String mealSource_new = "$mealSource.new";
static const String mealSource_fields = "$mealSource.fields";
static const String mealSource_fields_name = "$mealSource_fields.name";
static const String mealSource_fields_notes = "$mealSource_fields.notes";
static const String mealSource_fields_defaultCarbsRatioAccuracy = "$mealSource_fields.defaultCarbsRatioAccuracy";
static const String mealSource_fields_defaultPortionSizeAccuracy = "$mealSource_fields.defaultPortionSizeAccuracy";
static const String mealSource_fields_defaultMealCategory = "$mealSource_fields.defaultMealCategory";
static const String mealSource_fields_defaultMealPortionType = "$mealSource_fields.defaultMealPortionType";
static const String mealSource_fields_validators = "$mealSource_fields.validators";
static const String mealSource_fields_validators_name = "$mealSource_fields_validators.name";
static const String mealSource_detail = "$mealSource.detail";
static const String mealSource_detail_title = "$mealSource_detail.title";
static const String mealCategory = "mealCategory";
static const String mealCategory_title = "$mealCategory.title";
static const String mealCategory_empty = "$mealCategory.empty";
static const String mealCategory_confirmDelete = "$mealCategory.confirmDelete";
static const String mealCategory_saved = "$mealCategory.saved";
static const String mealCategory_deleted = "$mealCategory.deleted";
static const String mealCategory_new = "$mealCategory.new";
static const String mealCategory_fields = "$mealCategory.fields";
static const String mealCategory_fields_name = "$mealCategory_fields.name";
static const String mealCategory_fields_notes = "$mealCategory_fields.notes";
static const String mealCategory_fields_validators = "$mealCategory_fields.validators";
static const String mealCategory_fields_validators_name = "$mealCategory_fields_validators.name";
static const String mealCategory_detail = "$mealCategory.detail";
static const String mealCategory_detail_title = "$mealCategory_detail.title";
static const String portionType = "portionType";
static const String portionType_title = "$portionType.title";
static const String portionType_empty = "$portionType.empty";
static const String portionType_confirmDelete = "$portionType.confirmDelete";
static const String portionType_saved = "$portionType.saved";
static const String portionType_deleted = "$portionType.deleted";
static const String portionType_new = "$portionType.new";
static const String portionType_fields = "$portionType.fields";
static const String portionType_fields_name = "$portionType_fields.name";
static const String portionType_fields_notes = "$portionType_fields.notes";
static const String portionType_fields_validators = "$portionType_fields.validators";
static const String portionType_fields_validators_name = "$portionType_fields_validators.name";
static const String portionType_detail = "$portionType.detail";
static const String portionType_detail_title = "$portionType_detail.title";
static const String eventType = "eventType";
static const String eventType_title = "$eventType.title";
static const String eventType_empty = "$eventType.empty";
static const String eventType_saved = "$eventType.saved";
static const String eventType_deleted = "$eventType.deleted";
static const String eventType_new = "$eventType.new";
static const String eventType_fields = "$eventType.fields";
static const String eventType_fields_name = "$eventType_fields.name";
static const String eventType_fields_notes = "$eventType_fields.notes";
static const String eventType_fields_hasEndTime = "$eventType_fields.hasEndTime";
static const String eventType_fields_defaultReminderDuration = "$eventType_fields.defaultReminderDuration";
static const String eventType_fields_bolusProfile = "$eventType_fields.bolusProfile";
static const String eventType_fields_basalProfile = "$eventType_fields.basalProfile";
static const String eventType_fields_validators = "$eventType_fields.validators";
static const String eventType_fields_validators_name = "$eventType_fields_validators.name";
static const String eventType_detail = "$eventType.detail";
static const String eventType_detail_title = "$eventType_detail.title";
static const String event = "event";
static const String event_warnings = "$event.warnings";
static const String event_warnings_duplicate = "$event_warnings.duplicate";
static const String event_title = "$event.title";
static const String event_titleActive = "$event.titleAcive";
static const String event_empty = "$event.empty";
static const String event_emptyActive = "$event.emptyActive";
static const String event_saved = "$event.saved";
static const String event_deleted = "$event.deleted";
static const String event_confirmDelete = "$event.confirmDelete";
static const String event_end = "$event.end";
static const String event_ended = "$event.ended";
static const String event_confirmEnd = "$event.confirmEnd";
static const String event_new = "$event.new";
static const String event_fields = "$event.fields";
static const String event_fields_eventType = "$event_fields.eventType";
static const String event_fields_startDate = "$event_fields.startDate";
static const String event_fields_endDate = "$event_fields.endDate";
static const String event_fields_date = "$event_fields.date";
static const String event_fields_startTime = "$event_fields.startTime";
static const String event_fields_endTime = "$event_fields.endTime";
static const String event_fields_time = "$event_fields.time";
static const String event_fields_notes = "$event_fields.notes";
static const String event_fields_hasEndTime = "$event_fields.hasEndTime";
static const String event_fields_reminderDuration = "$event_fields.reminderDuration";
static const String event_fields_bolusProfile = "$event_fields.bolusProfile";
static const String event_fields_basalProfile = "$event_fields.basalProfile";
static const String event_detail = "$event.detail";
static const String event_detail_title = "$event_detail.title";
static const String settings = "settings";
static const String settings_title = "$settings.title";
static const String settings_reset = "$settings.reset";
static const String settings_resetAll = "$settings.resetAll";
static const String settings_confirmReset = "$settings.confirmReset";
static const String settings_updated = "$settings.updated";
static const String settings_sections = "$settings.sections";
static const String settings_sections_measurements = "$settings_sections.measurements";
static const String settings_sections_confirmation = "$settings_sections.confirmation";
static const String settings_sections_dateTimeFormat = "$settings_sections.dateTimeFormat";
static const String settings_fields = "$settings.fields";
static const String settings_fields_nutritionMeasurement = "$settings_fields.nutritionMeasurement";
static const String settings_fields_glucoseMeasurement = "$settings_fields.glucoseMeasurement";
static const String settings_fields_targetGlucose = "$settings_fields.targetGlucose";
static const String settings_fields_insulinIncrement = "$settings_fields.insulinIncrement";
static const String settings_fields_nutritionIncrement = "$settings_fields.nutritionIncrement";
static const String settings_fields_mmolLIncrement = "$settings_fields.mmolLIncrement";
static const String settings_fields_onlyDisplayActive = "$settings_fields.onlyDisplayActive";
static const String settings_fields_displayBothDetail = "$settings_fields.displayBothDetail";
static const String settings_fields_displayBothList = "$settings_fields.displayBothList";
static const String settings_fields_confirmOnCancel = "$settings_fields.confirmOnCancel";
static const String settings_fields_confirmOnDelete = "$settings_fields.confirmOnDelete";
static const String settings_fields_confirmOnEndEvent = "$settings_fields.confirmOnEndEvent";
static const String settings_fields_dateFormat = "$settings_fields.dateFormat";
static const String settings_fields_longDateFormat = "$settings_fields.longDateFormat";
static const String settings_fields_timeFormat = "$settings_fields.timeFormat";
static const String settings_fields_longTimeFormat = "$settings_fields.longTimeFormat";
static const String reports = "reports";
static const String reports_title = "$reports.title";
static const String reports_ids = "$reports.ids";
static const String reports_ids_glucose = "$reports_ids.glucose";
static const String reports_ids_carbs = "$reports_ids.carbs";
static const String reports_ids_basal = "$reports_ids.basal";
static const String reports_ids_bolus = "$reports_ids.bolus";
static const String reports_dailyCharts = "$reports.dailyCharts";
static const String reports_dailyCharts_showChart = "$reports_dailyCharts.showChart";
static const String reports_dailyCharts_showBolus = "$reports_dailyCharts.showBolus";
static const String reports_dailyCharts_showBasal = "$reports_dailyCharts.showBasal";
static const String reports_dailyCharts_showMeals = "$reports_dailyCharts.showMeals";
static const String reports_dailyCharts_empty = "$reports_dailyCharts.empty";
static const String reports_sections = "$reports.sections";
static const String reports_sections_dailyReport = "$reports_sections.dailyReport";
static const String reports_sections_pdfReport = "$reports_sections.pdfReport";
static const String reports_export = "$reports.export";
static const String reports_export_title = "$reports_export.title";
static const String reports_export_singleDate = "$reports_export.singleDate";
static const String reports_export_range = "$reports_export.range";
static const String reports_export_date = "$reports_export.date";
static const String reports_export_endDate = "$reports_export.endDate";
static const String reports_export_showChart = "$reports_export.showChart";
static const String reports_export_showBolus = "$reports_export.showBolus";
static const String reports_export_showBasal = "$reports_export.showBasal";
static const String reports_export_showMeals = "$reports_export.showMeals";
static const String reports_export_showTable = "$reports_export.showTable";
static const String reports_export_export = "$reports_export.export";
static const String reports_export_tableHeaders = "$reports_export.tableHeaders";
static const String reports_export_tableHeaders_time = "$reports_export_tableHeaders.time";
static const String reports_export_tableHeaders_glucose = "$reports_export_tableHeaders.glucose";
static const String reports_export_tableHeaders_bolus = "$reports_export_tableHeaders.bolus";
static const String reports_export_tableHeaders_notes = "$reports_export_tableHeaders.notes";
static const String reports_export_tableHeaders_meals = "$reports_export_tableHeaders.meals";
static const String reports_export_tableHeaders_portionSize = "$reports_export_tableHeaders.portionSize";
static const String reports_export_tableHeaders_carbs = "$reports_export_tableHeaders.carbs";
static const String reports_export_tableHeaders_mealBolus = "$reports_export_tableHeaders.mealBolus";
static const String reports_export_tableHeaders_mealNotes = "$reports_export_tableHeaders.mealNotes";
static const String export = "export";
static const String export_error = "$export.error";
}

View File

@ -25,30 +25,62 @@ import 'package:diameter/screens/meal/meal_list.dart';
import 'package:diameter/screens/reports/export.dart';
import 'package:diameter/screens/reports/reports.dart';
import 'package:diameter/settings.dart';
import 'package:diameter/data_export.dart';
import 'package:flutter/material.dart';
import 'package:diameter/screens/basal/basal_profile_list.dart';
import 'package:diameter/screens/bolus/bolus_profile_list.dart';
import 'package:diameter/navigation.dart';
import 'package:objectbox/objectbox.dart';
import 'package:flutter_translate/flutter_translate.dart';
late ObjectBox objectBox;
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
DataExport.exportToGoogleDrive();
objectBox = await ObjectBox.create();
Sync.isAvailable();
SyncClient syncClient = Sync.client(
objectBox.store,
'ws://192.168.1.184:9999',
SyncCredentials.sharedSecretString(secret)
);
SyncClient syncClient = Sync.client(objectBox.store,
'ws://192.168.1.184:9999', SyncCredentials.sharedSecretString(secret));
syncClient.start();
syncClient.requestUpdates(subscribeForFuturePushes: false);
DataExport.exportToGoogleDrive();
var delegate = await LocalizationDelegate.create(
fallbackLocale: 'en_US',
supportedLocales: ['en_US', 'de'],
);
runApp(
GestureDetector(
LocalizedApp(delegate, const App()),
);
}
class App extends StatelessWidget {
const App({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
var localizationDelegate = LocalizedApp.of(context).delegate;
return LocalizationProvider(
state: LocalizationProvider.of(context).state,
child: GestureDetector(
onTap: () => FocusManager.instance.primaryFocus?.unfocus(),
child: MaterialApp(
localizationsDelegates: [
localizationDelegate,
],
supportedLocales: const [
Locale('en', 'US'),
Locale('de', 'DE'),
],
// localizationDelegate.supportedLocales,
locale: localizationDelegate.currentLocale,
theme: AppTheme.makeTheme(AppTheme.lightTheme),
darkTheme: AppTheme.makeTheme(AppTheme.darkTheme),
themeMode: Settings.themeMode,
@ -86,4 +118,5 @@ Future<void> main() async {
),
),
);
}
}

View File

@ -10,11 +10,13 @@ class Accuracy {
// properties
int id;
bool deleted;
@Unique()
String value;
bool forCarbsRatio;
bool forPortionSize;
int? confidenceRating;
String? notes;
String? source;
// constructor
Accuracy({
@ -25,6 +27,7 @@ class Accuracy {
this.forPortionSize = false,
this.confidenceRating,
this.notes,
this.source,
});
// methods
@ -60,10 +63,14 @@ class Accuracy {
}
static void reorder(Accuracy accuracy, int? newPosition) {
QueryBuilder<Accuracy> all = box.query(Accuracy_.deleted.equals(false).and(Accuracy_.id.notEquals(accuracy.id)))
QueryBuilder<Accuracy> all = box.query(Accuracy_.deleted
.equals(false)
.and(Accuracy_.id.notEquals(accuracy.id)))
..order(Accuracy_.confidenceRating);
List<Accuracy> accuracies = all.build().find();
newPosition == null || newPosition >= accuracies.length ? accuracies.add(accuracy) : accuracies.insert(newPosition, accuracy);
newPosition == null || newPosition >= accuracies.length
? accuracies.add(accuracy)
: accuracies.insert(newPosition, accuracy);
box.putMany(accuracies.map((item) {
item.confidenceRating = accuracies.indexOf(item);
return item;
@ -74,4 +81,31 @@ class Accuracy {
String toString() {
return value;
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['id'] = id;
data['deleted'] = deleted;
data['value'] = value;
data['forCarbsRatio'] = forCarbsRatio;
data['forPortionSize'] = forPortionSize;
data['confidenceRating'] = confidenceRating;
data['notes'] = notes;
return data;
}
static String? putFromJson(Map<String, dynamic> json, bool overrideExisting, String? source) {
final accuracy = Accuracy(
id: overrideExisting ? json['id'] : 0,
deleted: json['deleted'],
value: json['value'],
forCarbsRatio: json['forCarbsRatio'],
forPortionSize: json['forPortionSize'],
confidenceRating: json['confidenceRating'],
notes: json['notes'],
source: source,
);
Accuracy.put(accuracy);
return null;
}
}

View File

@ -18,6 +18,7 @@ class Basal {
@Property(type: PropertyType.date)
DateTime endTime;
double units;
String? source;
// relations
final basalProfile = ToOne<BasalProfile>();
@ -29,6 +30,7 @@ class Basal {
required this.startTime,
required this.endTime,
this.units = 0,
this.source,
});
// methods
@ -43,6 +45,13 @@ class Basal {
}
}
static void removeAllForProfile(int id) {
box.putMany(getAllForProfile(id).map((item) {
item.deleted = true;
return item;
}).toList());
}
static List<Basal> getAllForProfile(int id) {
QueryBuilder<Basal> builder = box.query(Basal_.deleted.equals(false))
..order(Basal_.startTime);
@ -94,4 +103,42 @@ class Basal {
String toString() {
return DateTimeUtils.displayTime(startTime);
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['id'] = id;
data['deleted'] = deleted;
data['startTime'] = startTime.toIso8601String();
data['endTime'] = endTime.toIso8601String();
data['units'] = units;
data['basalProfile'] = basalProfile.targetId;
return data;
}
static String? putFromJson(Map<String, dynamic> json, bool overrideExisting, String? source) {
DateTime? startTime = DateTime.tryParse(json['startTime']);
DateTime? endTime = DateTime.tryParse(json['endTime']);
if (startTime == null || endTime == null) {
return startTime == null
? endTime == null
? 'start and end time are missing'
: 'start time is missing'
: 'end time is missing';
}
final basal = Basal(
id: overrideExisting ? json['id'] : 0,
deleted: json['deleted'],
startTime: startTime,
endTime: endTime,
units: json['units'],
source: source,
);
basal.basalProfile.targetId = json['basalProfile'];
Basal.put(basal);
return null;
}
}

View File

@ -1,4 +1,5 @@
import 'package:diameter/main.dart';
import 'package:diameter/models/basal.dart';
import 'package:diameter/models/log_event.dart';
import 'package:objectbox/objectbox.dart';
import 'package:diameter/objectbox.g.dart' show BasalProfile_;
@ -11,9 +12,11 @@ class BasalProfile {
// properties
int id;
bool deleted;
@Unique()
String name;
bool active;
String? notes;
String? source;
// constructor
BasalProfile({
@ -22,6 +25,7 @@ class BasalProfile {
this.name = '',
this.active = false,
this.notes,
this.source,
});
// methods
@ -29,7 +33,8 @@ class BasalProfile {
static void put(BasalProfile basalProfile) => box.put(basalProfile);
static List<BasalProfile> getAll() {
QueryBuilder<BasalProfile> all = box.query(BasalProfile_.deleted.equals(false))
QueryBuilder<BasalProfile> all = box
.query(BasalProfile_.deleted.equals(false))
..order(BasalProfile_.name);
return all.build().find();
}
@ -38,13 +43,16 @@ class BasalProfile {
final item = box.get(id);
if (item != null) {
item.deleted = true;
Basal.removeAllForProfile(id);
box.put(item);
}
}
static int activeCount() {
Query<BasalProfile> query = box
.query(BasalProfile_.active.equals(true) & BasalProfile_.deleted.equals(false)).build();
.query(BasalProfile_.active.equals(true) &
BasalProfile_.deleted.equals(false))
.build();
return query.find().length;
}
@ -58,13 +66,15 @@ class BasalProfile {
static BasalProfile? getActive(DateTime? dateTime) {
if (dateTime != null) {
List<LogEvent> activeEvents = LogEvent.getAllActiveForTime(dateTime)
.where((event) => event.basalProfile.target != null).toList();
.where((event) => event.basalProfile.target != null)
.toList();
if (activeEvents.length > 1) {
final now = DateTime.now();
activeEvents =
activeEvents.where((item) => !activeEvents.any((other) =>
item.time.isBefore(other.time) || (item.endTime ?? now).isAfter(other.endTime ?? now)
)).toList();
activeEvents = activeEvents
.where((item) => !activeEvents.any((other) =>
item.time.isBefore(other.time) ||
(item.endTime ?? now).isAfter(other.endTime ?? now)))
.toList();
}
if (activeEvents.length == 1) {
return activeEvents.single.basalProfile.target;
@ -84,4 +94,28 @@ class BasalProfile {
String toString() {
return name;
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['id'] = id;
data['deleted'] = deleted;
data['name'] = name;
data['active'] = active;
data['notes'] = notes;
return data;
}
static String? putFromJson(
Map<String, dynamic> json, bool overrideExisting, String? source) {
final basalProfile = BasalProfile(
id: overrideExisting ? json['id'] : 0,
deleted: json['deleted'] == 'true',
name: json['name'],
active: json['active'] == 'true',
notes: json['notes'],
source: source,
);
BasalProfile.put(basalProfile);
return null;
}
}

View File

@ -21,6 +21,7 @@ class Bolus {
double carbs;
int? mgPerDl;
double? mmolPerL;
String? source;
// relations
final bolusProfile = ToOne<BolusProfile>();
@ -35,6 +36,7 @@ class Bolus {
this.carbs = 0,
this.mgPerDl,
this.mmolPerL,
this.source,
});
// methods
@ -56,6 +58,13 @@ class Bolus {
}
}
static void removeAllForProfile(int id) {
box.putMany(getAllForProfile(id).map((item) {
item.deleted = true;
return item;
}).toList());
}
static Bolus? getRateForTime(DateTime? dateTime) {
if (dateTime != null) {
final bolusProfile = BolusProfile.getActive(dateTime);
@ -81,4 +90,48 @@ class Bolus {
String toString() {
return DateTimeUtils.displayTime(startTime);
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['id'] = id;
data['deleted'] = deleted;
data['startTime'] = startTime.toIso8601String();
data['endTime'] = endTime.toIso8601String();
data['units'] = units;
data['carbs'] = carbs;
data['mgPerDl'] = mgPerDl;
data['mmolPerL'] = mmolPerL;
data['bolusProfile'] = bolusProfile.targetId;
return data;
}
static String? putFromJson(Map<String, dynamic> json, bool overrideExisting, String? source) {
DateTime? startTime = DateTime.tryParse(json['startTime']);
DateTime? endTime = DateTime.tryParse(json['endTime']);
if (startTime == null || endTime == null) {
return startTime == null
? endTime == null
? 'start and end time are missing'
: 'start time is missing'
: 'end time is missing';
}
final bolus = Bolus(
id: overrideExisting ? json['id'] : 0,
deleted: json['deleted'],
startTime: startTime,
endTime: endTime,
units: json['units'],
carbs: json['carbs'],
mgPerDl: json['mgPerDl'],
mmolPerL: json['mmolPerL'],
source: source,
);
bolus.bolusProfile.targetId = json['bolusProfile'];
Bolus.put(bolus);
return null;
}
}

View File

@ -1,4 +1,5 @@
import 'package:diameter/main.dart';
import 'package:diameter/models/bolus.dart';
import 'package:diameter/models/log_event.dart';
import 'package:objectbox/objectbox.dart';
import 'package:diameter/objectbox.g.dart' show BolusProfile_;
@ -11,9 +12,11 @@ class BolusProfile {
// properties
int id;
bool deleted;
@Unique()
String name;
bool active;
String? notes;
String? source;
// constructor
BolusProfile({
@ -22,6 +25,7 @@ class BolusProfile {
this.name = '',
this.active = false,
this.notes,
this.source,
});
// methods
@ -38,6 +42,7 @@ class BolusProfile {
final item = box.get(id);
if (item != null) {
item.deleted = true;
Bolus.removeAllForProfile(id);
box.put(item);
}
}
@ -87,4 +92,29 @@ class BolusProfile {
String toString() {
return name;
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['id'] = id;
data['deleted'] = deleted;
data['name'] = name;
data['active'] = active;
data['notes'] = notes;
return data;
}
static String? putFromJson(
Map<String, dynamic> json, bool overrideExisting, String? source) {
final bolusProfile = BolusProfile(
id: overrideExisting ? json['id'] : 0,
deleted: json['deleted'] == 'true',
name: json['name'],
active: json['active'],
notes: json['notes'],
source: source,
);
BolusProfile.put(bolusProfile);
return null;
}
}

View File

@ -17,6 +17,7 @@ class GlucoseTarget {
double fromMmolPerL;
double toMmolPerL;
int color;
String? source;
// constructor
GlucoseTarget({
@ -27,6 +28,7 @@ class GlucoseTarget {
required this.fromMmolPerL,
required this.toMmolPerL,
required this.color,
this.source,
});
// methods
@ -49,11 +51,15 @@ class GlucoseTarget {
if (mgPerDl > 0 &&
(mmolPerL == 0 ||
Settings.glucoseMeasurement == GlucoseMeasurement.mgPerDl)) {
condition = GlucoseTarget_.fromMgPerDL.lessOrEqual(mgPerDl).and(GlucoseTarget_.toMgPerDl.greaterOrEqual(mgPerDl));
condition = GlucoseTarget_.fromMgPerDL
.lessOrEqual(mgPerDl)
.and(GlucoseTarget_.toMgPerDl.greaterOrEqual(mgPerDl));
} else if (mmolPerL > 0 &&
(mgPerDl == 0 ||
Settings.glucoseMeasurement == GlucoseMeasurement.mmolPerL)) {
condition = GlucoseTarget_.fromMmolPerL.lessOrEqual(mmolPerL).and(GlucoseTarget_.toMmolPerL.greaterOrEqual(mmolPerL));
condition = GlucoseTarget_.fromMmolPerL
.lessOrEqual(mmolPerL)
.and(GlucoseTarget_.toMmolPerL.greaterOrEqual(mmolPerL));
} else {
return Colors.black;
}
@ -119,4 +125,33 @@ class GlucoseTarget {
];
box.putMany(defaultTargets);
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['id'] = id;
data['deleted'] = deleted;
data['fromMgPerDL'] = fromMgPerDL;
data['toMgPerDl'] = toMgPerDl;
data['fromMmolPerL'] = fromMmolPerL;
data['toMmolPerL'] = toMmolPerL;
data['color'] = color;
return data;
}
static String? putFromJson(Map<String, dynamic> json, bool overrideExisting, String? source) {
final glucoseTarget = GlucoseTarget(
id: overrideExisting ? json['id'] : 0,
deleted: json['deleted'],
fromMgPerDL: json['fromMgPerDL'],
toMgPerDl: json['toMgPerDl'],
fromMmolPerL: json['fromMmolPerL'],
toMmolPerL: json['toMmolPerL'],
color: json['color'],
source: source,
);
GlucoseTarget.put(glucoseTarget);
return null;
}
}

View File

@ -24,6 +24,7 @@ class LogBolus {
double? mmolPerLCorrection;
bool setManually;
String? notes;
String? source;
// relations
final logEntry = ToOne<LogEntry>();
@ -45,6 +46,7 @@ class LogBolus {
this.mmolPerLCorrection,
this.setManually = false,
this.notes,
this.source,
});
// methods
@ -72,8 +74,7 @@ class LogBolus {
}
static bool bolusForMealExists(int id) {
QueryBuilder<LogBolus> builder = box.query(LogBolus_.deleted
.equals(false));
QueryBuilder<LogBolus> builder = box.query(LogBolus_.deleted.equals(false));
builder.link(LogBolus_.meal, LogMeal_.id.equals(id));
return builder.build().find().isNotEmpty;
}
@ -86,8 +87,63 @@ class LogBolus {
}
}
static void removeAllForEntry(int id) {
box.putMany(getAllForEntry(id).map((item) {
item.deleted = true;
return item;
}).toList());
}
@override
String toString() {
return units.toString();
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['id'] = id;
data['deleted'] = deleted;
data['units'] = units;
data['carbs'] = carbs;
data['delay'] = delay;
data['mgPerDlCurrent'] = mgPerDlCurrent;
data['mgPerDlTarget'] = mgPerDlTarget;
data['mgPerDlCorrection'] = mgPerDlCorrection;
data['mmolPerLCurrent'] = mmolPerLCurrent;
data['mmolPerLTarget'] = mmolPerLTarget;
data['mmolPerLCorrection'] = mmolPerLCorrection;
data['setManually'] = setManually;
data['notes'] = notes;
data['logEntry'] = logEntry.targetId;
data['rate'] = rate.targetId;
data['meal'] = meal.targetId;
return data;
}
static String? putFromJson(Map<String, dynamic> json, bool overrideExisting, String? source) {
final logBolus = LogBolus(
id: overrideExisting ? json['id'] : 0,
deleted: json['deleted'],
units: json['units'],
carbs: json['carbs'],
delay: json['delay'],
mgPerDlCurrent: json['mgPerDlCurrent'],
mgPerDlTarget: json['mgPerDlTarget'],
mgPerDlCorrection: json['mgPerDlCorrection'],
mmolPerLCurrent: json['mmolPerLCurrent'],
mmolPerLTarget: json['mmolPerLTarget'],
mmolPerLCorrection: json['mmolPerLCorrection'],
setManually: json['setManually'],
notes: json['notes'],
source: source,
);
logBolus.logEntry.targetId = json['logEntry'];
logBolus.rate.targetId = json['rate'];
logBolus.meal.targetId = json['meal'];
LogBolus.put(logBolus);
return null;
}
}

View File

@ -1,5 +1,6 @@
import 'package:diameter/main.dart';
import 'package:diameter/models/log_bolus.dart';
import 'package:diameter/models/log_meal.dart';
import 'package:diameter/models/settings.dart';
import 'package:diameter/utils/date_time_utils.dart';
import 'package:objectbox/objectbox.dart';
@ -19,6 +20,7 @@ class LogEntry {
double? mmolPerL;
double? glucoseTrend;
String? notes;
String? source;
// constructor
LogEntry({
@ -29,6 +31,7 @@ class LogEntry {
this.mmolPerL,
this.glucoseTrend,
this.notes,
this.source,
});
// methods
@ -40,6 +43,8 @@ class LogEntry {
final item = box.get(id);
if (item != null) {
item.deleted = true;
LogMeal.removeAllForEntry(id);
LogBolus.removeAllForEntry(id);
box.put(item);
}
}
@ -64,8 +69,77 @@ class LogEntry {
}).toList();
}
static List<LogEntry> getAllByFilter({
DateTime? startDate,
DateTime? endDate,
int? minMgPerDl,
int? maxMgPerDl,
double? minMmolPerL,
double? maxMmolPerL,
int? mealId,
String? mealName,
String? note,
}) {
DateTime start = startDate ?? DateTime(2000, 1, 1);
DateTime end = endDate ?? DateTime.now();
QueryBuilder<LogEntry> builder = box.query(LogEntry_.deleted.equals(false) &
(Settings.glucoseMeasurement == GlucoseMeasurement.mgPerDl
? LogEntry_.mgPerDl.between(
minMgPerDl ?? 0, maxMgPerDl ?? double.maxFinite.toInt())
: LogEntry_.mmolPerL
.between(minMmolPerL ?? 0, maxMmolPerL ?? double.maxFinite)))
..order(LogEntry_.time, flags: Order.descending);
return builder.build().find().where((entry) {
return (note == null || (entry.notes ?? '').contains(note)) &&
(mealId == null ||
LogMeal.getAllForEntry(entry.id)
.any((LogMeal logMeal) => logMeal.meal.targetId == mealId)) &&
(mealName == null ||
LogMeal.getAllForEntry(entry.id).any(
(LogMeal logMeal) => logMeal.value.contains(mealName))) &&
(entry.time.compareTo(start) >= 0 && entry.time.isBefore(end));
}).toList();
}
@override
String toString() {
return DateTimeUtils.displayDateTime(time);
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['id'] = id;
data['deleted'] = deleted;
data['time'] = time.toIso8601String();
data['mgPerDl'] = mgPerDl;
data['mmolPerL'] = mmolPerL;
data['glucoseTrend'] = glucoseTrend;
data['notes'] = notes;
return data;
}
static String? putFromJson(
Map<String, dynamic> json, bool overrideExisting, String? source) {
DateTime? time = DateTime.tryParse(json['time']);
if (time == null) {
return 'time is missing';
}
final logEntry = LogEntry(
id: overrideExisting ? json['id'] : 0,
deleted: json['deleted'],
time: time,
mgPerDl: json['mgPerDl'],
mmolPerL: json['mmolPerL'],
glucoseTrend: json['glucoseTrend'],
notes: json['notes'],
source: source,
);
LogEntry.put(logEntry);
return null;
}
}

View File

@ -20,6 +20,7 @@ class LogEvent {
bool hasEndTime;
int? reminderDuration;
String? notes;
String? source;
@Transient()
String? title;
@ -40,6 +41,7 @@ class LogEvent {
this.hasEndTime = false,
this.reminderDuration,
this.notes,
this.source,
});
// methods
@ -143,4 +145,46 @@ class LogEvent {
String toString() {
return eventType.target?.value ?? '';
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['id'] = id;
data['deleted'] = deleted;
data['time'] = time.toIso8601String();
data['endTime'] = endTime?.toIso8601String();
data['hasEndTime'] = hasEndTime;
data['reminderDuration'] = reminderDuration;
data['notes'] = notes;
data['eventType'] = eventType.targetId;
data['bolusProfile'] = bolusProfile.targetId;
data['basalProfile'] = basalProfile.targetId;
return data;
}
static String? putFromJson(Map<String, dynamic> json, bool overrideExisting, String? source) {
DateTime? time = DateTime.tryParse(json['time']);
if (time == null) {
return 'time is missing';
}
final logEvent = LogEvent(
id: overrideExisting ? json['id'] : 0,
deleted: json['deleted'],
time: time,
endTime: DateTime.tryParse(json['endTime']),
hasEndTime: json['hasEndTime'],
reminderDuration: json['reminderDuration'],
notes: json['notes'],
source: source,
);
logEvent.eventType.targetId = json['eventType'];
logEvent.bolusProfile.targetId = json['bolusProfile'];
logEvent.basalProfile.targetId = json['basalProfile'];
LogEvent.put(logEvent);
return null;
}
}

View File

@ -12,10 +12,12 @@ class LogEventType {
// properties
int id;
bool deleted;
@Unique()
String value;
bool hasEndTime;
int? defaultReminderDuration;
String? notes;
String? source;
// constructor
LogEventType({
@ -25,6 +27,7 @@ class LogEventType {
this.hasEndTime = false,
this.defaultReminderDuration,
this.notes,
this.source,
});
// relations
@ -52,4 +55,32 @@ class LogEventType {
String toString() {
return value;
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['id'] = id;
data['deleted'] = deleted;
data['value'] = value;
data['hasEndTime'] = hasEndTime;
data['defaultReminderDuration'] = defaultReminderDuration;
data['notes'] = notes;
return data;
}
static String? putFromJson(Map<String, dynamic> json, bool overrideExisting, String? source) {
final logEventType = LogEventType(
id: overrideExisting ? json['id'] : 0,
deleted: json['deleted'],
value: json['value'],
hasEndTime: json['hasEndTime'],
defaultReminderDuration: json['defaultReminderDuration'],
notes: json['notes'],
source: source,
);
LogEventType.put(logEventType);
return null;
}
}

View File

@ -22,8 +22,8 @@ class LogMeal {
double? portionSize;
double? totalCarbs;
String? notes;
double? bolus;
double amount;
String? source;
// relations
final logEntry = ToOne<LogEntry>();
@ -44,6 +44,7 @@ class LogMeal {
this.portionSize,
this.totalCarbs,
this.notes,
this.source,
});
// methods
@ -57,6 +58,13 @@ class LogMeal {
}
}
static void removeAllForEntry(int id) {
box.putMany(getAllForEntry(id).map((item) {
item.deleted = true;
return item;
}).toList());
}
static List<LogMeal> getAllForEntry(int id) {
QueryBuilder<LogMeal> builder = box.query(LogMeal_.deleted.equals(false));
builder.link(LogMeal_.logEntry, LogEntry_.id.equals(id));
@ -86,4 +94,50 @@ class LogMeal {
String toString() {
return value;
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['id'] = id;
data['deleted'] = deleted;
data['value'] = value;
data['carbsRatio'] = carbsRatio;
data['portionSize'] = portionSize;
data['totalCarbs'] = totalCarbs;
data['notes'] = notes;
data['amount'] = amount;
data['logEntry'] = logEntry.targetId;
data['meal'] = meal.targetId;
data['mealSource'] = mealSource.targetId;
data['mealCategory'] = mealCategory.targetId;
data['mealPortionType'] = mealPortionType.targetId;
data['portionSizeAccuracy'] = portionSizeAccuracy.targetId;
data['carbsRatioAccuracy'] = carbsRatioAccuracy.targetId;
return data;
}
static String? putFromJson(
Map<String, dynamic> json, bool overrideExisting, String? source) {
final logMeal = LogMeal(
id: overrideExisting ? json['id'] : 0,
deleted: json['deleted'] == 'true',
value: json['value'],
carbsRatio: json['carbsRatio'],
portionSize: json['portionSize'],
totalCarbs: json['totalCarbs'],
amount: json['amount'],
notes: json['notes'],
source: source,
);
logMeal.logEntry.targetId = json['logEntry'];
logMeal.meal.targetId = json['meal'];
logMeal.mealSource.targetId = json['mealSource'];
logMeal.mealCategory.targetId = json['mealCategory'];
logMeal.mealPortionType.targetId = json['mealPortionType'];
logMeal.portionSizeAccuracy.targetId = json['portionSizeAccuracy'];
logMeal.carbsRatioAccuracy.targetId = json['carbsRatioAccuracy'];
LogMeal.put(logMeal);
return null;
}
}

View File

@ -16,6 +16,7 @@ class Meal {
// properties
int id;
bool deleted;
@Unique()
String value;
double? carbsRatio;
double? portionSize;
@ -23,6 +24,7 @@ class Meal {
int? delayedBolusDuration;
double? delayedBolusPercentage;
String? notes;
String? source;
// relations
final mealSource = ToOne<MealSource>();
@ -42,6 +44,7 @@ class Meal {
this.delayedBolusDuration,
this.delayedBolusPercentage,
this.notes,
this.source,
});
// methods
@ -65,4 +68,48 @@ class Meal {
String toString() {
return value;
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['id'] = id;
data['deleted'] = deleted;
data['value'] = value;
data['carbsRatio'] = carbsRatio;
data['portionSize'] = portionSize;
data['carbsPerPortion'] = carbsPerPortion;
data['delayedBolusDuration'] = delayedBolusDuration;
data['delayedBolusPercentage'] = delayedBolusPercentage;
data['notes'] = notes;
data['mealSource'] = mealSource.targetId;
data['mealCategory'] = mealCategory.targetId;
data['mealPortionType'] = mealPortionType.targetId;
data['portionSizeAccuracy'] = portionSizeAccuracy.targetId;
data['carbsRatioAccuracy'] = carbsRatioAccuracy.targetId;
return data;
}
static String? putFromJson(
Map<String, dynamic> json, bool overrideExisting, String? source) {
final meal = Meal(
id: overrideExisting ? json['id'] : 0,
deleted: json['deleted'] == 'true',
value: json['value'],
carbsRatio: json['carbsRatio'],
portionSize: json['portionSize'],
carbsPerPortion: json['carbsPerPortion'],
delayedBolusDuration: json['delayedBolusDuration'],
delayedBolusPercentage: json['delayedBolusPercentage'],
notes: json['notes'],
source: source,
);
meal.mealSource.targetId = json['mealSource'];
meal.mealCategory.targetId = json['mealCategory'];
meal.mealPortionType.targetId = json['mealPortionType'];
meal.portionSizeAccuracy.targetId = json['portionSizeAccuracy'];
meal.carbsRatioAccuracy.targetId = json['carbsRatioAccuracy'];
Meal.put(meal);
return null;
}
}

View File

@ -10,8 +10,10 @@ class MealCategory {
// properties
int id;
bool deleted;
@Unique()
String value;
String? notes;
String? source;
// constructor
MealCategory({
@ -19,6 +21,7 @@ class MealCategory {
this.deleted = false,
this.value = '',
this.notes,
this.source,
});
// methods
@ -42,4 +45,27 @@ class MealCategory {
String toString() {
return value;
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['id'] = id;
data['deleted'] = deleted;
data['value'] = value;
data['notes'] = notes;
return data;
}
static String? putFromJson(
Map<String, dynamic> json, bool overrideExisting, String? source) {
final mealCategory = MealCategory(
id: overrideExisting ? json['id'] : 0,
deleted: json['deleted'] == 'true',
value: json['value'],
notes: json['notes'],
source: source,
);
MealCategory.put(mealCategory);
return null;
}
}

View File

@ -5,13 +5,16 @@ import 'package:diameter/objectbox.g.dart' show MealPortionType_;
@Entity(uid: 2111511899235985637)
@Sync()
class MealPortionType {
static final Box<MealPortionType> box = objectBox.store.box<MealPortionType>();
static final Box<MealPortionType> box =
objectBox.store.box<MealPortionType>();
// properties
int id;
bool deleted;
@Unique()
String value;
String? notes;
String? source;
// constructor
MealPortionType({
@ -19,6 +22,7 @@ class MealPortionType {
this.deleted = false,
this.value = '',
this.notes,
this.source,
});
// methods
@ -26,7 +30,9 @@ class MealPortionType {
static void put(MealPortionType mealPortionType) => box.put(mealPortionType);
static List<MealPortionType> getAll() {
QueryBuilder<MealPortionType> builder = box.query(MealPortionType_.deleted.equals(false))..order(MealPortionType_.value);
QueryBuilder<MealPortionType> builder = box
.query(MealPortionType_.deleted.equals(false))
..order(MealPortionType_.value);
return builder.build().find();
}
@ -42,4 +48,27 @@ class MealPortionType {
String toString() {
return value;
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['id'] = id;
data['deleted'] = deleted;
data['value'] = value;
data['notes'] = notes;
return data;
}
static String? putFromJson(
Map<String, dynamic> json, bool overrideExisting, String? source) {
final mealPortionType = MealPortionType(
id: overrideExisting ? json['id'] : 0,
deleted: json['deleted'] == 'true',
value: json['value'],
notes: json['notes'],
source: source,
);
MealPortionType.put(mealPortionType);
return null;
}
}

View File

@ -13,8 +13,10 @@ class MealSource {
// properties
int id;
bool deleted;
@Unique()
String value;
String? notes;
String? source;
// relations
final defaultMealCategory = ToOne<MealCategory>();
@ -28,6 +30,7 @@ class MealSource {
this.deleted = false,
this.value = '',
this.notes,
this.source,
});
// methods
@ -35,7 +38,8 @@ class MealSource {
static void put(MealSource mealSource) => box.put(mealSource);
static List<MealSource> getAll() {
QueryBuilder<MealSource> builder = box.query(MealSource_.deleted.equals(false))..order(MealSource_.value);
QueryBuilder<MealSource> builder =
box.query(MealSource_.deleted.equals(false))..order(MealSource_.value);
return builder.build().find();
}
@ -51,4 +55,36 @@ class MealSource {
String toString() {
return value;
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['id'] = id;
data['deleted'] = deleted;
data['value'] = value;
data['notes'] = notes;
data['defaultMealCategory'] = defaultMealCategory.targetId;
data['defaultMealPortionType'] = defaultMealPortionType.targetId;
data['defaultCarbsRatioAccuracy'] = defaultCarbsRatioAccuracy.targetId;
data['defaultPortionSizeAccuracy'] = defaultPortionSizeAccuracy.targetId;
return data;
}
static String? putFromJson(
Map<String, dynamic> json, bool overrideExisting, String? source) {
final mealSource = MealSource(
id: overrideExisting ? json['id'] : 0,
deleted: json['deleted'] == 'true',
value: json['value'],
notes: json['notes'],
source: source,
);
mealSource.defaultMealCategory.targetId = json['defaultMealCategory'];
mealSource.defaultMealPortionType.targetId = json['defaultMealPortionType'];
mealSource.defaultCarbsRatioAccuracy.targetId = json['defaultCarbsRatioAccuracy'];
mealSource.defaultPortionSizeAccuracy.targetId = json['defaultPortionSizeAccuracy'];
MealSource.put(mealSource);
return null;
}
}

View File

@ -69,6 +69,8 @@ class Settings {
bool useDarkTheme;
DateTime? lastExportTimestamp;
// constructor
Settings({
this.id = 0,
@ -89,6 +91,7 @@ class Settings {
this.targetGlucoseMgPerDl = 100,
this.targetGlucoseMmolPerL = 5.5,
this.useDarkTheme = false,
this.lastExportTimestamp,
});
// methods
@ -101,7 +104,8 @@ class Settings {
static NutritionMeasurement get nutritionMeasurement =>
NutritionMeasurement.values[get().nutritionMeasurementIndex];
static GlucoseMeasurement get glucoseMeasurement => GlucoseMeasurement.values[get().glucoseMeasurementIndex];
static GlucoseMeasurement get glucoseMeasurement =>
GlucoseMeasurement.values[get().glucoseMeasurementIndex];
static GlucoseDisplayMode get glucoseDisplayMode =>
GlucoseDisplayMode.values[get().glucoseDisplayModeIndex];
@ -120,10 +124,39 @@ class Settings {
static ThemeMode get themeMode =>
get().useDarkTheme ? ThemeMode.dark : ThemeMode.light;
static DateTime? get lastExportTimeStamp => get().lastExportTimestamp;
static void put(Settings settings) => box.put(settings);
static void reset() {
box.removeAll();
box.put(Settings(useDarkTheme: ThemeMode.system == ThemeMode.dark));
}
static Map<String, dynamic> toJson() {
Settings settings = get();
final Map<String, dynamic> data = <String, dynamic>{};
data['id'] = settings.id;
data['nutritionMeasurementIndex'] = settings.nutritionMeasurementIndex;
data['glucoseDisplayModeIndex'] = settings.glucoseDisplayModeIndex;
data['glucoseMeasurementIndex'] = settings.glucoseMeasurementIndex;
data['targetGlucoseMgPerDl'] = settings.targetGlucoseMgPerDl;
data['targetGlucoseMmolPerL'] = settings.targetGlucoseMmolPerL;
data['insulinIncrements'] = settings.insulinIncrements;
data['nutritionIncrements'] = settings.nutritionIncrements;
data['mmolPerLIncrements'] = settings.mmolPerLIncrements;
data['amountIncrements'] = settings.amountIncrements;
data['dateFormat'] = settings.dateFormat;
data['longDateFormat'] = settings.longDateFormat;
data['timeFormat'] = settings.timeFormat;
data['longTimeFormat'] = settings.longTimeFormat;
data['showConfirmationDialogOnCancel'] =
settings.showConfirmationDialogOnCancel;
data['showConfirmationDialogOnDelete'] =
settings.showConfirmationDialogOnDelete;
data['showConfirmationDialogOnStopEvent'] =
settings.showConfirmationDialogOnStopEvent;
data['useDarkTheme'] = settings.useDarkTheme;
return data;
}
}

View File

@ -1,12 +1,12 @@
import 'package:diameter/main.dart';
import 'package:diameter/models/meal.dart';
import 'package:diameter/models/recipe.dart';
import 'package:diameter/models/x_recipe.dart';
import 'package:diameter/utils/utils.dart';
import 'package:objectbox/objectbox.dart';
import 'package:diameter/objectbox.g.dart' show Ingredient_, Recipe_;
@Entity(uid: 6950311793136068892)
@Sync()
// @Entity(uid: 6950311793136068892)
// @Sync()
class Ingredient {
static final Box<Ingredient> box = objectBox.store.box<Ingredient>();

View File

@ -1,12 +1,12 @@
import 'package:diameter/main.dart';
import 'package:diameter/models/ingredient.dart';
import 'package:diameter/models/x_ingredient.dart';
import 'package:diameter/models/meal.dart';
import 'package:diameter/utils/utils.dart';
import 'package:objectbox/objectbox.dart';
import 'package:diameter/objectbox.g.dart' show Recipe_;
@Entity(uid: 6497942314956341514)
@Sync()
// @Entity(uid: 6497942314956341514)
// @Sync()
class Recipe {
static final Box<Recipe> box = objectBox.store.box<Recipe>();

View File

@ -1,8 +1,8 @@
import 'package:diameter/main.dart';
import 'package:objectbox/objectbox.dart';
@Entity()
@Sync()
// @Entity()
// @Sync()
class User {
static final Box<User> box = objectBox.store.box<User>();
// properties

View File

@ -1,3 +1,4 @@
import 'package:diameter/localization_keys.dart';
import 'package:diameter/screens/category/categories.dart';
import 'package:diameter/screens/category/accuracy_detail.dart';
import 'package:diameter/screens/category/accuracy_list.dart';
@ -28,6 +29,7 @@ import 'package:diameter/screens/reports/export.dart';
import 'package:diameter/screens/reports/reports.dart';
import 'package:diameter/settings.dart';
import 'package:flutter/material.dart';
import 'package:flutter_translate/flutter_translate.dart';
class Routes {
static const String basal = BasalDetailScreen.routeName;
@ -119,7 +121,7 @@ class _NavigationState extends State<Navigation> {
),
),
ListTile(
title: const Text('Log'),
title: Text(translate(LocalizationKeys.navigation_log)),
leading: const Icon(Icons.dashboard),
onTap: () {
selectDestination(Routes.log);
@ -127,7 +129,7 @@ class _NavigationState extends State<Navigation> {
selected: widget.currentLocation == Routes.log,
),
ListTile(
title: const Text('Log Events'),
title: Text(translate(LocalizationKeys.navigation_logEvents)),
leading: const Icon(Icons.event),
onTap: () {
selectDestination(Routes.logEvents);
@ -135,7 +137,7 @@ class _NavigationState extends State<Navigation> {
selected: widget.currentLocation == Routes.logEvents,
),
ListTile(
title: const Text('Reports'),
title: Text(translate(LocalizationKeys.navigation_reports)),
leading: const Icon(Icons.show_chart),
onTap: () {
selectDestination(Routes.reports);
@ -143,7 +145,7 @@ class _NavigationState extends State<Navigation> {
selected: Routes.reportRoutes.contains(widget.currentLocation),
),
ListTile(
title: const Text('Meals'),
title: Text(translate(LocalizationKeys.navigation_meals)),
leading: const Icon(Icons.dinner_dining),
onTap: () {
selectDestination(Routes.meals);
@ -151,7 +153,7 @@ class _NavigationState extends State<Navigation> {
selected: Routes.mealRoutes.contains(widget.currentLocation),
),
ListTile(
title: const Text('Basal Profiles'),
title: Text(translate(LocalizationKeys.navigation_basalProfiles)),
leading: const Icon(Icons.access_time),
onTap: () {
selectDestination(Routes.basalProfiles);
@ -159,7 +161,7 @@ class _NavigationState extends State<Navigation> {
selected: Routes.basalRoutes.contains(widget.currentLocation),
),
ListTile(
title: const Text('Bolus Profiles'),
title: Text(translate(LocalizationKeys.navigation_bolusProfiles)),
leading: const Icon(Icons.medication),
onTap: () {
selectDestination(Routes.bolusProfiles);
@ -167,7 +169,7 @@ class _NavigationState extends State<Navigation> {
selected: Routes.bolusRoutes.contains(widget.currentLocation),
),
ListTile(
title: const Text('Categorization'),
title: Text(translate(LocalizationKeys.navigation_categorization)),
leading: const Icon(Icons.category),
onTap: () {
selectDestination(Routes.category);
@ -175,7 +177,7 @@ class _NavigationState extends State<Navigation> {
selected: Routes.categoryRoutes.contains(widget.currentLocation),
),
ListTile(
title: const Text('Settings'),
title: Text(translate(LocalizationKeys.navigation_settings)),
leading: const Icon(Icons.settings),
onTap: () {
selectDestination(Routes.settings);

View File

@ -5,7 +5,7 @@
"entities": [
{
"id": "2:1467758525778521891",
"lastPropertyId": "6:3409466778841164684",
"lastPropertyId": "7:3194552447464951410",
"name": "Basal",
"flags": 2,
"properties": [
@ -42,13 +42,18 @@
"id": "6:3409466778841164684",
"name": "deleted",
"type": 1
},
{
"id": "7:3194552447464951410",
"name": "source",
"type": 9
}
],
"relations": []
},
{
"id": "3:3613736032926903785",
"lastPropertyId": "5:8140071977687660397",
"lastPropertyId": "6:3153627070507809184",
"name": "BasalProfile",
"flags": 2,
"properties": [
@ -77,13 +82,18 @@
"id": "5:8140071977687660397",
"name": "deleted",
"type": 1
},
{
"id": "6:3153627070507809184",
"name": "source",
"type": 9
}
],
"relations": []
},
{
"id": "4:3417770529060202389",
"lastPropertyId": "9:7440090146687096977",
"lastPropertyId": "10:6138236789530596038",
"name": "Bolus",
"flags": 2,
"properties": [
@ -135,13 +145,18 @@
"id": "9:7440090146687096977",
"name": "deleted",
"type": 1
},
{
"id": "10:6138236789530596038",
"name": "source",
"type": 9
}
],
"relations": []
},
{
"id": "5:8812452529027052317",
"lastPropertyId": "5:8082994824481464395",
"lastPropertyId": "6:8746199841403936632",
"name": "BolusProfile",
"flags": 2,
"properties": [
@ -170,13 +185,18 @@
"id": "5:8082994824481464395",
"name": "deleted",
"type": 1
},
{
"id": "6:8746199841403936632",
"name": "source",
"type": 9
}
],
"relations": []
},
{
"id": "6:752131069307970560",
"lastPropertyId": "10:2505303363495348118",
"lastPropertyId": "11:8454251731096762477",
"name": "LogEntry",
"flags": 2,
"properties": [
@ -215,13 +235,18 @@
"id": "10:2505303363495348118",
"name": "glucoseTrend",
"type": 8
},
{
"id": "11:8454251731096762477",
"name": "source",
"type": 9
}
],
"relations": []
},
{
"id": "7:4303325892753185970",
"lastPropertyId": "12:3041952167628926163",
"lastPropertyId": "13:9206766629540069341",
"name": "LogEvent",
"flags": 2,
"properties": [
@ -284,13 +309,18 @@
"id": "12:3041952167628926163",
"name": "reminderDuration",
"type": 6
},
{
"id": "13:9206766629540069341",
"name": "source",
"type": 9
}
],
"relations": []
},
{
"id": "8:8362795406595606110",
"lastPropertyId": "8:1869014400856897151",
"lastPropertyId": "9:7377683740652293217",
"name": "LogEventType",
"flags": 2,
"properties": [
@ -340,13 +370,18 @@
"flags": 520,
"indexId": "28:4563029809754152081",
"relationTarget": "BasalProfile"
},
{
"id": "9:7377683740652293217",
"name": "source",
"type": 9
}
],
"relations": []
},
{
"id": "9:411177866700467286",
"lastPropertyId": "19:8965198821438347033",
"lastPropertyId": "20:3296734778930341954",
"name": "LogMeal",
"flags": 2,
"properties": [
@ -371,11 +406,6 @@
"name": "portionSize",
"type": 8
},
{
"id": "6:8074052538574863399",
"name": "bolus",
"type": 8
},
{
"id": "9:1920579694098037947",
"name": "notes",
@ -451,13 +481,18 @@
"id": "19:8965198821438347033",
"name": "totalCarbs",
"type": 8
},
{
"id": "20:3296734778930341954",
"name": "source",
"type": 9
}
],
"relations": []
},
{
"id": "10:382130101578692012",
"lastPropertyId": "15:8283810711091063880",
"lastPropertyId": "16:8612850434699732822",
"name": "Meal",
"flags": 2,
"properties": [
@ -546,13 +581,18 @@
"id": "15:8283810711091063880",
"name": "delayedBolusPercentage",
"type": 8
},
{
"id": "16:8612850434699732822",
"name": "source",
"type": 9
}
],
"relations": []
},
{
"id": "11:3158200688796904913",
"lastPropertyId": "4:824435977543069541",
"lastPropertyId": "5:2221810999445049020",
"name": "MealCategory",
"flags": 2,
"properties": [
@ -576,13 +616,18 @@
"id": "4:824435977543069541",
"name": "deleted",
"type": 1
},
{
"id": "5:2221810999445049020",
"name": "source",
"type": 9
}
],
"relations": []
},
{
"id": "12:2111511899235985637",
"lastPropertyId": "4:5680236937391945907",
"lastPropertyId": "5:6807468448392267469",
"name": "MealPortionType",
"flags": 2,
"properties": [
@ -606,13 +651,18 @@
"id": "4:5680236937391945907",
"name": "deleted",
"type": 1
},
{
"id": "5:6807468448392267469",
"name": "source",
"type": 9
}
],
"relations": []
},
{
"id": "13:1283034494527412242",
"lastPropertyId": "8:4547899751779962180",
"lastPropertyId": "9:6873017370661053783",
"name": "MealSource",
"flags": 2,
"properties": [
@ -668,13 +718,18 @@
"id": "8:4547899751779962180",
"name": "deleted",
"type": 1
},
{
"id": "9:6873017370661053783",
"name": "source",
"type": 9
}
],
"relations": []
},
{
"id": "14:8033487006694871160",
"lastPropertyId": "18:7503231998671134983",
"lastPropertyId": "19:3683854670538916324",
"name": "LogBolus",
"flags": 2,
"properties": [
@ -767,13 +822,18 @@
"id": "18:7503231998671134983",
"name": "mmolPerLCorrection",
"type": 8
},
{
"id": "19:3683854670538916324",
"name": "source",
"type": 9
}
],
"relations": []
},
{
"id": "15:291512798403320400",
"lastPropertyId": "7:6675647182186603076",
"lastPropertyId": "8:1800866627092978542",
"name": "Accuracy",
"flags": 2,
"properties": [
@ -812,13 +872,18 @@
"id": "7:6675647182186603076",
"name": "deleted",
"type": 1
},
{
"id": "8:1800866627092978542",
"name": "source",
"type": 9
}
],
"relations": []
},
{
"id": "16:3989341091218179227",
"lastPropertyId": "27:3553639710779248831",
"lastPropertyId": "28:7196743106880728684",
"name": "Settings",
"flags": 2,
"properties": [
@ -912,13 +977,18 @@
"id": "27:3553639710779248831",
"name": "amountIncrements",
"type": 8
},
{
"id": "28:7196743106880728684",
"name": "lastExportTimestamp",
"type": 10
}
],
"relations": []
},
{
"id": "17:5041265995704044399",
"lastPropertyId": "7:1333487551279074696",
"lastPropertyId": "8:1091046738846336945",
"name": "GlucoseTarget",
"flags": 2,
"properties": [
@ -957,6 +1027,11 @@
"id": "7:1333487551279074696",
"name": "color",
"type": 6
},
{
"id": "8:1091046738846336945",
"name": "source",
"type": 9
}
],
"relations": []
@ -1125,7 +1200,8 @@
4678123663117222609,
780211923138281722,
763575433624979013,
1225271130099322691
1225271130099322691,
8074052538574863399
],
"retiredRelationUids": [],
"version": 1

View File

@ -19,7 +19,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/x_ingredient.dart';
import 'models/log_bolus.dart';
import 'models/log_entry.dart';
import 'models/log_event.dart';
@ -29,9 +29,9 @@ 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/x_recipe.dart';
import 'models/settings.dart';
import 'models/user.dart';
import 'models/x_user.dart';
export 'package:objectbox/objectbox.dart'; // so that callers only have to import this file
@ -39,7 +39,7 @@ final _entities = <ModelEntity>[
ModelEntity(
id: const IdUid(2, 1467758525778521891),
name: 'Basal',
lastPropertyId: const IdUid(6, 3409466778841164684),
lastPropertyId: const IdUid(7, 3194552447464951410),
flags: 2,
properties: <ModelProperty>[
ModelProperty(
@ -73,6 +73,11 @@ final _entities = <ModelEntity>[
id: const IdUid(6, 3409466778841164684),
name: 'deleted',
type: 1,
flags: 0),
ModelProperty(
id: const IdUid(7, 3194552447464951410),
name: 'source',
type: 9,
flags: 0)
],
relations: <ModelRelation>[],
@ -80,7 +85,7 @@ final _entities = <ModelEntity>[
ModelEntity(
id: const IdUid(3, 3613736032926903785),
name: 'BasalProfile',
lastPropertyId: const IdUid(5, 8140071977687660397),
lastPropertyId: const IdUid(6, 3153627070507809184),
flags: 2,
properties: <ModelProperty>[
ModelProperty(
@ -107,6 +112,11 @@ final _entities = <ModelEntity>[
id: const IdUid(5, 8140071977687660397),
name: 'deleted',
type: 1,
flags: 0),
ModelProperty(
id: const IdUid(6, 3153627070507809184),
name: 'source',
type: 9,
flags: 0)
],
relations: <ModelRelation>[],
@ -114,7 +124,7 @@ final _entities = <ModelEntity>[
ModelEntity(
id: const IdUid(4, 3417770529060202389),
name: 'Bolus',
lastPropertyId: const IdUid(9, 7440090146687096977),
lastPropertyId: const IdUid(10, 6138236789530596038),
flags: 2,
properties: <ModelProperty>[
ModelProperty(
@ -163,6 +173,11 @@ final _entities = <ModelEntity>[
id: const IdUid(9, 7440090146687096977),
name: 'deleted',
type: 1,
flags: 0),
ModelProperty(
id: const IdUid(10, 6138236789530596038),
name: 'source',
type: 9,
flags: 0)
],
relations: <ModelRelation>[],
@ -170,7 +185,7 @@ final _entities = <ModelEntity>[
ModelEntity(
id: const IdUid(5, 8812452529027052317),
name: 'BolusProfile',
lastPropertyId: const IdUid(5, 8082994824481464395),
lastPropertyId: const IdUid(6, 8746199841403936632),
flags: 2,
properties: <ModelProperty>[
ModelProperty(
@ -197,6 +212,11 @@ final _entities = <ModelEntity>[
id: const IdUid(5, 8082994824481464395),
name: 'deleted',
type: 1,
flags: 0),
ModelProperty(
id: const IdUid(6, 8746199841403936632),
name: 'source',
type: 9,
flags: 0)
],
relations: <ModelRelation>[],
@ -204,7 +224,7 @@ final _entities = <ModelEntity>[
ModelEntity(
id: const IdUid(6, 752131069307970560),
name: 'LogEntry',
lastPropertyId: const IdUid(10, 2505303363495348118),
lastPropertyId: const IdUid(11, 8454251731096762477),
flags: 2,
properties: <ModelProperty>[
ModelProperty(
@ -241,6 +261,11 @@ final _entities = <ModelEntity>[
id: const IdUid(10, 2505303363495348118),
name: 'glucoseTrend',
type: 8,
flags: 0),
ModelProperty(
id: const IdUid(11, 8454251731096762477),
name: 'source',
type: 9,
flags: 0)
],
relations: <ModelRelation>[],
@ -248,7 +273,7 @@ final _entities = <ModelEntity>[
ModelEntity(
id: const IdUid(7, 4303325892753185970),
name: 'LogEvent',
lastPropertyId: const IdUid(12, 3041952167628926163),
lastPropertyId: const IdUid(13, 9206766629540069341),
flags: 2,
properties: <ModelProperty>[
ModelProperty(
@ -306,6 +331,11 @@ final _entities = <ModelEntity>[
id: const IdUid(12, 3041952167628926163),
name: 'reminderDuration',
type: 6,
flags: 0),
ModelProperty(
id: const IdUid(13, 9206766629540069341),
name: 'source',
type: 9,
flags: 0)
],
relations: <ModelRelation>[],
@ -313,7 +343,7 @@ final _entities = <ModelEntity>[
ModelEntity(
id: const IdUid(8, 8362795406595606110),
name: 'LogEventType',
lastPropertyId: const IdUid(8, 1869014400856897151),
lastPropertyId: const IdUid(9, 7377683740652293217),
flags: 2,
properties: <ModelProperty>[
ModelProperty(
@ -359,14 +389,19 @@ final _entities = <ModelEntity>[
type: 11,
flags: 520,
indexId: const IdUid(28, 4563029809754152081),
relationTarget: 'BasalProfile')
relationTarget: 'BasalProfile'),
ModelProperty(
id: const IdUid(9, 7377683740652293217),
name: 'source',
type: 9,
flags: 0)
],
relations: <ModelRelation>[],
backlinks: <ModelBacklink>[]),
ModelEntity(
id: const IdUid(9, 411177866700467286),
name: 'LogMeal',
lastPropertyId: const IdUid(19, 8965198821438347033),
lastPropertyId: const IdUid(20, 3296734778930341954),
flags: 2,
properties: <ModelProperty>[
ModelProperty(
@ -389,11 +424,6 @@ final _entities = <ModelEntity>[
name: 'portionSize',
type: 8,
flags: 0),
ModelProperty(
id: const IdUid(6, 8074052538574863399),
name: 'bolus',
type: 8,
flags: 0),
ModelProperty(
id: const IdUid(9, 1920579694098037947),
name: 'notes',
@ -462,6 +492,11 @@ final _entities = <ModelEntity>[
id: const IdUid(19, 8965198821438347033),
name: 'totalCarbs',
type: 8,
flags: 0),
ModelProperty(
id: const IdUid(20, 3296734778930341954),
name: 'source',
type: 9,
flags: 0)
],
relations: <ModelRelation>[],
@ -469,7 +504,7 @@ final _entities = <ModelEntity>[
ModelEntity(
id: const IdUid(10, 382130101578692012),
name: 'Meal',
lastPropertyId: const IdUid(15, 8283810711091063880),
lastPropertyId: const IdUid(16, 8612850434699732822),
flags: 2,
properties: <ModelProperty>[
ModelProperty(
@ -551,6 +586,11 @@ final _entities = <ModelEntity>[
id: const IdUid(15, 8283810711091063880),
name: 'delayedBolusPercentage',
type: 8,
flags: 0),
ModelProperty(
id: const IdUid(16, 8612850434699732822),
name: 'source',
type: 9,
flags: 0)
],
relations: <ModelRelation>[],
@ -558,7 +598,7 @@ final _entities = <ModelEntity>[
ModelEntity(
id: const IdUid(11, 3158200688796904913),
name: 'MealCategory',
lastPropertyId: const IdUid(4, 824435977543069541),
lastPropertyId: const IdUid(5, 2221810999445049020),
flags: 2,
properties: <ModelProperty>[
ModelProperty(
@ -580,6 +620,11 @@ final _entities = <ModelEntity>[
id: const IdUid(4, 824435977543069541),
name: 'deleted',
type: 1,
flags: 0),
ModelProperty(
id: const IdUid(5, 2221810999445049020),
name: 'source',
type: 9,
flags: 0)
],
relations: <ModelRelation>[],
@ -587,7 +632,7 @@ final _entities = <ModelEntity>[
ModelEntity(
id: const IdUid(12, 2111511899235985637),
name: 'MealPortionType',
lastPropertyId: const IdUid(4, 5680236937391945907),
lastPropertyId: const IdUid(5, 6807468448392267469),
flags: 2,
properties: <ModelProperty>[
ModelProperty(
@ -609,6 +654,11 @@ final _entities = <ModelEntity>[
id: const IdUid(4, 5680236937391945907),
name: 'deleted',
type: 1,
flags: 0),
ModelProperty(
id: const IdUid(5, 6807468448392267469),
name: 'source',
type: 9,
flags: 0)
],
relations: <ModelRelation>[],
@ -616,7 +666,7 @@ final _entities = <ModelEntity>[
ModelEntity(
id: const IdUid(13, 1283034494527412242),
name: 'MealSource',
lastPropertyId: const IdUid(8, 4547899751779962180),
lastPropertyId: const IdUid(9, 6873017370661053783),
flags: 2,
properties: <ModelProperty>[
ModelProperty(
@ -666,6 +716,11 @@ final _entities = <ModelEntity>[
id: const IdUid(8, 4547899751779962180),
name: 'deleted',
type: 1,
flags: 0),
ModelProperty(
id: const IdUid(9, 6873017370661053783),
name: 'source',
type: 9,
flags: 0)
],
relations: <ModelRelation>[],
@ -673,7 +728,7 @@ final _entities = <ModelEntity>[
ModelEntity(
id: const IdUid(14, 8033487006694871160),
name: 'LogBolus',
lastPropertyId: const IdUid(18, 7503231998671134983),
lastPropertyId: const IdUid(19, 3683854670538916324),
flags: 2,
properties: <ModelProperty>[
ModelProperty(
@ -761,6 +816,11 @@ final _entities = <ModelEntity>[
id: const IdUid(18, 7503231998671134983),
name: 'mmolPerLCorrection',
type: 8,
flags: 0),
ModelProperty(
id: const IdUid(19, 3683854670538916324),
name: 'source',
type: 9,
flags: 0)
],
relations: <ModelRelation>[],
@ -768,7 +828,7 @@ final _entities = <ModelEntity>[
ModelEntity(
id: const IdUid(15, 291512798403320400),
name: 'Accuracy',
lastPropertyId: const IdUid(7, 6675647182186603076),
lastPropertyId: const IdUid(8, 1800866627092978542),
flags: 2,
properties: <ModelProperty>[
ModelProperty(
@ -805,6 +865,11 @@ final _entities = <ModelEntity>[
id: const IdUid(7, 6675647182186603076),
name: 'deleted',
type: 1,
flags: 0),
ModelProperty(
id: const IdUid(8, 1800866627092978542),
name: 'source',
type: 9,
flags: 0)
],
relations: <ModelRelation>[],
@ -812,7 +877,7 @@ final _entities = <ModelEntity>[
ModelEntity(
id: const IdUid(16, 3989341091218179227),
name: 'Settings',
lastPropertyId: const IdUid(27, 3553639710779248831),
lastPropertyId: const IdUid(28, 7196743106880728684),
flags: 2,
properties: <ModelProperty>[
ModelProperty(
@ -904,6 +969,11 @@ final _entities = <ModelEntity>[
id: const IdUid(27, 3553639710779248831),
name: 'amountIncrements',
type: 8,
flags: 0),
ModelProperty(
id: const IdUid(28, 7196743106880728684),
name: 'lastExportTimestamp',
type: 10,
flags: 0)
],
relations: <ModelRelation>[],
@ -911,7 +981,7 @@ final _entities = <ModelEntity>[
ModelEntity(
id: const IdUid(17, 5041265995704044399),
name: 'GlucoseTarget',
lastPropertyId: const IdUid(7, 1333487551279074696),
lastPropertyId: const IdUid(8, 1091046738846336945),
flags: 2,
properties: <ModelProperty>[
ModelProperty(
@ -948,6 +1018,11 @@ final _entities = <ModelEntity>[
id: const IdUid(7, 1333487551279074696),
name: 'color',
type: 6,
flags: 0),
ModelProperty(
id: const IdUid(8, 1091046738846336945),
name: 'source',
type: 9,
flags: 0)
],
relations: <ModelRelation>[],
@ -1124,7 +1199,8 @@ ModelDefinition getObjectBoxModel() {
4678123663117222609,
780211923138281722,
763575433624979013,
1225271130099322691
1225271130099322691,
8074052538574863399
],
retiredRelationUids: const [],
modelVersion: 5,
@ -1141,13 +1217,16 @@ ModelDefinition getObjectBoxModel() {
object.id = id;
},
objectToFB: (Basal object, fb.Builder fbb) {
fbb.startTable(7);
final sourceOffset =
object.source == null ? null : fbb.writeString(object.source!);
fbb.startTable(8);
fbb.addInt64(0, object.id);
fbb.addInt64(1, object.startTime.millisecondsSinceEpoch);
fbb.addInt64(2, object.endTime.millisecondsSinceEpoch);
fbb.addFloat64(3, object.units);
fbb.addInt64(4, object.basalProfile.targetId);
fbb.addBool(5, object.deleted);
fbb.addOffset(6, sourceOffset);
fbb.finish(fbb.endTable());
return object.id;
},
@ -1163,8 +1242,10 @@ ModelDefinition getObjectBoxModel() {
const fb.Int64Reader().vTableGet(buffer, rootOffset, 6, 0)),
endTime: DateTime.fromMillisecondsSinceEpoch(
const fb.Int64Reader().vTableGet(buffer, rootOffset, 8, 0)),
units: const fb.Float64Reader()
.vTableGet(buffer, rootOffset, 10, 0));
units:
const fb.Float64Reader().vTableGet(buffer, rootOffset, 10, 0),
source: const fb.StringReader(asciiOptimization: true)
.vTableGetNullable(buffer, rootOffset, 16));
object.basalProfile.targetId =
const fb.Int64Reader().vTableGet(buffer, rootOffset, 12, 0);
object.basalProfile.attach(store);
@ -1182,12 +1263,15 @@ ModelDefinition getObjectBoxModel() {
final nameOffset = fbb.writeString(object.name);
final notesOffset =
object.notes == null ? null : fbb.writeString(object.notes!);
fbb.startTable(6);
final sourceOffset =
object.source == null ? null : fbb.writeString(object.source!);
fbb.startTable(7);
fbb.addInt64(0, object.id);
fbb.addOffset(1, nameOffset);
fbb.addBool(2, object.active);
fbb.addOffset(3, notesOffset);
fbb.addBool(4, object.deleted);
fbb.addOffset(5, sourceOffset);
fbb.finish(fbb.endTable());
return object.id;
},
@ -1204,7 +1288,9 @@ ModelDefinition getObjectBoxModel() {
active:
const fb.BoolReader().vTableGet(buffer, rootOffset, 8, false),
notes: const fb.StringReader(asciiOptimization: true)
.vTableGetNullable(buffer, rootOffset, 10));
.vTableGetNullable(buffer, rootOffset, 10),
source: const fb.StringReader(asciiOptimization: true)
.vTableGetNullable(buffer, rootOffset, 14));
return object;
}),
@ -1217,7 +1303,9 @@ ModelDefinition getObjectBoxModel() {
object.id = id;
},
objectToFB: (Bolus object, fb.Builder fbb) {
fbb.startTable(10);
final sourceOffset =
object.source == null ? null : fbb.writeString(object.source!);
fbb.startTable(11);
fbb.addInt64(0, object.id);
fbb.addInt64(1, object.startTime.millisecondsSinceEpoch);
fbb.addInt64(2, object.endTime.millisecondsSinceEpoch);
@ -1227,6 +1315,7 @@ ModelDefinition getObjectBoxModel() {
fbb.addFloat64(6, object.mmolPerL);
fbb.addInt64(7, object.bolusProfile.targetId);
fbb.addBool(8, object.deleted);
fbb.addOffset(9, sourceOffset);
fbb.finish(fbb.endTable());
return object.id;
},
@ -1249,7 +1338,9 @@ ModelDefinition getObjectBoxModel() {
mgPerDl: const fb.Int64Reader()
.vTableGetNullable(buffer, rootOffset, 14),
mmolPerL: const fb.Float64Reader()
.vTableGetNullable(buffer, rootOffset, 16));
.vTableGetNullable(buffer, rootOffset, 16),
source: const fb.StringReader(asciiOptimization: true)
.vTableGetNullable(buffer, rootOffset, 22));
object.bolusProfile.targetId =
const fb.Int64Reader().vTableGet(buffer, rootOffset, 18, 0);
object.bolusProfile.attach(store);
@ -1267,12 +1358,15 @@ ModelDefinition getObjectBoxModel() {
final nameOffset = fbb.writeString(object.name);
final notesOffset =
object.notes == null ? null : fbb.writeString(object.notes!);
fbb.startTable(6);
final sourceOffset =
object.source == null ? null : fbb.writeString(object.source!);
fbb.startTable(7);
fbb.addInt64(0, object.id);
fbb.addOffset(1, nameOffset);
fbb.addBool(2, object.active);
fbb.addOffset(3, notesOffset);
fbb.addBool(4, object.deleted);
fbb.addOffset(5, sourceOffset);
fbb.finish(fbb.endTable());
return object.id;
},
@ -1289,7 +1383,9 @@ ModelDefinition getObjectBoxModel() {
active:
const fb.BoolReader().vTableGet(buffer, rootOffset, 8, false),
notes: const fb.StringReader(asciiOptimization: true)
.vTableGetNullable(buffer, rootOffset, 10));
.vTableGetNullable(buffer, rootOffset, 10),
source: const fb.StringReader(asciiOptimization: true)
.vTableGetNullable(buffer, rootOffset, 14));
return object;
}),
@ -1304,7 +1400,9 @@ ModelDefinition getObjectBoxModel() {
objectToFB: (LogEntry object, fb.Builder fbb) {
final notesOffset =
object.notes == null ? null : fbb.writeString(object.notes!);
fbb.startTable(11);
final sourceOffset =
object.source == null ? null : fbb.writeString(object.source!);
fbb.startTable(12);
fbb.addInt64(0, object.id);
fbb.addInt64(1, object.time.millisecondsSinceEpoch);
fbb.addInt64(2, object.mgPerDl);
@ -1312,6 +1410,7 @@ ModelDefinition getObjectBoxModel() {
fbb.addOffset(7, notesOffset);
fbb.addBool(8, object.deleted);
fbb.addFloat64(9, object.glucoseTrend);
fbb.addOffset(10, sourceOffset);
fbb.finish(fbb.endTable());
return object.id;
},
@ -1332,7 +1431,9 @@ ModelDefinition getObjectBoxModel() {
glucoseTrend: const fb.Float64Reader()
.vTableGetNullable(buffer, rootOffset, 22),
notes: const fb.StringReader(asciiOptimization: true)
.vTableGetNullable(buffer, rootOffset, 18));
.vTableGetNullable(buffer, rootOffset, 18),
source: const fb.StringReader(asciiOptimization: true)
.vTableGetNullable(buffer, rootOffset, 24));
return object;
}),
@ -1348,7 +1449,9 @@ ModelDefinition getObjectBoxModel() {
objectToFB: (LogEvent object, fb.Builder fbb) {
final notesOffset =
object.notes == null ? null : fbb.writeString(object.notes!);
fbb.startTable(13);
final sourceOffset =
object.source == null ? null : fbb.writeString(object.source!);
fbb.startTable(14);
fbb.addInt64(0, object.id);
fbb.addInt64(1, object.time.millisecondsSinceEpoch);
fbb.addInt64(2, object.endTime?.millisecondsSinceEpoch);
@ -1359,6 +1462,7 @@ ModelDefinition getObjectBoxModel() {
fbb.addInt64(9, object.bolusProfile.targetId);
fbb.addInt64(10, object.basalProfile.targetId);
fbb.addInt64(11, object.reminderDuration);
fbb.addOffset(12, sourceOffset);
fbb.finish(fbb.endTable());
return object.id;
},
@ -1381,7 +1485,9 @@ ModelDefinition getObjectBoxModel() {
reminderDuration: const fb.Int64Reader()
.vTableGetNullable(buffer, rootOffset, 26),
notes: const fb.StringReader(asciiOptimization: true)
.vTableGetNullable(buffer, rootOffset, 12));
.vTableGetNullable(buffer, rootOffset, 12),
source: const fb.StringReader(asciiOptimization: true)
.vTableGetNullable(buffer, rootOffset, 28));
object.eventType.targetId =
const fb.Int64Reader().vTableGet(buffer, rootOffset, 18, 0);
object.eventType.attach(store);
@ -1406,7 +1512,9 @@ ModelDefinition getObjectBoxModel() {
final valueOffset = fbb.writeString(object.value);
final notesOffset =
object.notes == null ? null : fbb.writeString(object.notes!);
fbb.startTable(9);
final sourceOffset =
object.source == null ? null : fbb.writeString(object.source!);
fbb.startTable(10);
fbb.addInt64(0, object.id);
fbb.addOffset(1, valueOffset);
fbb.addBool(2, object.hasEndTime);
@ -1415,6 +1523,7 @@ ModelDefinition getObjectBoxModel() {
fbb.addBool(5, object.deleted);
fbb.addInt64(6, object.bolusProfile.targetId);
fbb.addInt64(7, object.basalProfile.targetId);
fbb.addOffset(8, sourceOffset);
fbb.finish(fbb.endTable());
return object.id;
},
@ -1433,7 +1542,9 @@ ModelDefinition getObjectBoxModel() {
defaultReminderDuration: const fb.Int64Reader()
.vTableGetNullable(buffer, rootOffset, 10),
notes: const fb.StringReader(asciiOptimization: true)
.vTableGetNullable(buffer, rootOffset, 12));
.vTableGetNullable(buffer, rootOffset, 12),
source: const fb.StringReader(asciiOptimization: true)
.vTableGetNullable(buffer, rootOffset, 20));
object.bolusProfile.targetId =
const fb.Int64Reader().vTableGet(buffer, rootOffset, 16, 0);
object.bolusProfile.attach(store);
@ -1462,12 +1573,13 @@ ModelDefinition getObjectBoxModel() {
final valueOffset = fbb.writeString(object.value);
final notesOffset =
object.notes == null ? null : fbb.writeString(object.notes!);
fbb.startTable(20);
final sourceOffset =
object.source == null ? null : fbb.writeString(object.source!);
fbb.startTable(21);
fbb.addInt64(0, object.id);
fbb.addOffset(1, valueOffset);
fbb.addFloat64(2, object.carbsRatio);
fbb.addFloat64(3, object.portionSize);
fbb.addFloat64(5, object.bolus);
fbb.addOffset(8, notesOffset);
fbb.addInt64(9, object.logEntry.targetId);
fbb.addInt64(10, object.meal.targetId);
@ -1479,6 +1591,7 @@ ModelDefinition getObjectBoxModel() {
fbb.addBool(16, object.deleted);
fbb.addFloat64(17, object.amount);
fbb.addFloat64(18, object.totalCarbs);
fbb.addOffset(19, sourceOffset);
fbb.finish(fbb.endTable());
return object.id;
},
@ -1501,9 +1614,9 @@ ModelDefinition getObjectBoxModel() {
totalCarbs: const fb.Float64Reader()
.vTableGetNullable(buffer, rootOffset, 40),
notes: const fb.StringReader(asciiOptimization: true)
.vTableGetNullable(buffer, rootOffset, 20))
..bolus = const fb.Float64Reader()
.vTableGetNullable(buffer, rootOffset, 14);
.vTableGetNullable(buffer, rootOffset, 20),
source: const fb.StringReader(asciiOptimization: true)
.vTableGetNullable(buffer, rootOffset, 42));
object.logEntry.targetId =
const fb.Int64Reader().vTableGet(buffer, rootOffset, 22, 0);
object.logEntry.attach(store);
@ -1545,7 +1658,9 @@ ModelDefinition getObjectBoxModel() {
final valueOffset = fbb.writeString(object.value);
final notesOffset =
object.notes == null ? null : fbb.writeString(object.notes!);
fbb.startTable(16);
final sourceOffset =
object.source == null ? null : fbb.writeString(object.source!);
fbb.startTable(17);
fbb.addInt64(0, object.id);
fbb.addOffset(1, valueOffset);
fbb.addFloat64(2, object.carbsRatio);
@ -1560,6 +1675,7 @@ ModelDefinition getObjectBoxModel() {
fbb.addInt64(12, object.carbsRatioAccuracy.targetId);
fbb.addBool(13, object.deleted);
fbb.addFloat64(14, object.delayedBolusPercentage);
fbb.addOffset(15, sourceOffset);
fbb.finish(fbb.endTable());
return object.id;
},
@ -1584,7 +1700,9 @@ ModelDefinition getObjectBoxModel() {
delayedBolusPercentage: const fb.Float64Reader()
.vTableGetNullable(buffer, rootOffset, 32),
notes: const fb.StringReader(asciiOptimization: true)
.vTableGetNullable(buffer, rootOffset, 18));
.vTableGetNullable(buffer, rootOffset, 18),
source: const fb.StringReader(asciiOptimization: true)
.vTableGetNullable(buffer, rootOffset, 34));
object.mealSource.targetId =
const fb.Int64Reader().vTableGet(buffer, rootOffset, 20, 0);
object.mealSource.attach(store);
@ -1614,11 +1732,14 @@ ModelDefinition getObjectBoxModel() {
final valueOffset = fbb.writeString(object.value);
final notesOffset =
object.notes == null ? null : fbb.writeString(object.notes!);
fbb.startTable(5);
final sourceOffset =
object.source == null ? null : fbb.writeString(object.source!);
fbb.startTable(6);
fbb.addInt64(0, object.id);
fbb.addOffset(1, valueOffset);
fbb.addOffset(2, notesOffset);
fbb.addBool(3, object.deleted);
fbb.addOffset(4, sourceOffset);
fbb.finish(fbb.endTable());
return object.id;
},
@ -1633,7 +1754,9 @@ ModelDefinition getObjectBoxModel() {
value: const fb.StringReader(asciiOptimization: true)
.vTableGet(buffer, rootOffset, 6, ''),
notes: const fb.StringReader(asciiOptimization: true)
.vTableGetNullable(buffer, rootOffset, 8));
.vTableGetNullable(buffer, rootOffset, 8),
source: const fb.StringReader(asciiOptimization: true)
.vTableGetNullable(buffer, rootOffset, 12));
return object;
}),
@ -1649,11 +1772,14 @@ ModelDefinition getObjectBoxModel() {
final valueOffset = fbb.writeString(object.value);
final notesOffset =
object.notes == null ? null : fbb.writeString(object.notes!);
fbb.startTable(5);
final sourceOffset =
object.source == null ? null : fbb.writeString(object.source!);
fbb.startTable(6);
fbb.addInt64(0, object.id);
fbb.addOffset(1, valueOffset);
fbb.addOffset(2, notesOffset);
fbb.addBool(3, object.deleted);
fbb.addOffset(4, sourceOffset);
fbb.finish(fbb.endTable());
return object.id;
},
@ -1668,7 +1794,9 @@ ModelDefinition getObjectBoxModel() {
value: const fb.StringReader(asciiOptimization: true)
.vTableGet(buffer, rootOffset, 6, ''),
notes: const fb.StringReader(asciiOptimization: true)
.vTableGetNullable(buffer, rootOffset, 8));
.vTableGetNullable(buffer, rootOffset, 8),
source: const fb.StringReader(asciiOptimization: true)
.vTableGetNullable(buffer, rootOffset, 12));
return object;
}),
@ -1689,7 +1817,9 @@ ModelDefinition getObjectBoxModel() {
final valueOffset = fbb.writeString(object.value);
final notesOffset =
object.notes == null ? null : fbb.writeString(object.notes!);
fbb.startTable(9);
final sourceOffset =
object.source == null ? null : fbb.writeString(object.source!);
fbb.startTable(10);
fbb.addInt64(0, object.id);
fbb.addOffset(1, valueOffset);
fbb.addOffset(2, notesOffset);
@ -1698,6 +1828,7 @@ ModelDefinition getObjectBoxModel() {
fbb.addInt64(5, object.defaultCarbsRatioAccuracy.targetId);
fbb.addInt64(6, object.defaultPortionSizeAccuracy.targetId);
fbb.addBool(7, object.deleted);
fbb.addOffset(8, sourceOffset);
fbb.finish(fbb.endTable());
return object.id;
},
@ -1712,7 +1843,9 @@ ModelDefinition getObjectBoxModel() {
value: const fb.StringReader(asciiOptimization: true)
.vTableGet(buffer, rootOffset, 6, ''),
notes: const fb.StringReader(asciiOptimization: true)
.vTableGetNullable(buffer, rootOffset, 8));
.vTableGetNullable(buffer, rootOffset, 8),
source: const fb.StringReader(asciiOptimization: true)
.vTableGetNullable(buffer, rootOffset, 20));
object.defaultMealCategory.targetId =
const fb.Int64Reader().vTableGet(buffer, rootOffset, 10, 0);
object.defaultMealCategory.attach(store);
@ -1739,7 +1872,9 @@ ModelDefinition getObjectBoxModel() {
objectToFB: (LogBolus object, fb.Builder fbb) {
final notesOffset =
object.notes == null ? null : fbb.writeString(object.notes!);
fbb.startTable(19);
final sourceOffset =
object.source == null ? null : fbb.writeString(object.source!);
fbb.startTable(20);
fbb.addInt64(0, object.id);
fbb.addFloat64(1, object.units);
fbb.addFloat64(2, object.carbs);
@ -1756,6 +1891,7 @@ ModelDefinition getObjectBoxModel() {
fbb.addFloat64(15, object.mmolPerLCurrent);
fbb.addFloat64(16, object.mmolPerLTarget);
fbb.addFloat64(17, object.mmolPerLCorrection);
fbb.addOffset(18, sourceOffset);
fbb.finish(fbb.endTable());
return object.id;
},
@ -1788,7 +1924,9 @@ ModelDefinition getObjectBoxModel() {
setManually: const fb.BoolReader()
.vTableGet(buffer, rootOffset, 16, false),
notes: const fb.StringReader(asciiOptimization: true)
.vTableGetNullable(buffer, rootOffset, 18));
.vTableGetNullable(buffer, rootOffset, 18),
source:
const fb.StringReader(asciiOptimization: true).vTableGetNullable(buffer, rootOffset, 40));
object.logEntry.targetId =
const fb.Int64Reader().vTableGet(buffer, rootOffset, 20, 0);
object.logEntry.attach(store);
@ -1812,7 +1950,9 @@ ModelDefinition getObjectBoxModel() {
final valueOffset = fbb.writeString(object.value);
final notesOffset =
object.notes == null ? null : fbb.writeString(object.notes!);
fbb.startTable(8);
final sourceOffset =
object.source == null ? null : fbb.writeString(object.source!);
fbb.startTable(9);
fbb.addInt64(0, object.id);
fbb.addOffset(1, valueOffset);
fbb.addBool(2, object.forCarbsRatio);
@ -1820,6 +1960,7 @@ ModelDefinition getObjectBoxModel() {
fbb.addInt64(4, object.confidenceRating);
fbb.addOffset(5, notesOffset);
fbb.addBool(6, object.deleted);
fbb.addOffset(7, sourceOffset);
fbb.finish(fbb.endTable());
return object.id;
},
@ -1840,7 +1981,9 @@ ModelDefinition getObjectBoxModel() {
confidenceRating: const fb.Int64Reader()
.vTableGetNullable(buffer, rootOffset, 12),
notes: const fb.StringReader(asciiOptimization: true)
.vTableGetNullable(buffer, rootOffset, 14));
.vTableGetNullable(buffer, rootOffset, 14),
source: const fb.StringReader(asciiOptimization: true)
.vTableGetNullable(buffer, rootOffset, 18));
return object;
}),
@ -1861,7 +2004,7 @@ ModelDefinition getObjectBoxModel() {
final longTimeFormatOffset = object.longTimeFormat == null
? null
: fbb.writeString(object.longTimeFormat!);
fbb.startTable(28);
fbb.startTable(29);
fbb.addInt64(0, object.id);
fbb.addOffset(1, dateFormatOffset);
fbb.addOffset(2, longDateFormatOffset);
@ -1880,13 +2023,15 @@ ModelDefinition getObjectBoxModel() {
fbb.addFloat64(24, object.nutritionIncrements);
fbb.addFloat64(25, object.mmolPerLIncrements);
fbb.addFloat64(26, object.amountIncrements);
fbb.addInt64(27, object.lastExportTimestamp?.millisecondsSinceEpoch);
fbb.finish(fbb.endTable());
return object.id;
},
objectFromFB: (Store store, ByteData fbData) {
final buffer = fb.BufferContext(fbData);
final rootOffset = buffer.derefObject(0);
final lastExportTimestampValue =
const fb.Int64Reader().vTableGetNullable(buffer, rootOffset, 58);
final object = Settings(
id: const fb.Int64Reader().vTableGet(buffer, rootOffset, 4, 0),
nutritionMeasurementIndex:
@ -1916,7 +2061,8 @@ ModelDefinition getObjectBoxModel() {
showConfirmationDialogOnStopEvent: const fb.BoolReader().vTableGet(buffer, rootOffset, 18, false),
targetGlucoseMgPerDl: const fb.Int64Reader().vTableGet(buffer, rootOffset, 44, 0),
targetGlucoseMmolPerL: const fb.Float64Reader().vTableGet(buffer, rootOffset, 46, 0),
useDarkTheme: const fb.BoolReader().vTableGet(buffer, rootOffset, 48, false));
useDarkTheme: const fb.BoolReader().vTableGet(buffer, rootOffset, 48, false),
lastExportTimestamp: lastExportTimestampValue == null ? null : DateTime.fromMillisecondsSinceEpoch(lastExportTimestampValue));
return object;
}),
@ -1929,7 +2075,9 @@ ModelDefinition getObjectBoxModel() {
object.id = id;
},
objectToFB: (GlucoseTarget object, fb.Builder fbb) {
fbb.startTable(8);
final sourceOffset =
object.source == null ? null : fbb.writeString(object.source!);
fbb.startTable(9);
fbb.addInt64(0, object.id);
fbb.addBool(1, object.deleted);
fbb.addInt64(2, object.fromMgPerDL);
@ -1937,6 +2085,7 @@ ModelDefinition getObjectBoxModel() {
fbb.addFloat64(4, object.fromMmolPerL);
fbb.addFloat64(5, object.toMmolPerL);
fbb.addInt64(6, object.color);
fbb.addOffset(7, sourceOffset);
fbb.finish(fbb.endTable());
return object.id;
},
@ -1957,7 +2106,9 @@ ModelDefinition getObjectBoxModel() {
toMmolPerL:
const fb.Float64Reader().vTableGet(buffer, rootOffset, 14, 0),
color:
const fb.Int64Reader().vTableGet(buffer, rootOffset, 16, 0));
const fb.Int64Reader().vTableGet(buffer, rootOffset, 16, 0),
source: const fb.StringReader(asciiOptimization: true)
.vTableGetNullable(buffer, rootOffset, 18));
return object;
}),
@ -2105,6 +2256,9 @@ class Basal_ {
/// see [Basal.deleted]
static final deleted =
QueryBooleanProperty<Basal>(_entities[0].properties[5]);
/// see [Basal.source]
static final source = QueryStringProperty<Basal>(_entities[0].properties[6]);
}
/// [BasalProfile] entity fields to define ObjectBox queries.
@ -2128,6 +2282,10 @@ class BasalProfile_ {
/// see [BasalProfile.deleted]
static final deleted =
QueryBooleanProperty<BasalProfile>(_entities[1].properties[4]);
/// see [BasalProfile.source]
static final source =
QueryStringProperty<BasalProfile>(_entities[1].properties[5]);
}
/// [Bolus] entity fields to define ObjectBox queries.
@ -2164,6 +2322,9 @@ class Bolus_ {
/// see [Bolus.deleted]
static final deleted =
QueryBooleanProperty<Bolus>(_entities[2].properties[8]);
/// see [Bolus.source]
static final source = QueryStringProperty<Bolus>(_entities[2].properties[9]);
}
/// [BolusProfile] entity fields to define ObjectBox queries.
@ -2187,6 +2348,10 @@ class BolusProfile_ {
/// see [BolusProfile.deleted]
static final deleted =
QueryBooleanProperty<BolusProfile>(_entities[3].properties[4]);
/// see [BolusProfile.source]
static final source =
QueryStringProperty<BolusProfile>(_entities[3].properties[5]);
}
/// [LogEntry] entity fields to define ObjectBox queries.
@ -2217,6 +2382,10 @@ class LogEntry_ {
/// see [LogEntry.glucoseTrend]
static final glucoseTrend =
QueryDoubleProperty<LogEntry>(_entities[4].properties[6]);
/// see [LogEntry.source]
static final source =
QueryStringProperty<LogEntry>(_entities[4].properties[7]);
}
/// [LogEvent] entity fields to define ObjectBox queries.
@ -2259,6 +2428,10 @@ class LogEvent_ {
/// see [LogEvent.reminderDuration]
static final reminderDuration =
QueryIntegerProperty<LogEvent>(_entities[5].properties[9]);
/// see [LogEvent.source]
static final source =
QueryStringProperty<LogEvent>(_entities[5].properties[10]);
}
/// [LogEventType] entity fields to define ObjectBox queries.
@ -2294,6 +2467,10 @@ class LogEventType_ {
/// see [LogEventType.basalProfile]
static final basalProfile = QueryRelationToOne<LogEventType, BasalProfile>(
_entities[6].properties[7]);
/// see [LogEventType.source]
static final source =
QueryStringProperty<LogEventType>(_entities[6].properties[8]);
}
/// [LogMeal] entity fields to define ObjectBox queries.
@ -2312,51 +2489,52 @@ class LogMeal_ {
static final portionSize =
QueryDoubleProperty<LogMeal>(_entities[7].properties[3]);
/// see [LogMeal.bolus]
static final bolus = QueryDoubleProperty<LogMeal>(_entities[7].properties[4]);
/// see [LogMeal.notes]
static final notes = QueryStringProperty<LogMeal>(_entities[7].properties[5]);
static final notes = QueryStringProperty<LogMeal>(_entities[7].properties[4]);
/// see [LogMeal.logEntry]
static final logEntry =
QueryRelationToOne<LogMeal, LogEntry>(_entities[7].properties[6]);
QueryRelationToOne<LogMeal, LogEntry>(_entities[7].properties[5]);
/// see [LogMeal.meal]
static final meal =
QueryRelationToOne<LogMeal, Meal>(_entities[7].properties[7]);
QueryRelationToOne<LogMeal, Meal>(_entities[7].properties[6]);
/// see [LogMeal.mealSource]
static final mealSource =
QueryRelationToOne<LogMeal, MealSource>(_entities[7].properties[8]);
QueryRelationToOne<LogMeal, MealSource>(_entities[7].properties[7]);
/// see [LogMeal.mealCategory]
static final mealCategory =
QueryRelationToOne<LogMeal, MealCategory>(_entities[7].properties[9]);
QueryRelationToOne<LogMeal, MealCategory>(_entities[7].properties[8]);
/// see [LogMeal.mealPortionType]
static final mealPortionType =
QueryRelationToOne<LogMeal, MealPortionType>(_entities[7].properties[10]);
QueryRelationToOne<LogMeal, MealPortionType>(_entities[7].properties[9]);
/// see [LogMeal.portionSizeAccuracy]
static final portionSizeAccuracy =
QueryRelationToOne<LogMeal, Accuracy>(_entities[7].properties[11]);
QueryRelationToOne<LogMeal, Accuracy>(_entities[7].properties[10]);
/// see [LogMeal.carbsRatioAccuracy]
static final carbsRatioAccuracy =
QueryRelationToOne<LogMeal, Accuracy>(_entities[7].properties[12]);
QueryRelationToOne<LogMeal, Accuracy>(_entities[7].properties[11]);
/// see [LogMeal.deleted]
static final deleted =
QueryBooleanProperty<LogMeal>(_entities[7].properties[13]);
QueryBooleanProperty<LogMeal>(_entities[7].properties[12]);
/// see [LogMeal.amount]
static final amount =
QueryDoubleProperty<LogMeal>(_entities[7].properties[14]);
QueryDoubleProperty<LogMeal>(_entities[7].properties[13]);
/// see [LogMeal.totalCarbs]
static final totalCarbs =
QueryDoubleProperty<LogMeal>(_entities[7].properties[15]);
QueryDoubleProperty<LogMeal>(_entities[7].properties[14]);
/// see [LogMeal.source]
static final source =
QueryStringProperty<LogMeal>(_entities[7].properties[15]);
}
/// [Meal] entity fields to define ObjectBox queries.
@ -2413,6 +2591,9 @@ class Meal_ {
/// see [Meal.delayedBolusPercentage]
static final delayedBolusPercentage =
QueryDoubleProperty<Meal>(_entities[8].properties[13]);
/// see [Meal.source]
static final source = QueryStringProperty<Meal>(_entities[8].properties[14]);
}
/// [MealCategory] entity fields to define ObjectBox queries.
@ -2432,6 +2613,10 @@ class MealCategory_ {
/// see [MealCategory.deleted]
static final deleted =
QueryBooleanProperty<MealCategory>(_entities[9].properties[3]);
/// see [MealCategory.source]
static final source =
QueryStringProperty<MealCategory>(_entities[9].properties[4]);
}
/// [MealPortionType] entity fields to define ObjectBox queries.
@ -2451,6 +2636,10 @@ class MealPortionType_ {
/// see [MealPortionType.deleted]
static final deleted =
QueryBooleanProperty<MealPortionType>(_entities[10].properties[3]);
/// see [MealPortionType.source]
static final source =
QueryStringProperty<MealPortionType>(_entities[10].properties[4]);
}
/// [MealSource] entity fields to define ObjectBox queries.
@ -2487,6 +2676,10 @@ class MealSource_ {
/// see [MealSource.deleted]
static final deleted =
QueryBooleanProperty<MealSource>(_entities[11].properties[7]);
/// see [MealSource.source]
static final source =
QueryStringProperty<MealSource>(_entities[11].properties[8]);
}
/// [LogBolus] entity fields to define ObjectBox queries.
@ -2553,6 +2746,10 @@ class LogBolus_ {
/// see [LogBolus.mmolPerLCorrection]
static final mmolPerLCorrection =
QueryDoubleProperty<LogBolus>(_entities[12].properties[15]);
/// see [LogBolus.source]
static final source =
QueryStringProperty<LogBolus>(_entities[12].properties[16]);
}
/// [Accuracy] entity fields to define ObjectBox queries.
@ -2583,6 +2780,10 @@ class Accuracy_ {
/// see [Accuracy.deleted]
static final deleted =
QueryBooleanProperty<Accuracy>(_entities[13].properties[6]);
/// see [Accuracy.source]
static final source =
QueryStringProperty<Accuracy>(_entities[13].properties[7]);
}
/// [Settings] entity fields to define ObjectBox queries.
@ -2657,6 +2858,10 @@ class Settings_ {
/// see [Settings.amountIncrements]
static final amountIncrements =
QueryDoubleProperty<Settings>(_entities[14].properties[17]);
/// see [Settings.lastExportTimestamp]
static final lastExportTimestamp =
QueryIntegerProperty<Settings>(_entities[14].properties[18]);
}
/// [GlucoseTarget] entity fields to define ObjectBox queries.
@ -2688,6 +2893,10 @@ class GlucoseTarget_ {
/// see [GlucoseTarget.color]
static final color =
QueryIntegerProperty<GlucoseTarget>(_entities[15].properties[6]);
/// see [GlucoseTarget.source]
static final source =
QueryStringProperty<GlucoseTarget>(_entities[15].properties[7]);
}
/// [Recipe] entity fields to define ObjectBox queries.

View File

@ -1,6 +1,7 @@
import 'package:diameter/components/detail.dart';
import 'package:diameter/components/forms/number_form_field.dart';
import 'package:diameter/components/forms/time_of_day_form_field.dart';
import 'package:diameter/localization_keys.dart';
import 'package:diameter/utils/dialog_utils.dart';
import 'package:diameter/models/settings.dart';
import 'package:diameter/navigation.dart';
@ -10,6 +11,7 @@ import 'package:flutter/material.dart';
import 'package:diameter/components/forms/form_wrapper.dart';
import 'package:diameter/models/basal.dart';
import 'package:diameter/models/basal_profile.dart';
import 'package:flutter_translate/flutter_translate.dart';
class BasalDetailScreen extends StatefulWidget {
static const String routeName = '/basal';
@ -130,7 +132,7 @@ class _BasalDetailScreenState extends State<BasalDetailScreen> {
_startTime.hour == other.startTime.hour &&
_startTime.minute == other.startTime.minute)
.isNotEmpty) {
error = 'There\'s already a rate with this start time.';
error = translate(LocalizationKeys.basal_warnings_duplicate);
}
if (basalRates
@ -141,7 +143,7 @@ class _BasalDetailScreenState extends State<BasalDetailScreen> {
DateTimeUtils.convertTimeOfDayToDateTime(_endTime)
.isAfter(other.startTime))
.isNotEmpty) {
error = 'This rate\'s time period overlaps with another one.';
error = translate(LocalizationKeys.basal_warnings_overlap);
}
return error == null
@ -154,11 +156,11 @@ class _BasalDetailScreenState extends State<BasalDetailScreen> {
actions: <Widget>[
TextButton(
onPressed: () => Navigator.pop(context, 'CANCEL'),
child: const Text('GO BACK TO EDITING'),
child: Text(translate(LocalizationKeys.general_keepEditing).toUpperCase()),
),
ElevatedButton(
onPressed: () => Navigator.pop(context, 'CONFIRM'),
child: const Text('SAVE AS IS'),
child: Text(translate(LocalizationKeys.general_saveAsIs).toUpperCase()),
),
],
);
@ -196,13 +198,26 @@ class _BasalDetailScreenState extends State<BasalDetailScreen> {
).then((result) {
Navigator.pop(
context,
['New Basal Rate${result[1] != null ? 's' : ''} saved', basal] +
[result[1]],
[
translatePlural(
LocalizationKeys.basal_saved,
result.length,
args: {
"status": _isNew ? '${LocalizationKeys.basal_new} ' : ''
},
),
basal] + [result[1]],
);
});
} else {
Navigator.pop(
context, ['${_isNew ? 'New' : ''} Basal Rate saved', basal]);
context, [translatePlural(
LocalizationKeys.basal_saved,
1,
args: {
"status": _isNew ? '${LocalizationKeys.basal_new} ' : ''
},
), basal]);
}
}
});
@ -241,7 +256,14 @@ class _BasalDetailScreenState extends State<BasalDetailScreen> {
return Scaffold(
appBar: AppBar(
title: Text(
'${_isNew ? 'New' : 'Edit'} Basal Rate for ${BasalProfile.get(widget.basalProfileId)?.name}'),
translate(
LocalizationKeys.basal_title,
args: {
"status": _isNew ? LocalizationKeys.basal_new : LocalizationKeys.general_edit,
"profileName": BasalProfile.get(widget.basalProfileId)?.name,
}
),
),
),
drawer: const Navigation(currentLocation: BasalDetailScreen.routeName),
body: Scrollbar(
@ -259,7 +281,7 @@ class _BasalDetailScreenState extends State<BasalDetailScreen> {
child: Padding(
padding: const EdgeInsets.only(right: 5),
child: TimeOfDayFormField(
label: 'Start Time',
label: translate(LocalizationKeys.basal_fields_startTime),
controller: _startTimeController,
time: _startTime,
onChanged: updateStartTime,
@ -270,7 +292,7 @@ class _BasalDetailScreenState extends State<BasalDetailScreen> {
child: Padding(
padding: const EdgeInsets.only(left: 5),
child: TimeOfDayFormField(
label: 'End Time',
label: translate(LocalizationKeys.basal_fields_endTime),
controller: _endTimeController,
time: _endTime,
onChanged: updateEndTime,
@ -281,8 +303,8 @@ class _BasalDetailScreenState extends State<BasalDetailScreen> {
),
NumberFormField(
controller: _unitsController,
label: 'Units',
suffix: 'U',
label: translate(LocalizationKeys.basal_fields_units),
suffix: translate(LocalizationKeys.general_suffixes_units),
autoRoundToMultipleOfStep: true,
step: Settings.insulinSteps,
onChanged: (value) {
@ -291,7 +313,7 @@ class _BasalDetailScreenState extends State<BasalDetailScreen> {
Utils.toStringMatchingTemplateFractionPrecision(
value, Settings.insulinSteps);
}
}),
})
],
),
],
@ -305,8 +327,8 @@ class _BasalDetailScreenState extends State<BasalDetailScreen> {
onMiddleAction: _isSaving || _isFinalRate
? null
: () => handleSaveAction(next: false),
actionText: _isFinalRate ? 'SAVE & CLOSE' : 'NEXT',
middleActionText: 'SAVE & CLOSE',
actionTextKey: translate(_isFinalRate ? LocalizationKeys.general_saveAndClose : LocalizationKeys.general_next).toUpperCase(),
middleActionTextKey: translate(LocalizationKeys.general_saveAndClose).toUpperCase(),
),
);
}

View File

@ -1,3 +1,4 @@
import 'package:diameter/localization_keys.dart';
import 'package:diameter/utils/dialog_utils.dart';
import 'package:diameter/models/settings.dart';
import 'package:diameter/utils/date_time_utils.dart';
@ -5,6 +6,7 @@ import 'package:flutter/material.dart';
import 'package:diameter/models/basal.dart';
import 'package:diameter/models/basal_profile.dart';
import 'package:diameter/screens/basal/basal_detail.dart';
import 'package:flutter_translate/flutter_translate.dart';
class BasalListScreen extends StatefulWidget {
final BasalProfile basalProfile;
@ -61,7 +63,7 @@ class _BasalListScreenState extends State<BasalListScreen> {
void onDelete(Basal basal) {
Basal.remove(basal.id);
reload(message: 'Basal Rate deleted');
reload(message: translate(LocalizationKeys.basal_deleted));
}
void handleDeleteAction(Basal basal) async {
@ -69,7 +71,7 @@ class _BasalListScreenState extends State<BasalListScreen> {
DialogUtils.showConfirmationDialog(
context: context,
onConfirm: () => onDelete(basal),
message: 'Are you sure you want to delete this Basal Rate?',
message: translate(LocalizationKeys.basal_confirmDelete),
);
} else {
onDelete(basal);
@ -83,26 +85,26 @@ class _BasalListScreenState extends State<BasalListScreen> {
// check for gaps
if (index == 0 &&
(basal.startTime.hour != 0 || basal.startTime.minute != 0)) {
return 'First Basal of the day needs to start at 00:00';
return translate(LocalizationKeys.basal_warnings_startTimeFirst);
}
if (index > 0) {
var lastEndTime = basalRates[index - 1].endTime;
if (basal.startTime.isAfter(lastEndTime)) {
return 'There\'s a time gap between this and the previous rate';
return translate(LocalizationKeys.basal_warnings_gap);
}
}
if (index == basalRates.length - 1 &&
(basal.endTime.hour != 0 || basal.endTime.minute != 0)) {
return 'Last Basal of the day needs to end at 00:00';
return translate(LocalizationKeys.basal_warnings_endTimeLast);
}
// check for duplicates
if (basalRates
.where((other) => basal != other && basal.startTime == other.startTime)
.isNotEmpty) {
return 'There are multiple rates with this start time';
return translate(LocalizationKeys.basal_warnings_duplicate);
}
if (basalRates
@ -110,7 +112,7 @@ class _BasalListScreenState extends State<BasalListScreen> {
basal.startTime.isBefore(other.startTime) &&
basal.endTime.isAfter(other.startTime))
.isNotEmpty) {
return 'This rate\'s time period overlaps with another one';
return translate(LocalizationKeys.basal_warnings_overlap);
}
return null;
@ -158,7 +160,7 @@ class _BasalListScreenState extends State<BasalListScreen> {
child: Text(
'${DateTimeUtils.displayTime(basal.startTime)} - ${DateTimeUtils.displayTime(basal.endTime)}')),
const Spacer(),
Expanded(child: Text('${basal.units} U')),
Expanded(child: Text('${basal.units} ${translate(LocalizationKeys.general_suffixes_units)}')),
],
),
trailing: Row(
@ -180,8 +182,8 @@ class _BasalListScreenState extends State<BasalListScreen> {
},
),
)
: const Center(
child: Text('You have not created any Basal Rates yet!'),
: Center(
child: Text(translate(LocalizationKeys.basal_empty)),
);
}
}

View File

@ -1,5 +1,6 @@
import 'package:diameter/components/detail.dart';
import 'package:diameter/components/forms/boolean_form_field.dart';
import 'package:diameter/localization_keys.dart';
import 'package:diameter/utils/dialog_utils.dart';
import 'package:diameter/models/basal.dart';
import 'package:diameter/models/settings.dart';
@ -9,6 +10,7 @@ import 'package:flutter/material.dart';
import 'package:diameter/components/forms/form_wrapper.dart';
import 'package:diameter/models/basal_profile.dart';
import 'package:diameter/screens/basal/basal_list.dart';
import 'package:flutter_translate/flutter_translate.dart';
class BasalProfileDetailScreen extends StatefulWidget {
static const String routeName = '/basal-profile';
@ -133,21 +135,30 @@ class _BasalProfileDetailScreenState extends State<BasalProfileDetailScreen> {
context: context,
builder: (BuildContext context) {
return AlertDialog(
content: const Text(
'There are already one or more active profiles. What would you like to do?'),
content: Text(translate(LocalizationKeys.basalProfile_warnings_activeAlreadySet)),
actions: [
TextButton(
onPressed: () => Navigator.pop(context, 0),
child: const Text('IGNORE'),
child: Text(translate(LocalizationKeys.basalProfile_warnings_resolve_ignore).toUpperCase()),
),
TextButton(
onPressed: () => Navigator.pop(context, 1),
child:
Text('DEACTIVATE ${_nameController.text.toUpperCase()}'),
Text(
translate(
LocalizationKeys.basalProfile_warnings_resolve_deactivateProfile,
args: {
"profileName": _nameController.text,
}
).toUpperCase()
),
),
ElevatedButton(
onPressed: () => Navigator.pop(context, 2),
child: const Text('DEACTIVATE ALL OTHERS'),
child: Text(
translate(
LocalizationKeys.basalProfile_warnings_resolve_deactivateOthers,
).toUpperCase()),
)
],
);
@ -167,16 +178,25 @@ class _BasalProfileDetailScreenState extends State<BasalProfileDetailScreen> {
context: context,
builder: (BuildContext context) {
return AlertDialog(
content: const Text(
'There is currently no active profile. Would you like to set this one as active?'),
content: Text(
translate(
LocalizationKeys.basalProfile_warnings_noActiveOnCreate,
).toUpperCase()),
actions: [
TextButton(
onPressed: () => Navigator.pop(context, 0),
child: const Text('IGNORE'),
child: Text(
translate(
LocalizationKeys.basalProfile_warnings_resolve_ignore,
).toUpperCase()),
),
TextButton(
onPressed: () => Navigator.pop(context, 1),
child: const Text('ACTIVATE THIS PROFILE'),
child: Text(
translate(
LocalizationKeys.basalProfile_warnings_resolve_activateCurrent,
).toUpperCase()
),
),
],
);
@ -249,7 +269,11 @@ class _BasalProfileDetailScreenState extends State<BasalProfileDetailScreen> {
if (close) {
Navigator.pop(context,
['${_isNew ? 'New' : ''} Basal Profile saved', basalProfile]);
[
translate(LocalizationKeys.basalProfile_saved, args: {
"status": _isNew ? '${translate(LocalizationKeys.basalProfile_new)} ' : ''
}),
]);
} else {
if (_isNew) {
Navigator.push(
@ -260,7 +284,7 @@ class _BasalProfileDetailScreenState extends State<BasalProfileDetailScreen> {
),
).then((result) => Navigator.pop(context, result));
} else {
reload(message: 'Basal Profile saved');
reload(message: translate(LocalizationKeys.basalProfile_saved));
}
}
}
@ -329,12 +353,12 @@ class _BasalProfileDetailScreenState extends State<BasalProfileDetailScreen> {
fields: [
TextFormField(
controller: _nameController,
decoration: const InputDecoration(
labelText: 'Name',
decoration: InputDecoration(
labelText: translate(LocalizationKeys.basalProfile_fields_name),
),
validator: (value) {
if (value!.trim().isEmpty) {
return 'Empty title';
return translate(LocalizationKeys.basalProfile_fields_validators_name);
}
return null;
},
@ -342,8 +366,8 @@ class _BasalProfileDetailScreenState extends State<BasalProfileDetailScreen> {
TextFormField(
keyboardType: TextInputType.multiline,
controller: _notesController,
decoration: const InputDecoration(
labelText: 'Notes',
decoration: InputDecoration(
labelText: translate(LocalizationKeys.basalProfile_fields_notes),
),
minLines: 2,
maxLines: 5,
@ -355,7 +379,7 @@ class _BasalProfileDetailScreenState extends State<BasalProfileDetailScreen> {
_active = value;
});
},
label: 'active',
label: translate(LocalizationKeys.basalProfile_fields_active),
),
],
),
@ -374,13 +398,24 @@ class _BasalProfileDetailScreenState extends State<BasalProfileDetailScreen> {
return Scaffold(
appBar: AppBar(
title: Text(_isNew ? 'New Basal Profile' : _basalProfile!.name),
title: Text(translate(LocalizationKeys.basalProfile_detail_title, args: {
"status": _isNew ? '${translate(LocalizationKeys.basalProfile_new)} ' : '',
"profileName": _basalProfile!.name,
})),
bottom: _isNew
? PreferredSize(child: Container(), preferredSize: Size.zero)
: const TabBar(
: TabBar(
tabs: [
Tab(text: 'PROFILE'),
Tab(text: 'RATES'),
Tab(
text: translate(
LocalizationKeys.basalProfile_detail_tabs_profile,
).toUpperCase(),
),
Tab(
text: translate(
LocalizationKeys.basalProfile_detail_tabs_rates,
).toUpperCase(),
),
],
),
actions: appBarActions,

View File

@ -1,3 +1,4 @@
import 'package:diameter/localization_keys.dart';
import 'package:diameter/utils/dialog_utils.dart';
import 'package:diameter/components/forms/auto_complete_dropdown_button.dart';
import 'package:diameter/models/basal.dart';
@ -6,6 +7,7 @@ import 'package:diameter/navigation.dart';
import 'package:flutter/material.dart';
import 'package:diameter/models/basal_profile.dart';
import 'package:diameter/screens/basal/basal_profile_detail.dart';
import 'package:flutter_translate/flutter_translate.dart';
class BasalProfileListScreen extends StatefulWidget {
static const String routeName = '/basal-profiles';
@ -59,26 +61,33 @@ class _BasalProfileListScreenState extends State<BasalProfileListScreen> {
banner = activeProfileCount != 1
? MaterialBanner(
content: Text(activeProfileCount == 0
? 'You currently do not have an active Basal Profile.'
: 'More than one active Basal Profile has been found.'),
? translate(LocalizationKeys.basalProfile_warnings_noActive)
: translate(
LocalizationKeys.basalProfile_warnings_multipleActive)),
leading: const CircleAvatar(child: Icon(Icons.warning)),
forceActionsBelow: true,
actions: activeProfileCount == 0
? [
_basalProfiles.isNotEmpty
? TextButton(
child: const Text('ACTIVATE A PROFILE'),
child: Text(translate(LocalizationKeys
.basalProfile_warnings_resolve_activate)
.toUpperCase()),
onPressed: handlePickActiveProfileAction,
)
: Container(),
TextButton(
child: const Text('CREATE A NEW PROFILE'),
child: Text(translate(LocalizationKeys
.basalProfile_warnings_resolve_create)
.toUpperCase()),
onPressed: () => onNew(true),
),
]
: [
TextButton(
child: const Text('PICK A PROFILE'),
child: Text(translate(LocalizationKeys
.basalProfile_warnings_resolve_pick)
.toUpperCase()),
onPressed: handlePickActiveProfileAction,
),
],
@ -90,8 +99,9 @@ class _BasalProfileListScreenState extends State<BasalProfileListScreen> {
void handleDuplicateAction(BasalProfile basalProfile) async {
final copy = BasalProfile(
active: false,
name: 'Copy of ${basalProfile.name}',
);
name: translate(LocalizationKeys.basalProfile_copyOf, args: {
"profileName": basalProfile.name,
}));
BasalProfile.put(copy);
final rates = Basal.getAllForProfile(basalProfile.id);
@ -105,12 +115,19 @@ class _BasalProfileListScreenState extends State<BasalProfileListScreen> {
Basal.put(basal);
}
reload(message: 'Added copy of ${basalProfile.name}');
reload(
message: translate(
LocalizationKeys.basalProfile_warnings_resolve_ignore,
args: {"profileName": basalProfile.name},
));
}
void onDelete(BasalProfile basalProfile) {
BasalProfile.remove(basalProfile.id);
reload(message: 'Basal Profile deleted');
reload(
message: translate(
LocalizationKeys.basalProfile_deleted,
));
}
void handleDeleteAction(BasalProfile basalProfile) async {
@ -118,7 +135,7 @@ class _BasalProfileListScreenState extends State<BasalProfileListScreen> {
DialogUtils.showConfirmationDialog(
context: context,
onConfirm: () => onDelete(basalProfile),
message: 'Are you sure you want to delete this Basal Profile?',
message: translate(LocalizationKeys.basalProfile_confirmDelete),
);
} else {
onDelete(basalProfile);
@ -131,7 +148,8 @@ class _BasalProfileListScreenState extends State<BasalProfileListScreen> {
basalProfile.active = true;
BasalProfile.put(basalProfile);
reload(
message: '${basalProfile.name} has been set as your active Profile');
message: translate(LocalizationKeys.basalProfile_activated,
args: {"profileName": basalProfile.name}));
}
}
@ -141,14 +159,16 @@ class _BasalProfileListScreenState extends State<BasalProfileListScreen> {
content: AutoCompleteDropdownButton(
controller: TextEditingController(text: ''),
items: _basalProfiles,
label: 'Default Basal Profile',
label: translate(LocalizationKeys.basalProfile_default),
onChanged: onPickActive,
),
leading: const CircleAvatar(child: Icon(Icons.info)),
forceActionsBelow: true,
actions: [
TextButton(
child: const Text('CREATE A NEW PROFILE INSTEAD'),
child: Text(translate(LocalizationKeys
.basalProfile_warnings_resolve_createInstead)
.toUpperCase()),
onPressed: () => onNew(true),
),
],
@ -178,7 +198,7 @@ class _BasalProfileListScreenState extends State<BasalProfileListScreen> {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Basal Profiles'),
title: Text(translate(LocalizationKeys.basalProfile_title)),
actions: <Widget>[
IconButton(onPressed: reload, icon: const Icon(Icons.refresh))
],
@ -202,9 +222,9 @@ class _BasalProfileListScreenState extends State<BasalProfileListScreen> {
double dailyTotal =
Basal.getDailyTotalForProfile(basalProfile.id);
String activeProfileText = basalProfile.active
? ' (Default Profile)'
? '(${translate(LocalizationKeys.basalProfile_default)})'
: basalProfile.id == _activeProfile?.id
? ' (Current Active Profile)'
? ' (${translate(LocalizationKeys.basalProfile_active)})'
: '';
return Card(
child: ListTile(
@ -228,7 +248,9 @@ class _BasalProfileListScreenState extends State<BasalProfileListScreen> {
? [
Text(dailyTotal
.toStringAsPrecision(3)),
const Text('U/day',
Text(
translate(LocalizationKeys
.general_suffixes_perDay),
textScaleFactor: 0.75),
]
: [],
@ -263,8 +285,8 @@ class _BasalProfileListScreenState extends State<BasalProfileListScreen> {
},
),
)
: const Center(
child: Text('You have not created any Basal Profiles yet!'),
: Center(
child: Text(translate(LocalizationKeys.basalProfile_empty)),
),
),
],

View File

@ -1,6 +1,7 @@
import 'package:diameter/components/detail.dart';
import 'package:diameter/components/forms/number_form_field.dart';
import 'package:diameter/components/forms/time_of_day_form_field.dart';
import 'package:diameter/localization_keys.dart';
import 'package:diameter/utils/dialog_utils.dart';
import 'package:diameter/models/settings.dart';
import 'package:diameter/navigation.dart';
@ -10,6 +11,7 @@ import 'package:flutter/material.dart';
import 'package:diameter/components/forms/form_wrapper.dart';
import 'package:diameter/models/bolus.dart';
import 'package:diameter/models/bolus_profile.dart';
import 'package:flutter_translate/flutter_translate.dart';
class BolusDetailScreen extends StatefulWidget {
static const String routeName = '/bolus';
@ -140,7 +142,7 @@ class _BolusDetailScreenState extends State<BolusDetailScreen> {
_startTime.hour == other.startTime.hour &&
_startTime.minute == other.startTime.minute)
.isNotEmpty) {
error = 'There\'s already a rate with this start time.';
error = translate(LocalizationKeys.bolus_warnings_duplicate);
}
if (bolusRates
@ -151,7 +153,7 @@ class _BolusDetailScreenState extends State<BolusDetailScreen> {
DateTimeUtils.convertTimeOfDayToDateTime(_endTime)
.isAfter(other.startTime))
.isNotEmpty) {
error = 'This rate\'s time period overlaps with another one.';
error = translate(LocalizationKeys.bolus_warnings_overlap);
}
return error == null
@ -164,11 +166,11 @@ class _BolusDetailScreenState extends State<BolusDetailScreen> {
actions: <Widget>[
TextButton(
onPressed: () => Navigator.pop(context, 'CANCEL'),
child: const Text('GO BACK TO EDITING'),
child: Text(translate(LocalizationKeys.general_keepEditing).toUpperCase()),
),
ElevatedButton(
onPressed: () => Navigator.pop(context, 'CONFIRM'),
child: const Text('SAVE AS IS'),
child: Text(translate(LocalizationKeys.general_saveAsIs).toUpperCase()),
),
],
);
@ -210,13 +212,27 @@ class _BolusDetailScreenState extends State<BolusDetailScreen> {
).then((result) {
Navigator.pop(
context,
['New Bolus Rate${result[1] != null ? 's' : ''} saved', bolus] +
[
translatePlural(
LocalizationKeys.bolus_saved,
result.length,
args: {
"status": _isNew ? '${LocalizationKeys.bolus_new} ' : ''
},
),
bolus] +
[result[1]],
);
});
} else {
Navigator.pop(
context, ['${_isNew ? 'New' : ''} Bolus Rate saved', bolus]);
context, [translatePlural(
LocalizationKeys.bolus_saved,
1,
args: {
"status": _isNew ? '${LocalizationKeys.bolus_new} ' : ''
},
), bolus]);
}
}
});
@ -288,7 +304,14 @@ class _BolusDetailScreenState extends State<BolusDetailScreen> {
return Scaffold(
appBar: AppBar(
title: Text(
'${_isNew ? 'New' : 'Edit'} Bolus Rate for ${BolusProfile.get(widget.bolusProfileId)?.name}'),
translate(
LocalizationKeys.basal_title,
args: {
"status": _isNew ? LocalizationKeys.bolus_new : LocalizationKeys.general_edit,
"profileName": BolusProfile.get(widget.bolusProfileId)?.name,
}
),
),
),
drawer: const Navigation(currentLocation: BolusDetailScreen.routeName),
body: Scrollbar(
@ -306,7 +329,7 @@ class _BolusDetailScreenState extends State<BolusDetailScreen> {
child: Padding(
padding: const EdgeInsets.only(right: 5),
child: TimeOfDayFormField(
label: 'Start Time',
label: translate(LocalizationKeys.bolus_fields_startTime),
controller: _startTimeController,
time: _startTime,
onChanged: updateStartTime,
@ -317,7 +340,7 @@ class _BolusDetailScreenState extends State<BolusDetailScreen> {
child: Padding(
padding: const EdgeInsets.only(left: 5),
child: TimeOfDayFormField(
label: 'End Time',
label: translate(LocalizationKeys.bolus_fields_endTime),
controller: _endTimeController,
time: _endTime,
onChanged: updateEndTime,
@ -328,8 +351,8 @@ class _BolusDetailScreenState extends State<BolusDetailScreen> {
),
NumberFormField(
controller: _unitsController,
label: 'Units',
suffix: 'U',
label: translate(LocalizationKeys.bolus_fields_units),
suffix: translate(LocalizationKeys.general_suffixes_units),
autoRoundToMultipleOfStep: true,
step: Settings.insulinSteps,
onChanged: (value) {
@ -342,7 +365,7 @@ class _BolusDetailScreenState extends State<BolusDetailScreen> {
),
NumberFormField(
controller: _carbsController,
label: 'per carbs',
label: translate(LocalizationKeys.bolus_fields_perCarbs),
suffix: Settings.nutritionMeasurementSuffix,
autoRoundToMultipleOfStep: true,
step: Settings.nutritionSteps,
@ -365,8 +388,10 @@ class _BolusDetailScreenState extends State<BolusDetailScreen> {
? Expanded(
flex: Settings.glucoseMeasurement == GlucoseMeasurement.mgPerDl ? 2 : 1,
child: NumberFormField(
label: 'per mg/dl',
suffix: 'mg/dl',
label: translate(LocalizationKeys.bolus_fields_perGlucose, args: {
"glucoseMeasurement": Settings.glucoseMeasurementSuffix
}),
suffix: Settings.glucoseMeasurementSuffix,
readOnly: Settings.glucoseMeasurement ==
GlucoseMeasurement.mmolPerL,
showSteppers: Settings.glucoseMeasurement == GlucoseMeasurement.mgPerDl,
@ -384,8 +409,10 @@ class _BolusDetailScreenState extends State<BolusDetailScreen> {
? Expanded(
flex: Settings.glucoseMeasurement == GlucoseMeasurement.mmolPerL ? 2 : 1,
child: NumberFormField(
label: 'per mmol/l',
suffix: 'mmol/l',
label: translate(LocalizationKeys.bolus_fields_perGlucose, args: {
"glucoseMeasurement": Settings.glucoseMeasurementSuffix
}),
suffix: Settings.glucoseMeasurementSuffix,
readOnly: Settings.glucoseMeasurement ==
GlucoseMeasurement.mgPerDl,
showSteppers: Settings.glucoseMeasurement == GlucoseMeasurement.mmolPerL,
@ -410,8 +437,8 @@ class _BolusDetailScreenState extends State<BolusDetailScreen> {
onMiddleAction: _isSaving || _isFinalRate
? null
: () => handleSaveAction(next: false),
actionText: _isFinalRate ? 'SAVE & CLOSE' : 'NEXT',
middleActionText: 'SAVE & CLOSE',
actionTextKey: _isFinalRate ? translate(LocalizationKeys.general_saveAndClose) : translate(LocalizationKeys.general_next),
middleActionTextKey: translate(LocalizationKeys.general_saveAndClose),
),
);
}

View File

@ -1,3 +1,4 @@
import 'package:diameter/localization_keys.dart';
import 'package:diameter/utils/dialog_utils.dart';
import 'package:diameter/models/settings.dart';
import 'package:diameter/utils/date_time_utils.dart';
@ -5,6 +6,7 @@ import 'package:flutter/material.dart';
import 'package:diameter/models/bolus.dart';
import 'package:diameter/models/bolus_profile.dart';
import 'package:diameter/screens/bolus/bolus_detail.dart';
import 'package:flutter_translate/flutter_translate.dart';
class BolusListScreen extends StatefulWidget {
final BolusProfile bolusProfile;
@ -61,7 +63,7 @@ class _BolusListScreenState extends State<BolusListScreen> {
void onDelete(Bolus bolus) {
Bolus.remove(bolus.id);
reload(message: 'Bolus Rate deleted');
reload(message: translate(LocalizationKeys.bolus_deleted));
}
void handleDeleteAction(Bolus bolus) async {
@ -69,7 +71,7 @@ class _BolusListScreenState extends State<BolusListScreen> {
DialogUtils.showConfirmationDialog(
context: context,
onConfirm: () => onDelete(bolus),
message: 'Are you sure you want to delete this Bolus Rate?',
message: translate(LocalizationKeys.bolus_confirmDelete),
);
} else {
onDelete(bolus);
@ -82,26 +84,26 @@ class _BolusListScreenState extends State<BolusListScreen> {
if (index == 0 &&
(bolus.startTime.toLocal().hour != 0 || bolus.startTime.minute != 0)) {
return 'First Bolus of the day needs to start at 00:00';
return translate(LocalizationKeys.bolus_warnings_startTimeFirst);
}
if (index > 0) {
var lastEndTime = bolusRates[index - 1].endTime;
if (bolus.startTime.isAfter(lastEndTime)) {
return 'There\'s a time gap between this and the previous rate';
return translate(LocalizationKeys.bolus_warnings_gap);
}
}
if (index == bolusRates.length - 1 &&
(bolus.endTime.toLocal().hour != 0 || bolus.endTime.minute != 0)) {
return 'Last Bolus of the day needs to end at 00:00';
return translate(LocalizationKeys.bolus_warnings_endTimeLast);
}
// check for duplicates
if (bolusRates
.where((other) => bolus != other && bolus.startTime == other.startTime)
.isNotEmpty) {
return 'There are multiple rates with this start time';
return translate(LocalizationKeys.bolus_warnings_duplicate);
}
if (bolusRates
@ -109,7 +111,7 @@ class _BolusListScreenState extends State<BolusListScreen> {
bolus.startTime.isBefore(other.startTime) &&
bolus.endTime.isAfter(other.startTime))
.isNotEmpty) {
return 'This rate\'s time period overlaps with another one';
return translate(LocalizationKeys.bolus_warnings_overlap);
}
return null;
@ -162,8 +164,9 @@ class _BolusListScreenState extends State<BolusListScreen> {
? [
Text((bolus.carbs / bolus.units)
.toStringAsPrecision(2)),
Text(
'${Settings.nutritionMeasurementSuffix} carbs per U',
Text(translate(LocalizationKeys.general_suffixes_carbsPerU, args: {
"nutritionMeasurementSuffix": Settings.nutritionMeasurementSuffix
}),
textAlign: TextAlign.center,
textScaleFactor: 0.75),
]
@ -176,7 +179,7 @@ class _BolusListScreenState extends State<BolusListScreen> {
? [
Text((bolus.units / bolus.carbs * 12)
.toStringAsPrecision(2)),
const Text('U per bread unit',
Text(translate(LocalizationKeys.general_suffixes_uPerBreadUnit),
textAlign: TextAlign.center,
textScaleFactor: 0.75),
]
@ -196,8 +199,9 @@ class _BolusListScreenState extends State<BolusListScreen> {
: bolus.mmolPerL ?? 0)! /
bolus.units))
.toString()),
Text(
'${Settings.glucoseMeasurementSuffix} per unit',
Text(translate(LocalizationKeys.general_suffixes_uPerGlucose, args: {
"glucoseMeasurementSuffix": Settings.glucoseMeasurementSuffix
}),
textAlign: TextAlign.center,
textScaleFactor: 0.75),
]
@ -225,8 +229,8 @@ class _BolusListScreenState extends State<BolusListScreen> {
},
),
)
: const Center(
child: Text('You have not created any Bolus Rates yet!'),
: Center(
child: Text(translate(LocalizationKeys.bolus_title)),
);
}
}

View File

@ -1,5 +1,6 @@
import 'package:diameter/components/detail.dart';
import 'package:diameter/components/forms/boolean_form_field.dart';
import 'package:diameter/localization_keys.dart';
import 'package:diameter/utils/dialog_utils.dart';
import 'package:diameter/models/bolus.dart';
import 'package:diameter/models/settings.dart';
@ -9,6 +10,7 @@ import 'package:flutter/material.dart';
import 'package:diameter/components/forms/form_wrapper.dart';
import 'package:diameter/models/bolus_profile.dart';
import 'package:diameter/screens/bolus/bolus_list.dart';
import 'package:flutter_translate/flutter_translate.dart';
class BolusProfileDetailScreen extends StatefulWidget {
static const String routeName = '/bolus-profile';
@ -131,20 +133,19 @@ class _BolusProfileDetailScreenState extends State<BolusProfileDetailScreen> {
context: context,
builder: (BuildContext context) {
return AlertDialog(
content: const Text(
'There are already one or more active profiles. What would you like to do?'),
content: Text(translate(LocalizationKeys.bolusProfile_warnings_activeAlreadySet)),
actions: [
TextButton(
onPressed: () => Navigator.pop(context, 0),
child: const Text('IGNORE'),
child: Text(translate(LocalizationKeys.bolusProfile_warnings_resolve_ignore).toUpperCase()),
),
TextButton(
onPressed: () => Navigator.pop(context, 1),
child: const Text('DEACTIVATE THIS PROFILE'),
child: Text(translate(LocalizationKeys.bolusProfile_warnings_resolve_deactivateProfile).toUpperCase()),
),
ElevatedButton(
onPressed: () => Navigator.pop(context, 2),
child: const Text('DEACTIVATE ALL OTHERS'),
child: Text(translate(LocalizationKeys.bolusProfile_warnings_resolve_deactivateOthers).toUpperCase()),
)
],
);
@ -164,16 +165,15 @@ class _BolusProfileDetailScreenState extends State<BolusProfileDetailScreen> {
context: context,
builder: (BuildContext context) {
return AlertDialog(
content: const Text(
'There is currently no active profile. Would you like to set this one as active?'),
content: Text(translate(LocalizationKeys.bolusProfile_warnings_noActiveOnCreate)),
actions: [
TextButton(
onPressed: () => Navigator.pop(context, 0),
child: const Text('IGNORE'),
child: Text(translate(LocalizationKeys.bolusProfile_warnings_resolve_ignore).toUpperCase()),
),
TextButton(
onPressed: () => Navigator.pop(context, 1),
child: const Text('ACTIVATE THIS PROFILE'),
child: Text(translate(LocalizationKeys.bolusProfile_warnings_resolve_activateCurrent).toUpperCase()),
),
],
);
@ -246,8 +246,15 @@ class _BolusProfileDetailScreenState extends State<BolusProfileDetailScreen> {
BolusProfile.put(bolusProfile);
if (close) {
Navigator.pop(context,
['${_isNew ? 'New' : ''} Bolus Profile saved', bolusProfile]);
Navigator.pop(context, [
translate(
LocalizationKeys.bolusProfile_saved,
args: {
"status": _isNew ? '${translate(LocalizationKeys.bolusProfile_new)} ' : ''
},
), bolusProfile
],
);
} else {
if (_isNew) {
Navigator.push(
@ -258,7 +265,7 @@ class _BolusProfileDetailScreenState extends State<BolusProfileDetailScreen> {
),
).then((result) => Navigator.pop(context, result));
} else {
reload(message: 'Bolus Profile saved');
reload(message: translate(LocalizationKeys.bolusProfile_saved));
}
}
}
@ -329,19 +336,19 @@ class _BolusProfileDetailScreenState extends State<BolusProfileDetailScreen> {
fields: [
TextFormField(
controller: _nameController,
decoration: const InputDecoration(
labelText: 'Name',
decoration: InputDecoration(
labelText: translate(LocalizationKeys.bolusProfile_fields_name),
),
validator: (value) {
if (value!.trim().isEmpty) {
return 'Empty title';
return translate(LocalizationKeys.bolusProfile_fields_validators_name);
}
return null;
},
),
TextFormField(
decoration: const InputDecoration(
labelText: 'Notes',
decoration: InputDecoration(
labelText: translate(LocalizationKeys.bolusProfile_fields_notes),
),
controller: _notesController,
keyboardType: TextInputType.multiline,
@ -355,7 +362,7 @@ class _BolusProfileDetailScreenState extends State<BolusProfileDetailScreen> {
_active = value;
});
},
label: 'active',
label: translate(LocalizationKeys.bolusProfile_fields_active),
),
],
),
@ -374,13 +381,16 @@ class _BolusProfileDetailScreenState extends State<BolusProfileDetailScreen> {
return Scaffold(
appBar: AppBar(
title: Text(_isNew ? 'New Bolus Profile' : _bolusProfile!.name),
title: Text(translate(LocalizationKeys.bolusProfile_detail_title, args: {
"status": _isNew ? '${translate(LocalizationKeys.bolusProfile_new)} ' : '',
"profileName": _bolusProfile!.name,
})),
bottom: _isNew
? PreferredSize(child: Container(), preferredSize: Size.zero)
: const TabBar(
: TabBar(
tabs: [
Tab(text: 'PROFILE'),
Tab(text: 'RATES'),
Tab(text: translate(LocalizationKeys.bolusProfile_detail_tabs_profile).toUpperCase()),
Tab(text: translate(LocalizationKeys.bolusProfile_detail_tabs_rates).toUpperCase()),
],
),
actions: appBarActions,

View File

@ -1,3 +1,4 @@
import 'package:diameter/localization_keys.dart';
import 'package:diameter/utils/dialog_utils.dart';
import 'package:diameter/components/forms/auto_complete_dropdown_button.dart';
import 'package:diameter/models/bolus.dart';
@ -6,6 +7,7 @@ import 'package:diameter/navigation.dart';
import 'package:flutter/material.dart';
import 'package:diameter/models/bolus_profile.dart';
import 'package:diameter/screens/bolus/bolus_profile_detail.dart';
import 'package:flutter_translate/flutter_translate.dart';
class BolusProfileListScreen extends StatefulWidget {
static const String routeName = '/bolus-profiles';
@ -62,26 +64,26 @@ class _BolusProfileListScreenState extends State<BolusProfileListScreen> {
banner = activeProfileCount != 1
? MaterialBanner(
content: Text(activeProfileCount == 0
? 'You currently do not have an active Bolus Profile.'
: 'More than one active Bolus Profile has been found.'),
? translate(LocalizationKeys.bolusProfile_warnings_noActive)
: translate(LocalizationKeys.bolusProfile_warnings_multipleActive)),
leading: const CircleAvatar(child: Icon(Icons.warning)),
forceActionsBelow: true,
actions: activeProfileCount == 0
? [
_bolusProfiles.isNotEmpty
? TextButton(
child: const Text('ACTIVATE A PROFILE'),
child: Text(translate(LocalizationKeys.bolusProfile_warnings_resolve_activate).toUpperCase()),
onPressed: handlePickActiveProfileAction,
)
: Container(),
TextButton(
child: const Text('CREATE A NEW PROFILE'),
child: Text(translate(LocalizationKeys.bolusProfile_warnings_resolve_create).toUpperCase()),
onPressed: () => onNew(true),
),
]
: [
TextButton(
child: const Text('PICK A PROFILE'),
child: Text(translate(LocalizationKeys.bolusProfile_warnings_resolve_pick).toUpperCase()),
onPressed: handlePickActiveProfileAction,
),
],
@ -93,7 +95,12 @@ class _BolusProfileListScreenState extends State<BolusProfileListScreen> {
void handleDuplicateAction(BolusProfile bolusProfile) async {
final copy = BolusProfile(
active: false,
name: 'Copy of ${bolusProfile.name}',
name: translate(
LocalizationKeys.bolusProfile_copyOf,
args: {
"profileName": bolusProfile.name
},
),
);
BolusProfile.put(copy);
@ -111,12 +118,19 @@ class _BolusProfileListScreenState extends State<BolusProfileListScreen> {
Bolus.put(bolus);
}
reload(message: 'Added copy of ${bolusProfile.name}');
reload(
message: translate(
LocalizationKeys.bolusProfile_copied,
args: {
"profileName": bolusProfile.name
},
),
);
}
void onDelete(BolusProfile bolusProfile) {
BolusProfile.remove(bolusProfile.id);
reload(message: 'Bolus Profile deleted');
reload(message: translate(LocalizationKeys.bolusProfile_deleted));
}
void handleDeleteAction(BolusProfile bolusProfile) async {
@ -124,7 +138,7 @@ class _BolusProfileListScreenState extends State<BolusProfileListScreen> {
DialogUtils.showConfirmationDialog(
context: context,
onConfirm: () => onDelete(bolusProfile),
message: 'Are you sure you want to delete this Bolus Profile?',
message: translate(LocalizationKeys.bolusProfile_confirmDelete),
);
} else {
onDelete(bolusProfile);
@ -137,7 +151,13 @@ class _BolusProfileListScreenState extends State<BolusProfileListScreen> {
bolusProfile.active = true;
BolusProfile.put(bolusProfile);
reload(
message: '${bolusProfile.name} has been set as your active Profile');
message: translate(
LocalizationKeys.bolusProfile_activated,
args: {
"profileName": bolusProfile.name
},
),
);
}
}
@ -147,14 +167,14 @@ class _BolusProfileListScreenState extends State<BolusProfileListScreen> {
content: AutoCompleteDropdownButton(
controller: TextEditingController(text: ''),
items: _bolusProfiles,
label: 'Default Basal Profile',
label: translate(LocalizationKeys.bolusProfile_default),
onChanged: onPickActive,
),
leading: const CircleAvatar(child: Icon(Icons.info)),
forceActionsBelow: true,
actions: [
TextButton(
child: const Text('CREATE A NEW PROFILE INSTEAD'),
child: Text(translate(LocalizationKeys.bolusProfile_warnings_resolve_createInstead).toUpperCase()),
onPressed: () => onNew(true),
),
],
@ -184,7 +204,7 @@ class _BolusProfileListScreenState extends State<BolusProfileListScreen> {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Bolus Profiles'),
title: Text(translate(LocalizationKeys.bolusProfile_title)),
actions: <Widget>[
IconButton(onPressed: reload, icon: const Icon(Icons.refresh))
],
@ -206,9 +226,9 @@ class _BolusProfileListScreenState extends State<BolusProfileListScreen> {
itemBuilder: (context, index) {
final bolusProfile = _bolusProfiles[index];
String activeProfileText = bolusProfile.active
? ' (Default Profile)'
? ' (${translate(LocalizationKeys.bolusProfile_default)})'
: bolusProfile.id == _activeProfile?.id
? ' (Current Active Profile)'
? ' (${translate(LocalizationKeys.bolusProfile_active)})'
: '';
return Card(
child: ListTile(
@ -250,8 +270,8 @@ class _BolusProfileListScreenState extends State<BolusProfileListScreen> {
},
),
)
: const Center(
child: Text('You have not created any Bolus Profiles yet!'),
: Center(
child: Text(translate(LocalizationKeys.bolusProfile_empty)),
),
),
],

View File

@ -1,12 +1,14 @@
import 'package:diameter/components/detail.dart';
import 'package:diameter/components/forms/boolean_form_field.dart';
import 'package:diameter/components/forms/number_form_field.dart';
import 'package:diameter/localization_keys.dart';
import 'package:diameter/utils/dialog_utils.dart';
import 'package:diameter/models/settings.dart';
import 'package:diameter/navigation.dart';
import 'package:flutter/material.dart';
import 'package:diameter/components/forms/form_wrapper.dart';
import 'package:diameter/models/accuracy.dart';
import 'package:flutter_translate/flutter_translate.dart';
class AccuracyDetailScreen extends StatefulWidget {
static const String routeName = '/accuracy';
@ -93,7 +95,12 @@ class _AccuracyDetailScreenState extends State<AccuracyDetailScreen> {
Accuracy.reorder(
accuracy, int.tryParse(_confidenceRatingController.text));
Navigator.pop(
context, ['${_isNew ? 'New' : ''} Accuracy saved', accuracy]);
context, [translate(
LocalizationKeys.accuracy_saved,
args: {
"status": _isNew ? '${translate(LocalizationKeys.accuracy_new)} ' : ''
},
), accuracy]);
}
setState(() {
_isSaving = false;
@ -130,7 +137,9 @@ class _AccuracyDetailScreenState extends State<AccuracyDetailScreen> {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(_isNew ? 'New Accuracy' : _accuracy!.value),
title: Text(translate(LocalizationKeys.accuracy_detail_title, args: {
"status": _isNew ? '${translate(LocalizationKeys.accuracy_new)} ' : '',
})),
),
drawer: const Navigation(currentLocation: AccuracyDetailScreen.routeName),
body: Scrollbar(
@ -145,12 +154,12 @@ class _AccuracyDetailScreenState extends State<AccuracyDetailScreen> {
fields: [
TextFormField(
controller: _valueController,
decoration: const InputDecoration(
labelText: 'Name',
decoration: InputDecoration(
labelText: translate(LocalizationKeys.accuracy_fields_name),
),
validator: (value) {
if (value!.trim().isEmpty) {
return 'Empty name';
return translate(LocalizationKeys.accuracy_fields_validators_name);
}
return null;
},
@ -158,7 +167,7 @@ class _AccuracyDetailScreenState extends State<AccuracyDetailScreen> {
BooleanFormField(
icon: const Icon(Icons.square_foot),
value: _forPortionSize,
label: 'for portion size',
label: translate(LocalizationKeys.accuracy_fields_forPortionSize),
onChanged: (value) {
setState(() {
_forPortionSize = value;
@ -168,7 +177,7 @@ class _AccuracyDetailScreenState extends State<AccuracyDetailScreen> {
BooleanFormField(
icon: const Icon(Icons.pie_chart),
value: _forCarbsRatio,
label: 'for carbs ratio',
label: translate(LocalizationKeys.accuracy_fields_forCarbsRatio),
onChanged: (value) {
setState(() {
_forCarbsRatio = value;
@ -177,7 +186,7 @@ class _AccuracyDetailScreenState extends State<AccuracyDetailScreen> {
),
NumberFormField(
controller: _confidenceRatingController,
label: 'Confidence Rating',
label: translate(LocalizationKeys.accuracy_fields_confidenceRating),
onChanged: (value) {
setState(() {
_confidenceRatingController.text =
@ -188,8 +197,8 @@ class _AccuracyDetailScreenState extends State<AccuracyDetailScreen> {
TextFormField(
controller: _notesController,
keyboardType: TextInputType.multiline,
decoration: const InputDecoration(
labelText: 'Notes',
decoration: InputDecoration(
labelText: translate(LocalizationKeys.accuracy_fields_notes),
),
minLines: 2,
maxLines: 5,

View File

@ -1,9 +1,11 @@
import 'package:diameter/localization_keys.dart';
import 'package:diameter/utils/dialog_utils.dart';
import 'package:diameter/models/settings.dart';
import 'package:diameter/navigation.dart';
import 'package:diameter/screens/category/accuracy_detail.dart';
import 'package:flutter/material.dart';
import 'package:diameter/models/accuracy.dart';
import 'package:flutter_translate/flutter_translate.dart';
class AccuracyListScreen extends StatefulWidget {
static const String routeName = '/accuracies';
@ -58,7 +60,7 @@ class _AccuracyListScreenState extends State<AccuracyListScreen> {
DialogUtils.showConfirmationDialog(
context: context,
onConfirm: () => onDelete(accuracy),
message: 'Are you sure you want to delete this Accuracy?',
message: translate(LocalizationKeys.accuracy_confirmDelete),
);
} else {
onDelete(accuracy);
@ -81,7 +83,7 @@ class _AccuracyListScreenState extends State<AccuracyListScreen> {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Accuracies'),
title: Text(translate(LocalizationKeys.accuracy_detail)),
actions: <Widget>[
IconButton(onPressed: reload, icon: const Icon(Icons.refresh))
],
@ -164,8 +166,8 @@ class _AccuracyListScreenState extends State<AccuracyListScreen> {
);
}),
)
: const Center(
child: Text('You have not created any Accuracies yet!'),
: Center(
child: Text(translate(LocalizationKeys.accuracy_empty)),
),
),
],

View File

@ -1,5 +1,7 @@
import 'package:diameter/localization_keys.dart';
import 'package:diameter/navigation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_translate/flutter_translate.dart';
class CategoryOverviewScreen extends StatefulWidget {
static const String routeName = '/category';
@ -22,7 +24,7 @@ class _CategoryOverviewScreenState extends State<CategoryOverviewScreen> {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Reports'),
title: Text(translate(LocalizationKeys.categories)),
),
drawer:
const Navigation(currentLocation: CategoryOverviewScreen.routeName),
@ -45,7 +47,7 @@ class _CategoryOverviewScreenState extends State<CategoryOverviewScreen> {
child: Icon(Icons.local_grocery_store, size: 50, color: Theme.of(context).textTheme.subtitle2?.color),
),
Text(
'MEAL SOURCES',
translate(LocalizationKeys.mealSource_title).toUpperCase(),
style: Theme.of(context).textTheme.subtitle2,
textAlign: TextAlign.center
),
@ -66,7 +68,7 @@ class _CategoryOverviewScreenState extends State<CategoryOverviewScreen> {
child: Icon(Icons.category, size: 50, color: Theme.of(context).textTheme.subtitle2?.color),
),
Text(
'MEAL CATEGORIES',
translate(LocalizationKeys.mealCategory_title).toUpperCase(),
style: Theme.of(context).textTheme.subtitle2,
textAlign: TextAlign.center
),
@ -86,7 +88,7 @@ class _CategoryOverviewScreenState extends State<CategoryOverviewScreen> {
child: Icon(Icons.pie_chart, size: 50, color: Theme.of(context).textTheme.subtitle2?.color),
),
Text(
'PORTION TYPES',
translate(LocalizationKeys.portionType_title).toUpperCase(),
style: Theme.of(context).textTheme.subtitle2,
textAlign: TextAlign.center
),
@ -107,7 +109,7 @@ class _CategoryOverviewScreenState extends State<CategoryOverviewScreen> {
child: Icon(Icons.architecture, size: 50, color: Theme.of(context).textTheme.subtitle2?.color),
),
Text(
'ACCURACIES',
translate(LocalizationKeys.accuracy_title).toUpperCase(),
style: Theme.of(context).textTheme.subtitle2,
textAlign: TextAlign.center
),
@ -127,7 +129,7 @@ class _CategoryOverviewScreenState extends State<CategoryOverviewScreen> {
child: Icon(Icons.event, size: 50, color: Theme.of(context).textTheme.subtitle2?.color),
),
Text(
'EVENT TYPES',
translate(LocalizationKeys.eventType_title).toUpperCase(),
style: Theme.of(context).textTheme.subtitle2,
textAlign: TextAlign.center
),

View File

@ -1,6 +1,7 @@
import 'package:diameter/components/detail.dart';
import 'package:diameter/components/forms/boolean_form_field.dart';
import 'package:diameter/components/forms/duration_form_field.dart';
import 'package:diameter/localization_keys.dart';
import 'package:diameter/utils/dialog_utils.dart';
import 'package:diameter/components/forms/auto_complete_dropdown_button.dart';
import 'package:diameter/components/forms/form_wrapper.dart';
@ -12,6 +13,7 @@ import 'package:diameter/navigation.dart';
import 'package:diameter/screens/basal/basal_profile_detail.dart';
import 'package:diameter/screens/bolus/bolus_profile_detail.dart';
import 'package:flutter/material.dart';
import 'package:flutter_translate/flutter_translate.dart';
class EventTypeDetailScreen extends StatefulWidget {
static const String routeName = '/log-event-type';
@ -126,7 +128,9 @@ class _EventTypeDetailScreenState extends State<EventTypeDetailScreen> {
eventType.bolusProfile.target = _bolusProfile;
LogEventType.put(eventType);
Navigator.pop(
context, ['${_isNew ? 'New' : ''} Log Event Type Saved', eventType]);
context, [translate(LocalizationKeys.eventType_saved, args: {
"status": _isNew ? LocalizationKeys.eventType_new : '',
}), eventType]);
}
setState(() {
_isSaving = false;
@ -161,7 +165,10 @@ class _EventTypeDetailScreenState extends State<EventTypeDetailScreen> {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(_isNew ? 'New Log Event Type' : _logEventType!.value),
title: Text(translate(LocalizationKeys.eventType_detail_title, args: {
"status": _isNew ? LocalizationKeys.eventType_new : LocalizationKeys.general_edit,
"name": _isNew ? '' : _logEventType!.value,
}))
),
drawer:
const Navigation(currentLocation: EventTypeDetailScreen.routeName),
@ -175,19 +182,19 @@ class _EventTypeDetailScreenState extends State<EventTypeDetailScreen> {
FormWrapper(formState: _logEventTypeForm, fields: [
TextFormField(
controller: _valueController,
decoration: const InputDecoration(
labelText: 'Name',
decoration: InputDecoration(
labelText: translate(LocalizationKeys.eventType_fields_name),
),
validator: (value) {
if (value!.trim().isEmpty) {
return 'Empty name';
return translate(LocalizationKeys.eventType_fields_validators_name);
}
return null;
},
),
BooleanFormField(
value: _hasEndTime,
label: 'has end time',
label: translate(LocalizationKeys.eventType_fields_hasEndTime),
onChanged: (value) {
setState(() {
_hasEndTime = value;
@ -201,7 +208,7 @@ class _EventTypeDetailScreenState extends State<EventTypeDetailScreen> {
padding: const EdgeInsets.only(bottom: 10.0),
child: DurationFormField(
minutes: _defaultReminderDuration,
label: 'Default Reminder Duration',
label: translate(LocalizationKeys.eventType_fields_defaultReminderDuration),
onChanged: (value) => _defaultReminderDuration = value ?? 0,
showSteppers: true,
),
@ -215,7 +222,7 @@ class _EventTypeDetailScreenState extends State<EventTypeDetailScreen> {
BolusProfile>(
selectedItem: _bolusProfile,
controller: _bolusProfileController,
label: 'Bolus Profile',
label: translate(LocalizationKeys.eventType_fields_bolusProfile),
items: _bolusProfiles,
onChanged: updateBolusProfile,
),
@ -252,7 +259,7 @@ class _EventTypeDetailScreenState extends State<EventTypeDetailScreen> {
AutoCompleteDropdownButton<BasalProfile>(
controller: _basalProfileController,
selectedItem: _basalProfile,
label: 'Basal Profile',
label: translate(LocalizationKeys.eventType_fields_basalProfile),
items: _basalProfiles,
onChanged: updateBasalProfile,
),
@ -283,8 +290,8 @@ class _EventTypeDetailScreenState extends State<EventTypeDetailScreen> {
: []),
TextFormField(
controller: _notesController,
decoration: const InputDecoration(
labelText: 'Notes',
decoration: InputDecoration(
labelText: translate(LocalizationKeys.eventType_fields_notes),
),
keyboardType: TextInputType.multiline,
minLines: 2,

View File

@ -1,7 +1,9 @@
import 'package:diameter/localization_keys.dart';
import 'package:diameter/models/log_event_type.dart';
import 'package:diameter/navigation.dart';
import 'package:diameter/screens/category/event_type_detail.dart';
import 'package:flutter/material.dart';
import 'package:flutter_translate/flutter_translate.dart';
class EventTypeListScreen extends StatefulWidget {
static const String routeName = '/log-event-types';
@ -48,7 +50,7 @@ class _EventTypeListScreenState extends State<EventTypeListScreen> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Log Event Types'), actions: <Widget>[
appBar: AppBar(title: Text(translate(LocalizationKeys.eventType_title)), actions: <Widget>[
IconButton(onPressed: reload, icon: const Icon(Icons.refresh))
]),
drawer:
@ -88,7 +90,7 @@ class _EventTypeListScreenState extends State<EventTypeListScreen> {
IconButton(
onPressed: () async {
LogEventType.remove(logEventType.id);
reload(message: 'Log Event Type deleted');
reload(message: translate(LocalizationKeys.eventType_deleted));
},
icon: const Icon(Icons.delete,
color: Colors.blue),
@ -100,9 +102,9 @@ class _EventTypeListScreenState extends State<EventTypeListScreen> {
},
),
)
: const Center(
: Center(
child:
Text('You have not created any Log Event Types yet!'),
Text(translate(LocalizationKeys.eventType_empty)),
),
),
],

View File

@ -1,10 +1,12 @@
import 'package:diameter/components/detail.dart';
import 'package:diameter/localization_keys.dart';
import 'package:diameter/utils/dialog_utils.dart';
import 'package:diameter/components/forms/form_wrapper.dart';
import 'package:diameter/models/settings.dart';
import 'package:diameter/navigation.dart';
import 'package:flutter/material.dart';
import 'package:diameter/models/meal_category.dart';
import 'package:flutter_translate/flutter_translate.dart';
class MealCategoryDetailScreen extends StatefulWidget {
static const String routeName = '/meal-category';
@ -76,7 +78,9 @@ class _MealCategoryDetailScreenState extends State<MealCategoryDetailScreen> {
);
MealCategory.put(mealCategory);
Navigator.pop(context, [
'${_isNew ? 'New' : ''} Meal Category saved', mealCategory
translate(LocalizationKeys.mealCategory_saved, args: {
"status": _isNew ? LocalizationKeys.mealCategory_new : '',
}), mealCategory
]);
}
}
@ -102,7 +106,11 @@ class _MealCategoryDetailScreenState extends State<MealCategoryDetailScreen> {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(_isNew ? 'New Meal Category' : _mealCategory!.value),
title: Text(translate(LocalizationKeys.mealCategory_detail_title, args: {
"status": _isNew ? LocalizationKeys.mealCategory_new : '',
"name": _mealCategory!.value
}),
),
),
drawer:
const Navigation(currentLocation: MealCategoryDetailScreen.routeName),
@ -118,20 +126,20 @@ class _MealCategoryDetailScreenState extends State<MealCategoryDetailScreen> {
fields: [
TextFormField(
controller: _valueController,
decoration: const InputDecoration(
labelText: 'Name',
decoration: InputDecoration(
labelText: translate(LocalizationKeys.mealCategory_fields_name),
),
validator: (value) {
if (value!.trim().isEmpty) {
return 'Empty name';
return translate(LocalizationKeys.mealCategory_fields_validators_name);
}
return null;
},
),
TextFormField(
controller: _notesController,
decoration: const InputDecoration(
labelText: 'Notes',
decoration: InputDecoration(
labelText: translate(LocalizationKeys.mealCategory_fields_notes),
),
keyboardType: TextInputType.multiline,
minLines: 2,

View File

@ -1,9 +1,11 @@
import 'package:diameter/localization_keys.dart';
import 'package:diameter/utils/dialog_utils.dart';
import 'package:diameter/models/settings.dart';
import 'package:diameter/navigation.dart';
import 'package:diameter/screens/category/meal_category_detail.dart';
import 'package:flutter/material.dart';
import 'package:diameter/models/meal_category.dart';
import 'package:flutter_translate/flutter_translate.dart';
class MealCategoryListScreen extends StatefulWidget {
static const String routeName = '/meal-categories';
@ -50,7 +52,7 @@ class _MealCategoryListScreenState extends State<MealCategoryListScreen> {
void onDelete(MealCategory mealCategory) {
MealCategory.remove(mealCategory.id);
reload(message: 'Meal Category deleted');
reload(message: translate(LocalizationKeys.mealCategory_deleted));
}
void handleDeleteAction(MealCategory mealCategory) async {
@ -58,7 +60,7 @@ class _MealCategoryListScreenState extends State<MealCategoryListScreen> {
DialogUtils.showConfirmationDialog(
context: context,
onConfirm: () => onDelete(mealCategory),
message: 'Are you sure you want to delete this Meal Category?',
message: translate(LocalizationKeys.mealCategory_confirmDelete),
);
} else {
onDelete(mealCategory);
@ -69,7 +71,7 @@ class _MealCategoryListScreenState extends State<MealCategoryListScreen> {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Meal Categories'),
title: Text(translate(LocalizationKeys.mealCategory_title)),
actions: <Widget>[
IconButton(
onPressed: reload,
@ -125,8 +127,8 @@ class _MealCategoryListScreenState extends State<MealCategoryListScreen> {
);
},
),
): const Center(
child: Text('You have not created any Meal Categories yet!'),
): Center(
child: Text(translate(LocalizationKeys.mealCategory_empty)),
),
),
],

View File

@ -1,10 +1,12 @@
import 'package:diameter/components/detail.dart';
import 'package:diameter/localization_keys.dart';
import 'package:diameter/utils/dialog_utils.dart';
import 'package:diameter/components/forms/form_wrapper.dart';
import 'package:diameter/models/settings.dart';
import 'package:diameter/navigation.dart';
import 'package:flutter/material.dart';
import 'package:diameter/models/meal_portion_type.dart';
import 'package:flutter_translate/flutter_translate.dart';
class MealPortionTypeDetailScreen extends StatefulWidget {
static const String routeName = '/meal-portion-type';
@ -78,7 +80,9 @@ class _MealPortionTypeDetailScreenState
);
MealPortionType.put(mealPortionType);
Navigator.pop(context,
['${_isNew ? 'New' : ''} Meal Portion Type saved', mealPortionType]);
[translate(LocalizationKeys.portionType_saved, args: {
"status": _isNew ? LocalizationKeys.portionType_new : '',
}), mealPortionType]);
}
}
@ -102,10 +106,12 @@ class _MealPortionTypeDetailScreenState
@override
Widget build(BuildContext context) {
bool isNew = _mealPortionType == null;
return Scaffold(
appBar: AppBar(
title: Text(isNew ? 'New Meal Portion Type' : _mealPortionType!.value),
title: Text(translate(LocalizationKeys.portionType_detail_title, args: {
"status": translate(_isNew ? LocalizationKeys.portionType_new : LocalizationKeys.general_edit),
"name": _mealPortionType!.value,
})),
),
drawer: const Navigation(
currentLocation: MealPortionTypeDetailScreen.routeName),
@ -121,20 +127,20 @@ class _MealPortionTypeDetailScreenState
fields: [
TextFormField(
controller: _valueController,
decoration: const InputDecoration(
labelText: 'Name',
decoration: InputDecoration(
labelText: translate(LocalizationKeys.portionType_fields_name),
),
validator: (value) {
if (value!.trim().isEmpty) {
return 'Empty name';
return translate(LocalizationKeys.portionType_fields_validators_name);
}
return null;
},
),
TextFormField(
controller: _notesController,
decoration: const InputDecoration(
labelText: 'Notes',
decoration: InputDecoration(
labelText: translate(LocalizationKeys.portionType_fields_notes),
),
keyboardType: TextInputType.multiline,
minLines: 2,

View File

@ -1,9 +1,11 @@
import 'package:diameter/localization_keys.dart';
import 'package:diameter/utils/dialog_utils.dart';
import 'package:diameter/models/settings.dart';
import 'package:diameter/navigation.dart';
import 'package:diameter/screens/category/meal_portion_type_detail.dart';
import 'package:flutter/material.dart';
import 'package:diameter/models/meal_portion_type.dart';
import 'package:flutter_translate/flutter_translate.dart';
class MealPortionTypeListScreen extends StatefulWidget {
static const String routeName = '/meal-portion-types';
@ -51,7 +53,7 @@ class _MealPortionTypeListScreenState extends State<MealPortionTypeListScreen> {
void onDelete(MealPortionType mealPortionType) {
MealPortionType.remove(mealPortionType.id);
reload(message: 'Meal Portion Type deleted');
reload(message: translate(LocalizationKeys.portionType_deleted));
}
void handleDeleteAction(MealPortionType mealPortionType) async {
@ -59,7 +61,7 @@ class _MealPortionTypeListScreenState extends State<MealPortionTypeListScreen> {
DialogUtils.showConfirmationDialog(
context: context,
onConfirm: () => onDelete(mealPortionType),
message: 'Are you sure you want to delete this Meal Portion Type?',
message: translate(LocalizationKeys.portionType_confirmDelete),
);
} else {
onDelete(mealPortionType);
@ -70,7 +72,7 @@ class _MealPortionTypeListScreenState extends State<MealPortionTypeListScreen> {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Meal Portion Types'),
title: Text(translate(LocalizationKeys.portionType_title)),
actions: <Widget>[
IconButton(onPressed: reload, icon: const Icon(Icons.refresh))
],
@ -124,8 +126,8 @@ class _MealPortionTypeListScreenState extends State<MealPortionTypeListScreen> {
);
},
),
) : const Center(
child: Text('You have not created any Meal Portion Types yet!'),
) : Center(
child: Text(translate(LocalizationKeys.portionType_empty)),
),
),
],

View File

@ -1,4 +1,5 @@
import 'package:diameter/components/detail.dart';
import 'package:diameter/localization_keys.dart';
import 'package:diameter/utils/dialog_utils.dart';
import 'package:diameter/components/forms/auto_complete_dropdown_button.dart';
import 'package:diameter/components/forms/form_wrapper.dart';
@ -12,6 +13,7 @@ import 'package:diameter/screens/category/accuracy_detail.dart';
import 'package:diameter/screens/category/meal_category_detail.dart';
import 'package:diameter/screens/category/meal_portion_type_detail.dart';
import 'package:flutter/material.dart';
import 'package:flutter_translate/flutter_translate.dart';
class MealSourceDetailScreen extends StatefulWidget {
static const String routeName = '/meal-source';
@ -121,7 +123,9 @@ class _MealSourceDetailScreenState extends State<MealSourceDetailScreen> {
mealSource.defaultMealCategory.target = _defaultMealCategory;
mealSource.defaultMealPortionType.target = _defaultMealPortionType;
MealSource.put(mealSource);
Navigator.pop(context, ['${_isNew ? 'New' : ''} Meal Source saved', mealSource]);
Navigator.pop(context, [translate(LocalizationKeys.mealSource_saved, args: {
"status": _isNew ? LocalizationKeys.mealSource_new : ''
}), mealSource]);
}
void handleCancelAction() {
@ -158,7 +162,12 @@ class _MealSourceDetailScreenState extends State<MealSourceDetailScreen> {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(_isNew ? 'New Meal Source' : _mealSource!.value),
title: Text(
translate(LocalizationKeys.mealSource_saved, args: {
"status": _isNew ? LocalizationKeys.mealSource_new : LocalizationKeys.general_edit,
"name": _mealSource!.value,
})
),
),
drawer:
const Navigation(currentLocation: MealSourceDetailScreen.routeName),
@ -174,12 +183,12 @@ class _MealSourceDetailScreenState extends State<MealSourceDetailScreen> {
fields: [
TextFormField(
controller: _valueController,
decoration: const InputDecoration(
labelText: 'Name',
decoration: InputDecoration(
labelText: translate(LocalizationKeys.mealSource_fields_name),
),
validator: (value) {
if (value!.trim().isEmpty) {
return 'Empty name';
return translate(LocalizationKeys.mealSource_fields_validators_name);
}
return null;
},
@ -190,7 +199,7 @@ class _MealSourceDetailScreenState extends State<MealSourceDetailScreen> {
child: AutoCompleteDropdownButton<Accuracy>(
selectedItem: _defaultCarbsRatioAccuracy,
controller: _defaultCarbsRatioAccuracyController,
label: 'Default Carbs Ratio Accuracy',
label: translate(LocalizationKeys.mealSource_fields_defaultCarbsRatioAccuracy),
items: _carbsRatioAccuracies,
onChanged: (value) {
setState(() {
@ -233,7 +242,7 @@ class _MealSourceDetailScreenState extends State<MealSourceDetailScreen> {
child: AutoCompleteDropdownButton<Accuracy>(
selectedItem: _defaultPortionSizeAccuracy,
controller: _defaultPortionSizeAccuracyController,
label: 'Default Portion Size Accuracy',
label: translate(LocalizationKeys.mealSource_fields_defaultPortionSizeAccuracy),
items: _portionSizeAccuracies,
onChanged: (value) {
setState(() {
@ -278,7 +287,7 @@ class _MealSourceDetailScreenState extends State<MealSourceDetailScreen> {
child: AutoCompleteDropdownButton<MealCategory>(
selectedItem: _defaultMealCategory,
controller: _defaultMealCategoryController,
label: 'Default Meal Category',
label: translate(LocalizationKeys.mealSource_fields_defaultMealCategory),
items: _mealCategories,
onChanged: (value) {
setState(() {
@ -320,7 +329,7 @@ class _MealSourceDetailScreenState extends State<MealSourceDetailScreen> {
child: AutoCompleteDropdownButton<MealPortionType>(
selectedItem: _defaultMealPortionType,
controller: _defaultMealPortionTypeController,
label: 'Default Meal Portion Type',
label: translate(LocalizationKeys.mealSource_fields_defaultMealPortionType),
items: _mealPortionTypes,
onChanged: (value) {
setState(() {
@ -359,8 +368,8 @@ class _MealSourceDetailScreenState extends State<MealSourceDetailScreen> {
),
TextFormField(
controller: _notesController,
decoration: const InputDecoration(
labelText: 'Notes',
decoration: InputDecoration(
labelText: translate(LocalizationKeys.mealSource_fields_notes),
),
keyboardType: TextInputType.multiline,
minLines: 2,

View File

@ -1,9 +1,11 @@
import 'package:diameter/localization_keys.dart';
import 'package:diameter/utils/dialog_utils.dart';
import 'package:diameter/models/meal_source.dart';
import 'package:diameter/models/settings.dart';
import 'package:diameter/navigation.dart';
import 'package:diameter/screens/category/meal_source_detail.dart';
import 'package:flutter/material.dart';
import 'package:flutter_translate/flutter_translate.dart';
class MealSourceListScreen extends StatefulWidget {
static const String routeName = '/meal-sources';
@ -50,7 +52,7 @@ class _MealSourceListScreenState extends State<MealSourceListScreen> {
void onDelete(MealSource mealSource) {
MealSource.remove(mealSource.id);
reload(message: 'Meal Source deleted');
reload(message: translate(LocalizationKeys.mealSource_deleted));
}
void handleDeleteAction(MealSource mealSource) async {
@ -58,7 +60,7 @@ class _MealSourceListScreenState extends State<MealSourceListScreen> {
DialogUtils.showConfirmationDialog(
context: context,
onConfirm: () => onDelete(mealSource),
message: 'Are you sure you want to delete this Meal Source?',
message: translate(LocalizationKeys.mealSource_confirmDelete),
);
} else {
onDelete(mealSource);
@ -69,7 +71,7 @@ class _MealSourceListScreenState extends State<MealSourceListScreen> {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Meal Sources'),
title: Text(translate(LocalizationKeys.mealSource_title)),
actions: <Widget>[
IconButton(onPressed: reload, icon: const Icon(Icons.refresh))
],
@ -122,8 +124,8 @@ class _MealSourceListScreenState extends State<MealSourceListScreen> {
);
}
),
) : const Center(
child: Text('You have not created any Meal Sources yet!'),
) : Center(
child: Text(translate(LocalizationKeys.mealSource_empty)),
),
),
],

View File

@ -3,6 +3,7 @@ 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';
import 'package:diameter/localization_keys.dart';
import 'package:diameter/utils/dialog_utils.dart';
import 'package:diameter/components/forms/auto_complete_dropdown_button.dart';
import 'package:diameter/components/forms/form_wrapper.dart';
@ -15,6 +16,7 @@ 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';
import 'package:flutter_translate/flutter_translate.dart';
enum BolusType {
meal,
@ -453,7 +455,9 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
}
Navigator.pop(context,
['${_isNew ? 'New' : ''} Bolus Saved', logBolus, delayedBolus]);
[translate(LocalizationKeys.log_detail_tabs_bolus_saved, args: {
"status": _isNew ? LocalizationKeys.log_detail_tabs_bolus_new : ''
}), logBolus, delayedBolus]);
}
setState(() {
_isSaving = false;
@ -509,7 +513,9 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(_isNew ? 'New Bolus' : 'Edit Bolus'),
title: Text(translate(LocalizationKeys.log_detail_tabs_bolus_detail_title, args: {
"status": _isNew ? LocalizationKeys.log_detail_tabs_bolus_new : LocalizationKeys.general_edit
})),
),
drawer: const Navigation(currentLocation: LogBolusDetailScreen.routeName),
body: Scrollbar(
@ -544,7 +550,7 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
contentPadding: const EdgeInsets.only(
left: 10.0, right: 10.0, top: 10.0),
value: _setManually,
label: 'set manually',
label: translate(LocalizationKeys.log_detail_tabs_bolus_detail_fields_setManually),
onChanged: (value) {
setState(() {
_setManually = value;
@ -559,7 +565,7 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
children: [
Expanded(
child: RadioListTile(
title: const Text('for glucose'),
title: Text(translate(LocalizationKeys.log_detail_tabs_bolus_detail_fields_forGlucose)),
groupValue: _bolusType,
value: BolusType.glucose,
onChanged: (_) {
@ -571,7 +577,7 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
),
Expanded(
child: RadioListTile(
title: const Text('for meal'),
title: Text(translate(LocalizationKeys.log_detail_tabs_bolus_detail_fields_forMeal)),
groupValue: _bolusType,
value: BolusType.meal,
onChanged: (value) {
@ -599,8 +605,8 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
padding:
const EdgeInsets.only(right: 5.0),
child: NumberFormField(
label: 'Current',
suffix: 'mg/dl',
label: translate(LocalizationKeys.log_detail_tabs_bolus_detail_fields_current),
suffix: Settings.glucoseMeasurementSuffix,
controller:
_mgPerDlCurrentController,
onChanged: (_) => onChangeGlucose(),
@ -613,8 +619,8 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
padding: const EdgeInsets.symmetric(
horizontal: 5.0),
child: NumberFormField(
label: 'Target',
suffix: 'mg/dl',
label: translate(LocalizationKeys.log_detail_tabs_bolus_detail_fields_target),
suffix: Settings.glucoseMeasurementSuffix,
controller:
_mgPerDlTargetController,
onChanged: (_) => onChangeGlucose(),
@ -627,9 +633,9 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
padding:
const EdgeInsets.only(left: 5.0),
child: TextFormField(
decoration: const InputDecoration(
labelText: 'Correction',
suffixText: 'mg/dl',
decoration: InputDecoration(
labelText: translate(LocalizationKeys.log_detail_tabs_bolus_detail_fields_correction),
suffixText: Settings.glucoseMeasurementSuffix,
),
controller:
_mgPerDlCorrectionController,
@ -661,8 +667,8 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
padding: const EdgeInsets.only(
right: 5.0),
child: NumberFormField(
label: 'Current',
suffix: 'mmol/l',
label: translate(LocalizationKeys.log_detail_tabs_bolus_detail_fields_current),
suffix: Settings.glucoseMeasurementSuffix,
controller:
_mmolPerLCurrentController,
onChanged: (_) =>
@ -676,8 +682,8 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
padding: const EdgeInsets.symmetric(
horizontal: 5.0),
child: NumberFormField(
label: 'Target',
suffix: 'mmol/l',
label: translate(LocalizationKeys.log_detail_tabs_bolus_detail_fields_target),
suffix: Settings.glucoseMeasurementSuffix,
controller:
_mmolPerLTargetController,
onChanged: (_) =>
@ -691,9 +697,9 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
padding: const EdgeInsets.only(
left: 5.0),
child: TextFormField(
decoration: const InputDecoration(
labelText: 'Correction',
suffixText: 'mmol/l',
decoration: InputDecoration(
labelText: translate(LocalizationKeys.log_detail_tabs_bolus_detail_fields_correction),
suffixText: Settings.glucoseMeasurementSuffix,
),
controller:
_mmolPerLCorrectionController,
@ -713,7 +719,7 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
child: AutoCompleteDropdownButton<LogMeal>(
controller: _mealController,
selectedItem: _meal,
label: 'Meal',
label: translate(LocalizationKeys.log_detail_tabs_bolus_detail_fields_meal),
items: _logMeals,
onChanged: onSelectMeal,
),
@ -758,9 +764,9 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
children: [
Expanded(
child: TextFormField(
decoration: const InputDecoration(
labelText: 'Delayed Bolus Duration',
suffixText: ' min',
decoration: InputDecoration(
labelText: translate(LocalizationKeys.log_detail_tabs_bolus_detail_fields_delayedBolusDuration),
suffixText: translate(LocalizationKeys.general_suffixes_mins),
),
controller: _delayController,
onChanged: (value) => setState(() {}),
@ -789,8 +795,8 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
child: Padding(
padding: const EdgeInsets.only(right: 5.0),
child: NumberFormField(
label: 'Immediate Bolus',
suffix: ' U',
label: translate(LocalizationKeys.log_detail_tabs_bolus_detail_fields_immediateBolus),
suffix: translate(LocalizationKeys.general_suffixes_units),
controller: _immediateUnitsController,
max: double.tryParse(_unitsController.text),
step: Settings.insulinSteps,
@ -804,8 +810,8 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
child: Padding(
padding: const EdgeInsets.only(left: 5.0),
child: NumberFormField(
label: 'Delayed Bolus',
suffix: ' U',
label: translate(LocalizationKeys.log_detail_tabs_bolus_detail_fields_delayedBolus),
suffix: translate(LocalizationKeys.general_suffixes_units),
controller: _delayedUnitsController,
max: double.tryParse(_unitsController.text),
step: Settings.insulinSteps,

View File

@ -1,3 +1,4 @@
import 'package:diameter/localization_keys.dart';
import 'package:diameter/utils/dialog_utils.dart';
import 'package:diameter/models/log_bolus.dart';
import 'package:diameter/models/log_entry.dart';
@ -5,6 +6,7 @@ import 'package:diameter/models/settings.dart';
import 'package:diameter/screens/log/log_entry/log_bolus_detail.dart';
import 'package:diameter/screens/log/log_entry/log_meal_detail.dart';
import 'package:flutter/material.dart';
import 'package:flutter_translate/flutter_translate.dart';
class LogBolusListScreen extends StatefulWidget {
final LogEntry logEntry;
@ -61,7 +63,7 @@ class _LogBolusListScreenState extends State<LogBolusListScreen> {
void onDelete(LogBolus logBolus) {
LogBolus.remove(logBolus.id);
reload(message: 'Bolus deleted');
reload(message: translate(LocalizationKeys.log_detail_tabs_bolus_deleted));
}
void handleDeleteAction(LogBolus logBolus) async {
@ -69,7 +71,8 @@ class _LogBolusListScreenState extends State<LogBolusListScreen> {
DialogUtils.showConfirmationDialog(
context: context,
onConfirm: () => onDelete(logBolus),
message: 'Are you sure you want to delete this Bolus?',
message:
translate(LocalizationKeys.log_detail_tabs_bolus_confirmDelete),
);
} else {
onDelete(logBolus);
@ -100,9 +103,14 @@ class _LogBolusListScreenState extends State<LogBolusListScreen> {
itemCount: widget.logBoli.length,
itemBuilder: (context, index) {
final bolus = widget.logBoli[index];
String titleText = '${bolus.units} U ${(bolus.delay ?? 0) != 0
? ' (delayed by ${bolus.delay} min)'
: ''}';
String titleText =
'${bolus.units} ${translate(LocalizationKeys.general_suffixes_units)} ${(bolus.delay ?? 0) != 0 ?
translate(
LocalizationKeys.log_detail_tabs_bolus_delayedBy,
args: {
"delay": '${bolus.delay} ${translate(LocalizationKeys.general_suffixes_mins)}'
}
) : ''}';
return Card(
child: ListTile(
onTap: () => handleEditAction(bolus),
@ -110,8 +118,8 @@ class _LogBolusListScreenState extends State<LogBolusListScreen> {
titleText.toUpperCase(),
style: Theme.of(context).textTheme.subtitle2,
),
subtitle: Text(bolus.carbs != null ?
'for ${(bolus.meal.target ?? '').toString()} (${bolus.carbs}${Settings.nutritionMeasurementSuffix} carbs)'
subtitle: Text(bolus.carbs != null
? '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,
@ -138,9 +146,9 @@ class _LogBolusListScreenState extends State<LogBolusListScreen> {
},
),
)
: const Center(
child: Text(
'You have not added any Boli to this Log Entry yet!'),
: Center(
child:
Text(translate(LocalizationKeys.log_detail_tabs_bolus_empty)),
);
}
}

View File

@ -2,6 +2,7 @@ import 'package:diameter/components/detail.dart';
import 'package:diameter/components/forms/date_time_form_field.dart';
import 'package:diameter/components/forms/number_form_field.dart';
import 'package:diameter/components/forms/time_of_day_form_field.dart';
import 'package:diameter/localization_keys.dart';
import 'package:diameter/utils/dialog_utils.dart';
import 'package:diameter/components/forms/form_wrapper.dart';
import 'package:diameter/models/log_bolus.dart';
@ -18,6 +19,8 @@ import 'package:diameter/utils/utils.dart';
import 'package:flutter/material.dart';
import 'dart:math' as math;
import 'package:flutter_translate/flutter_translate.dart';
class LogEntryScreen extends StatefulWidget {
static const String routeName = '/log-entry';
final int id;
@ -192,7 +195,9 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
if (close) {
Navigator.pop(
context, ['${_isNew ? 'New' : ''} Log Entry Saved', logEntry]);
context, [translate(LocalizationKeys.log_saved, args: {
"status": _isNew ? LocalizationKeys.log_new : ''
}), logEntry]);
} else {
if (_isNew) {
Navigator.push(
@ -202,7 +207,7 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
),
).then((result) => Navigator.pop(context, result));
} else {
reload(message: 'Log Entry Saved');
reload(message: translate(LocalizationKeys.log_saved));
}
}
}
@ -306,7 +311,7 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
padding: const EdgeInsets.only(right: 5),
child: DateTimeFormField(
date: _time,
label: 'Date',
label: translate(LocalizationKeys.log_fields_date),
controller: _dateController,
onChanged: (newTime) {
if (newTime != null) {
@ -329,7 +334,7 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
padding: const EdgeInsets.only(left: 5),
child: TimeOfDayFormField(
time: TimeOfDay.fromDateTime(_time),
label: 'Time',
label: translate(LocalizationKeys.log_fields_time),
controller: _timeController,
onChanged: (newTime) {
if (newTime != null) {
@ -363,8 +368,8 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
? 2
: 1,
child: NumberFormField(
label: 'Blood Glucose',
suffix: 'mg/dl',
label: translate(LocalizationKeys.log_fields_glucose),
suffix: Settings.glucoseMeasurementSuffix,
readOnly: Settings.glucoseMeasurement ==
GlucoseMeasurement.mmolPerL,
showSteppers:
@ -388,8 +393,8 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
? 2
: 1,
child: NumberFormField(
label: 'Blood Glucose',
suffix: 'mmol/l',
label: translate(LocalizationKeys.log_fields_glucose),
suffix: Settings.glucoseMeasurementSuffix,
readOnly: Settings.glucoseMeasurement ==
GlucoseMeasurement.mgPerDl,
showSteppers:
@ -421,8 +426,8 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
),
TextFormField(
controller: _notesController,
decoration: const InputDecoration(
labelText: 'Notes',
decoration: InputDecoration(
labelText: translate(LocalizationKeys.log_fields_notes),
),
keyboardType: TextInputType.multiline,
minLines: 2,
@ -444,14 +449,16 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
return Scaffold(
appBar: AppBar(
title: Text(_isNew ? 'New Log Entry' : 'Edit Log Entry'),
title: Text(translate(LocalizationKeys.log_detail_title, args: {
"status": _isNew ? LocalizationKeys.log_new : LocalizationKeys.general_edit
})),
bottom: _isNew
? PreferredSize(child: Container(), preferredSize: Size.zero)
: const TabBar(
: TabBar(
tabs: [
Tab(text: 'GENERAL'),
Tab(text: 'MEALS'),
Tab(text: 'BOLI'),
Tab(text: translate(LocalizationKeys.log_detail_tabs_general).toUpperCase()),
Tab(text: translate(LocalizationKeys.log_detail_tabs_meal_title).toUpperCase()),
Tab(text: translate(LocalizationKeys.log_detail_tabs_bolus_title).toUpperCase()),
],
),
actions: appBarActions,

View File

@ -1,6 +1,7 @@
import 'package:diameter/components/detail.dart';
import 'package:diameter/components/forms/boolean_form_field.dart';
import 'package:diameter/components/forms/number_form_field.dart';
import 'package:diameter/localization_keys.dart';
import 'package:diameter/utils/dialog_utils.dart';
import 'package:diameter/components/forms/auto_complete_dropdown_button.dart';
import 'package:diameter/components/forms/form_wrapper.dart';
@ -19,6 +20,7 @@ import 'package:diameter/screens/category/meal_source_detail.dart';
import 'package:diameter/screens/meal/meal_detail.dart';
import 'package:diameter/utils/utils.dart';
import 'package:flutter/material.dart';
import 'package:flutter_translate/flutter_translate.dart';
class LogMealDetailScreen extends StatefulWidget {
static const String routeName = '/log-meal';
@ -230,7 +232,9 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
logMeal.carbsRatioAccuracy.target = _carbsRatioAccuracy;
LogMeal.put(logMeal);
Navigator.pop(context, ['${_isNew ? 'New' : ''} Meal Saved', logMeal]);
Navigator.pop(context, [translate(LocalizationKeys.log_detail_tabs_meal_saved, args: {
"status": _isNew ? translate(LocalizationKeys.log_detail_tabs_bolus_new) : ''
}), logMeal]);
}
setState(() {
_isSaving = false;
@ -405,7 +409,9 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(_isNew ? 'New Meal for Log Entry' : _logMeal!.value),
title: Text(translate(LocalizationKeys.log_detail_tabs_meal_detail_title, args: {
"status": _isNew ? translate(LocalizationKeys.log_detail_tabs_bolus_new) : LocalizationKeys.general_edit
})),
),
drawer: const Navigation(currentLocation: LogMealDetailScreen.routeName),
body: Scrollbar(
@ -424,7 +430,7 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
child: AutoCompleteDropdownButton<Meal>(
controller: _mealController,
selectedItem: _meal,
label: 'Meal',
label: translate(LocalizationKeys.log_detail_tabs_meal_detail_fields_meal),
items: _meals,
onChanged: onSelectMeal,
),
@ -449,12 +455,12 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
),
TextFormField(
controller: _valueController,
decoration: const InputDecoration(
labelText: 'Name',
decoration: InputDecoration(
labelText: translate(LocalizationKeys.log_detail_tabs_meal_detail_fields_name),
),
validator: (value) {
if (value!.trim().isEmpty) {
return 'Empty name';
return translate(LocalizationKeys.log_detail_tabs_meal_detail_fields_validators_name);
}
return null;
},
@ -465,7 +471,7 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
flex: 10,
child: NumberFormField(
controller: _amountController,
label: 'Amount',
label: translate(LocalizationKeys.log_detail_tabs_meal_detail_fields_amount),
suffix: _mealPortionType?.value,
onChanged: updateAmount,
),
@ -524,7 +530,7 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
children: [
Expanded(
child: NumberFormField(
label: 'Portion size',
label: translate(LocalizationKeys.log_detail_tabs_meal_detail_fields_portionSize),
suffix: Settings.nutritionMeasurementSuffix,
controller: _portionSizeController,
showSteppers: false,
@ -548,7 +554,7 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
const SizedBox(width: 10),
Expanded(
child: NumberFormField(
label: 'Carbs ratio',
label: translate(LocalizationKeys.log_detail_tabs_meal_detail_fields_carbsRatio),
suffix: '%',
controller: _carbsRatioController,
showSteppers: false,
@ -570,7 +576,7 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
const SizedBox(width: 10),
Expanded(
child: NumberFormField(
label: 'Total carbs',
label: translate(LocalizationKeys.log_detail_tabs_meal_detail_fields_totalCarbs),
suffix: Settings.nutritionMeasurementSuffix,
controller: _totalCarbsController,
showSteppers: false,
@ -595,7 +601,7 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
),
BooleanFormField(
value: _setManually,
label: 'set carbs ratio manually',
label: translate(LocalizationKeys.log_detail_tabs_meal_detail_fields_setManually),
onChanged: (value) {
setState(() {
_setManually = value;
@ -605,8 +611,8 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
),
TextFormField(
controller: _notesController,
decoration: const InputDecoration(
labelText: 'Notes',
decoration: InputDecoration(
labelText: translate(LocalizationKeys.log_detail_tabs_meal_detail_fields_notes),
),
keyboardType: TextInputType.multiline,
minLines: 2,
@ -621,7 +627,7 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
mainAxisSize: MainAxisSize.max,
children: [
Text(
'ADDITIONAL FIELDS',
translate(LocalizationKeys.log_detail_tabs_meal_detail_additionalFields).toUpperCase(),
style: Theme.of(context).textTheme.subtitle2,
),
const Spacer(),
@ -642,7 +648,7 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
AutoCompleteDropdownButton<MealSource>(
controller: _mealSourceController,
selectedItem: _mealSource,
label: 'Meal Source',
label: translate(LocalizationKeys.log_detail_tabs_meal_detail_fields_mealSource),
items: _mealSources,
onChanged: updateMealSource,
),
@ -677,7 +683,7 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
AutoCompleteDropdownButton<MealCategory>(
controller: _mealCategoryController,
selectedItem: _mealCategory,
label: 'Meal Category',
label: translate(LocalizationKeys.log_detail_tabs_meal_detail_fields_mealCategory),
items: _mealCategories,
onChanged: updateMealCategory,
),
@ -712,7 +718,7 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
AutoCompleteDropdownButton<MealPortionType>(
controller: _mealPortionTypeController,
selectedItem: _mealPortionType,
label: 'Meal Portion Type',
label: translate(LocalizationKeys.log_detail_tabs_meal_detail_fields_mealPortionType),
items: _mealPortionTypes,
onChanged: updateMealPortionType,
),
@ -747,7 +753,7 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
AutoCompleteDropdownButton<Accuracy>(
controller: _portionSizeAccuracyController,
selectedItem: _portionSizeAccuracy,
label: 'Portion Size Accuracy',
label: translate(LocalizationKeys.log_detail_tabs_meal_detail_fields_portionSizeAccuracy),
items: _portionSizeAccuracies,
onChanged: updatePortionSizeAccuracy,
),
@ -783,7 +789,7 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
AutoCompleteDropdownButton<Accuracy>(
controller: _carbsRatioAccuracyController,
selectedItem: _carbsRatioAccuracy,
label: 'Carbs Ratio Accuracy',
label: translate(LocalizationKeys.log_detail_tabs_meal_detail_fields_carbsRatioAccuracy),
items: _carbsRatioAccuracies,
onChanged: updateCarbsRatioAccuracy,
),

View File

@ -1,9 +1,11 @@
import 'package:diameter/localization_keys.dart';
import 'package:diameter/utils/dialog_utils.dart';
import 'package:diameter/models/log_entry.dart';
import 'package:diameter/models/log_meal.dart';
import 'package:diameter/models/settings.dart';
import 'package:diameter/screens/log/log_entry/log_meal_detail.dart';
import 'package:flutter/material.dart';
import 'package:flutter_translate/flutter_translate.dart';
class LogMealListScreen extends StatefulWidget {
final LogEntry logEntry;
@ -57,7 +59,7 @@ class _LogMealListScreenState extends State<LogMealListScreen> {
void onDelete(LogMeal logMeal) {
LogMeal.remove(logMeal.id);
reload(message: 'Meal deleted');
reload(message: translate(LocalizationKeys.log_detail_tabs_meal_deleted));
}
void handleDeleteAction(LogMeal meal) async {
@ -65,7 +67,7 @@ class _LogMealListScreenState extends State<LogMealListScreen> {
DialogUtils.showConfirmationDialog(
context: context,
onConfirm: () => onDelete(meal),
message: 'Are you sure you want to delete this Meal?',
message: translate(LocalizationKeys.log_detail_tabs_meal_confirmDelete),
);
} else {
onDelete(meal);
@ -99,7 +101,7 @@ class _LogMealListScreenState extends State<LogMealListScreen> {
? [
Text(meal.totalCarbs!.toStringAsPrecision(3)),
Text(
'${Settings.nutritionMeasurementSuffix} carbs',
'${Settings.nutritionMeasurementSuffix} ${translate(LocalizationKeys.general_suffixes_carbs)}',
textScaleFactor: 0.75),
]
: [],
@ -119,9 +121,9 @@ class _LogMealListScreenState extends State<LogMealListScreen> {
},
),
)
: const Center(
: Center(
child: Text(
'You have not added any Meals to this Log Entry yet!'),
translate(LocalizationKeys.log_detail_tabs_meal_empty)),
);
}
}

View File

@ -2,6 +2,7 @@ import 'package:diameter/components/detail.dart';
import 'package:diameter/components/forms/boolean_form_field.dart';
import 'package:diameter/components/forms/date_time_form_field.dart';
import 'package:diameter/components/forms/time_of_day_form_field.dart';
import 'package:diameter/localization_keys.dart';
import 'package:diameter/utils/dialog_utils.dart';
import 'package:diameter/components/forms/auto_complete_dropdown_button.dart';
import 'package:diameter/components/forms/form_wrapper.dart';
@ -15,6 +16,7 @@ import 'package:diameter/screens/basal/basal_profile_detail.dart';
import 'package:diameter/screens/bolus/bolus_profile_detail.dart';
import 'package:diameter/utils/date_time_utils.dart';
import 'package:flutter/material.dart';
import 'package:flutter_translate/flutter_translate.dart';
class LogEventDetailScreen extends StatefulWidget {
static const String routeName = '/log-event';
@ -194,20 +196,19 @@ class _LogEventDetailScreenState extends State<LogEventDetailScreen> {
context: context,
builder: (BuildContext context) {
return AlertDialog(
content: const Text(
'An Event of this type is already active within the set time frame. What would you like to do?'),
content: Text(translate(LocalizationKeys.event_warnings_duplicate)),
actions: [
TextButton(
onPressed: () => Navigator.pop(context, 'DISCARD'),
child: const Text('DISCARD'),
child: Text(translate(LocalizationKeys.general_discard).toUpperCase()),
),
TextButton(
onPressed: () => Navigator.pop(context, 'EDIT'),
child: const Text('KEEP EDITING'),
child: Text(translate(LocalizationKeys.general_keepEditing).toUpperCase()),
),
ElevatedButton(
onPressed: () => Navigator.pop(context, 'SAVE'),
child: const Text('SAVE'),
child: Text(translate(LocalizationKeys.general_save).toUpperCase()),
)
],
);
@ -236,7 +237,9 @@ class _LogEventDetailScreenState extends State<LogEventDetailScreen> {
event.basalProfile.target = _basalProfile;
event.bolusProfile.target = _bolusProfile;
LogEvent.put(event);
Navigator.pop(context, ['${_isNew ? 'New' : ''} Event Saved', event]);
Navigator.pop(context, [translate(LocalizationKeys.event_saved, args: {
"status": _isNew ? LocalizationKeys.event_new : "",
}), event]);
}
void handleSaveAction() async {
@ -276,7 +279,10 @@ class _LogEventDetailScreenState extends State<LogEventDetailScreen> {
final now = DateTime.now();
return Scaffold(
appBar: AppBar(
title: Text(_isNew ? 'New Event' : 'Edit Event'),
title: Text(translate(LocalizationKeys.event_saved, args: {
"status": _isNew ? LocalizationKeys.event_new : LocalizationKeys.general_edit,
"name": _isNew ? '' : _logEvent?.eventType.target?.value
})),
),
drawer: const Navigation(currentLocation: LogEventDetailScreen.routeName),
body: Scrollbar(
@ -292,7 +298,7 @@ class _LogEventDetailScreenState extends State<LogEventDetailScreen> {
AutoCompleteDropdownButton<LogEventType>(
controller: _eventTypeController,
selectedItem: _eventType,
label: 'Event Type',
label: translate(LocalizationKeys.event_fields_eventType),
items: _logEventTypes,
onChanged: onSelectEventType,
),
@ -303,7 +309,7 @@ class _LogEventDetailScreenState extends State<LogEventDetailScreen> {
padding: const EdgeInsets.only(right: 5),
child: DateTimeFormField(
date: _time,
label: _hasEndTime ? 'Start Date' : 'Date',
label: translate(_hasEndTime ? LocalizationKeys.event_fields_startDate : LocalizationKeys.event_fields_date),
controller: _dateController,
onChanged: (newTime) {
if (newTime != null) {
@ -322,7 +328,7 @@ class _LogEventDetailScreenState extends State<LogEventDetailScreen> {
padding: const EdgeInsets.only(left: 5),
child: TimeOfDayFormField(
time: TimeOfDay.fromDateTime(_time),
label: _hasEndTime ? 'Start Time' : 'Time',
label: translate(_hasEndTime ? LocalizationKeys.event_fields_startTime : LocalizationKeys.event_fields_time),
controller: _timeController,
onChanged: (newTime) {
if (newTime != null) {
@ -345,7 +351,7 @@ class _LogEventDetailScreenState extends State<LogEventDetailScreen> {
_hasEndTime = value;
});
},
label: 'has end time',
label: translate(LocalizationKeys.event_fields_hasEndTime),
),
Column(
children: _hasEndTime
@ -357,7 +363,7 @@ class _LogEventDetailScreenState extends State<LogEventDetailScreen> {
padding: const EdgeInsets.only(right: 5),
child: DateTimeFormField(
date: _endTime ?? now,
label: 'End Date',
label: translate(LocalizationKeys.event_fields_endDate),
controller: _endDateController,
onChanged: (newTime) {
if (newTime != null) {
@ -381,7 +387,7 @@ class _LogEventDetailScreenState extends State<LogEventDetailScreen> {
child: TimeOfDayFormField(
time: TimeOfDay.fromDateTime(
_endTime ?? now),
label: 'End Time',
label: translate(LocalizationKeys.event_fields_endTime),
controller: _endTimeController,
onChanged: (newTime) {
if (newTime != null) {
@ -408,8 +414,8 @@ class _LogEventDetailScreenState extends State<LogEventDetailScreen> {
keyboardType:
const TextInputType.numberWithOptions(),
decoration: InputDecoration(
labelText: 'Default Reminder Duration',
suffixText: ' min',
labelText: translate(LocalizationKeys.event_fields_reminderDuration),
suffixText: translate(LocalizationKeys.general_suffixes_mins),
enabled: _hasEndTime,
),
),
@ -421,7 +427,7 @@ class _LogEventDetailScreenState extends State<LogEventDetailScreen> {
BolusProfile>(
controller: _bolusProfileController,
selectedItem: _bolusProfile,
label: 'Bolus Profile',
label: translate(LocalizationKeys.event_fields_bolusProfile),
items: _bolusProfiles,
onChanged: updateBolusProfile,
),
@ -457,7 +463,7 @@ class _LogEventDetailScreenState extends State<LogEventDetailScreen> {
BasalProfile>(
controller: _basalProfileController,
selectedItem: _basalProfile,
label: 'Basal Profile',
label: translate(LocalizationKeys.event_fields_basalProfile),
items: _basalProfiles,
onChanged: updateBasalProfile,
),
@ -489,8 +495,8 @@ class _LogEventDetailScreenState extends State<LogEventDetailScreen> {
: []),
TextFormField(
controller: _notesController,
decoration: const InputDecoration(
labelText: 'Notes',
decoration: InputDecoration(
labelText: translate(LocalizationKeys.event_fields_notes),
),
keyboardType: TextInputType.multiline,
minLines: 2,

View File

@ -1,3 +1,4 @@
import 'package:diameter/localization_keys.dart';
import 'package:diameter/utils/dialog_utils.dart';
import 'package:diameter/models/log_event.dart';
import 'package:diameter/models/settings.dart';
@ -5,6 +6,7 @@ import 'package:diameter/screens/log/log_event/log_event_detail.dart';
import 'package:diameter/utils/date_time_utils.dart';
import 'package:flutter/material.dart';
import 'package:diameter/navigation.dart';
import 'package:flutter_translate/flutter_translate.dart';
class LogEventListScreen extends StatefulWidget {
static const String routeName = '/log-events';
@ -88,7 +90,7 @@ class _LogEventListScreenState extends State<LogEventListScreen> {
void onDelete(LogEvent logEvent) {
LogEvent.remove(logEvent.id);
reload(message: 'Event deleted');
reload(message: translate(LocalizationKeys.event_deleted));
}
void handleDeleteAction(LogEvent logEvent) async {
@ -96,7 +98,7 @@ class _LogEventListScreenState extends State<LogEventListScreen> {
DialogUtils.showConfirmationDialog(
context: context,
onConfirm: () => onDelete(logEvent),
message: 'Are you sure you want to delete this Event?',
message: translate(LocalizationKeys.event_confirmDelete),
);
} else {
onDelete(logEvent);
@ -106,7 +108,7 @@ class _LogEventListScreenState extends State<LogEventListScreen> {
void onStop(LogEvent event) async {
event.endTime = DateTime.now();
LogEvent.put(event);
reload(message: 'Event ended');
reload(message: translate(LocalizationKeys.event_ended));
}
void handleStopAction(LogEvent event) async {
@ -114,8 +116,8 @@ class _LogEventListScreenState extends State<LogEventListScreen> {
DialogUtils.showConfirmationDialog(
context: context,
onConfirm: () => onStop(event),
message: 'Are you sure you want to end this Event?',
confirmationLabel: 'END EVENT',
message: translate(LocalizationKeys.event_confirmEnd),
confirmationLabel: translate(LocalizationKeys.event_end),
);
} else {
onStop(event);
@ -136,7 +138,7 @@ class _LogEventListScreenState extends State<LogEventListScreen> {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Log Events'),
title: Text(translate(LocalizationKeys.event_title)),
actions: <Widget>[
IconButton(
onPressed: () => onChangeDate(DateTime.now()),
@ -159,7 +161,7 @@ class _LogEventListScreenState extends State<LogEventListScreen> {
children: [
Expanded(
child: Text(
'ACTIVE EVENTS',
translate(LocalizationKeys.event_titleActive).toUpperCase(),
style: Theme.of(context).textTheme.subtitle2,
textAlign: TextAlign.center,
),
@ -235,8 +237,8 @@ class _LogEventListScreenState extends State<LogEventListScreen> {
);
},
)
: const Center(
child: Text('There are no Active Events!'),
: Center(
child: Text(translate(LocalizationKeys.event_emptyActive)),
),
const Padding(
padding: EdgeInsets.all(10.0),
@ -351,8 +353,8 @@ class _LogEventListScreenState extends State<LogEventListScreen> {
);
},
))
: const Center(
child: Text('There are no Events for that date!'),
: Center(
child: Text(translate(LocalizationKeys.event_empty)),
),
),
],

View File

@ -1,304 +0,0 @@
import 'package:diameter/components/detail.dart';
import 'package:diameter/components/forms/boolean_form_field.dart';
import 'package:diameter/components/forms/duration_form_field.dart';
import 'package:diameter/utils/dialog_utils.dart';
import 'package:diameter/components/forms/auto_complete_dropdown_button.dart';
import 'package:diameter/components/forms/form_wrapper.dart';
import 'package:diameter/models/basal_profile.dart';
import 'package:diameter/models/bolus_profile.dart';
import 'package:diameter/models/log_event_type.dart';
import 'package:diameter/models/settings.dart';
import 'package:diameter/navigation.dart';
import 'package:diameter/screens/basal/basal_profile_detail.dart';
import 'package:diameter/screens/bolus/bolus_profile_detail.dart';
import 'package:flutter/material.dart';
class EventTypeDetailScreen extends StatefulWidget {
static const String routeName = '/log-event-type';
final int id;
const EventTypeDetailScreen({Key? key, this.id = 0}) : super(key: key);
@override
_EventTypeDetailScreenState createState() => _EventTypeDetailScreenState();
}
class _EventTypeDetailScreenState extends State<EventTypeDetailScreen> {
LogEventType? _logEventType;
bool _isNew = true;
bool _isSaving = false;
List<BolusProfile> _bolusProfiles = [];
List<BasalProfile> _basalProfiles = [];
final GlobalKey<FormState> _logEventTypeForm = GlobalKey<FormState>();
final ScrollController _scrollController = ScrollController();
final _valueController = TextEditingController(text: '');
final _notesController = TextEditingController(text: '');
bool _hasEndTime = false;
int _defaultReminderDuration = 0;
BolusProfile? _bolusProfile;
BasalProfile? _basalProfile;
final _bolusProfileController = TextEditingController(text: '');
final _basalProfileController = TextEditingController(text: '');
@override
void initState() {
super.initState();
reload();
_bolusProfiles = BolusProfile.getAll();
_basalProfiles = BasalProfile.getAll();
if (_logEventType != null) {
_valueController.text = _logEventType!.value;
_defaultReminderDuration =
_logEventType!.defaultReminderDuration ?? 0;
_hasEndTime = _logEventType!.hasEndTime;
_notesController.text = _logEventType!.notes ?? '';
_basalProfile = _logEventType!.basalProfile.target;
_basalProfileController.text = (_basalProfile ?? '').toString();
_bolusProfile = _logEventType!.bolusProfile.target;
_bolusProfileController.text = (_bolusProfile ?? '').toString();
}
}
@override
void dispose() {
_scrollController.dispose();
_valueController.dispose();
_notesController.dispose();
_bolusProfileController.dispose();
_basalProfileController.dispose();
super.dispose();
}
void reload({String? message}) {
if (widget.id != 0) {
setState(() {
_logEventType = LogEventType.get(widget.id);
});
}
_isNew = _logEventType == null;
setState(() {
if (message != null) {
var snackBar = SnackBar(
content: Text(message),
duration: const Duration(seconds: 2),
);
ScaffoldMessenger.of(context)
..removeCurrentSnackBar()
..showSnackBar(snackBar);
}
});
}
void updateBasalProfile(BasalProfile? value) {
setState(() {
_basalProfile = value;
_basalProfileController.text = (_basalProfile ?? '').toString();
});
}
void updateBolusProfile(BolusProfile? value) {
setState(() {
_bolusProfile = value;
_bolusProfileController.text = (_bolusProfile ?? '').toString();
});
}
void handleSaveAction() async {
setState(() {
_isSaving = true;
});
if (_logEventTypeForm.currentState!.validate()) {
LogEventType eventType = LogEventType(
id: widget.id,
value: _valueController.text,
notes: _notesController.text,
defaultReminderDuration: _defaultReminderDuration,
hasEndTime: _hasEndTime,
);
eventType.basalProfile.target = _basalProfile;
eventType.bolusProfile.target = _bolusProfile;
LogEventType.put(eventType);
Navigator.pop(
context, ['${_isNew ? 'New' : ''} Log Event Type Saved', eventType]);
}
setState(() {
_isSaving = false;
});
}
void handleCancelAction() {
bool isNew = _logEventType == null;
if (Settings.get().showConfirmationDialogOnCancel &&
((isNew &&
(_valueController.text != '' ||
_defaultReminderDuration != 0 ||
_notesController.text != '' ||
_hasEndTime)) ||
(!isNew &&
(_valueController.text != _logEventType!.value ||
_defaultReminderDuration !=
_logEventType!.defaultReminderDuration ||
_notesController.text != (_logEventType!.notes ?? '') ||
_hasEndTime != _logEventType!.hasEndTime)))) {
DialogUtils.showCancelConfirmationDialog(
context: context,
isNew: isNew,
onSave: handleSaveAction,
);
} else {
Navigator.pop(context);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(_isNew ? 'New Log Event Type' : _logEventType!.value),
),
drawer:
const Navigation(currentLocation: EventTypeDetailScreen.routeName),
body: Scrollbar(
controller: _scrollController,
child: SingleChildScrollView(
controller: _scrollController,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
FormWrapper(formState: _logEventTypeForm, fields: [
TextFormField(
controller: _valueController,
decoration: const InputDecoration(
labelText: 'Name',
),
validator: (value) {
if (value!.trim().isEmpty) {
return 'Empty name';
}
return null;
},
),
BooleanFormField(
value: _hasEndTime,
label: 'has end time',
onChanged: (value) {
setState(() {
_hasEndTime = value;
});
},
),
Column(
children: _hasEndTime
? [
Padding(
padding: const EdgeInsets.only(bottom: 10.0),
child: DurationFormField(
minutes: _defaultReminderDuration,
label: 'Default Reminder Duration',
onChanged: (value) => _defaultReminderDuration = value ?? 0,
showSteppers: true,
),
),
Padding(
padding: const EdgeInsets.only(bottom: 10.0),
child: Row(
children: [
Expanded(
child: AutoCompleteDropdownButton<
BolusProfile>(
selectedItem: _bolusProfile,
controller: _bolusProfileController,
label: 'Bolus Profile',
items: _bolusProfiles,
onChanged: updateBolusProfile,
),
),
IconButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => _bolusProfile ==
null
? const BolusProfileDetailScreen()
: BolusProfileDetailScreen(
id: _bolusProfile!.id),
),
).then((result) {
setState(() {
updateBolusProfile(result?[1]);
});
reload(message: result?[0]);
});
},
icon: Icon(_bolusProfile == null
? Icons.add
: Icons.edit),
),
],
),
),
Row(
children: [
Expanded(
child:
AutoCompleteDropdownButton<BasalProfile>(
controller: _basalProfileController,
selectedItem: _basalProfile,
label: 'Basal Profile',
items: _basalProfiles,
onChanged: updateBasalProfile,
),
),
IconButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => _basalProfile ==
null
? const BasalProfileDetailScreen()
: BasalProfileDetailScreen(
id: _basalProfile!.id),
),
).then((result) {
updateBasalProfile(result?[1]);
reload(message: result?[0]);
});
},
icon: Icon(_basalProfile == null
? Icons.add
: Icons.edit),
),
],
),
]
: []),
TextFormField(
controller: _notesController,
decoration: const InputDecoration(
labelText: 'Notes',
),
keyboardType: TextInputType.multiline,
minLines: 2,
maxLines: 5,
),
]),
],
),
),
),
bottomNavigationBar: DetailBottomRow(
onCancel: handleCancelAction,
onAction: _isSaving ? null : handleSaveAction,
),
);
}
}

View File

@ -1,123 +0,0 @@
import 'package:diameter/models/log_event_type.dart';
import 'package:diameter/navigation.dart';
import 'package:diameter/screens/log/log_event/log_event_type_detail.dart';
import 'package:flutter/material.dart';
class LogEventTypeListScreen extends StatefulWidget {
static const String routeName = '/log-event-types';
const LogEventTypeListScreen({Key? key}) : super(key: key);
@override
_LogEventTypeListScreenState createState() => _LogEventTypeListScreenState();
}
class _LogEventTypeListScreenState extends State<LogEventTypeListScreen> {
List<LogEventType> _logEventTypes = [];
final ScrollController _scrollController = ScrollController();
@override
void initState() {
super.initState();
reload();
}
@override
void dispose() {
_scrollController.dispose();
super.dispose();
}
void reload({String? message}) {
setState(() {
_logEventTypes = LogEventType.getAll();
});
setState(() {
if (message != null) {
var snackBar = SnackBar(
content: Text(message),
duration: const Duration(seconds: 2),
);
ScaffoldMessenger.of(context)
..removeCurrentSnackBar()
..showSnackBar(snackBar);
}
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Log Event Types'), actions: <Widget>[
IconButton(onPressed: reload, icon: const Icon(Icons.refresh))
]),
drawer:
const Navigation(currentLocation: LogEventTypeListScreen.routeName),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Expanded(
child: _logEventTypes.isNotEmpty
? Scrollbar(
controller: _scrollController,
child: ListView.builder(
controller: _scrollController,
padding: const EdgeInsets.all(10.0),
itemCount: _logEventTypes.length,
itemBuilder: (context, index) {
final logEventType = _logEventTypes[index];
return Card(
child: ListTile(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => EventTypeDetailScreen(
id: logEventType.id),
),
).then((result) => reload(message: result?[0]));
},
title: Text(
logEventType.value.toUpperCase(),
style: Theme.of(context).textTheme.subtitle2,
),
subtitle: Text(logEventType.notes ?? ''),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
onPressed: () async {
LogEventType.remove(logEventType.id);
reload(message: 'Log Event Type deleted');
},
icon: const Icon(Icons.delete,
color: Colors.blue),
)
],
),
),
);
},
),
)
: const Center(
child:
Text('You have not created any Log Event Types yet!'),
),
),
],
),
floatingActionButton: FloatingActionButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const EventTypeDetailScreen(),
),
).then((result) => reload(message: result?[0]));
},
child: const Icon(Icons.add),
),
);
}
}

View File

@ -0,0 +1,230 @@
import 'package:diameter/components/forms/auto_complete_dropdown_button.dart';
import 'package:diameter/components/forms/date_time_form_field.dart';
import 'package:diameter/components/forms/number_form_field.dart';
import 'package:diameter/localization_keys.dart';
import 'package:diameter/models/meal.dart';
import 'package:diameter/models/settings.dart';
import 'package:diameter/utils/date_time_utils.dart';
import 'package:flutter/material.dart';
import 'package:flutter_translate/flutter_translate.dart';
class LogFilterDialog extends StatefulWidget {
static const String routeName = '/log/filter';
final DateTime? date;
final void Function(
{DateTime? startDate,
DateTime? endDate,
int? minMgPerDl,
int? maxMgPerDl,
double? minMmolPerL,
double? maxMmolPerL,
int? mealId,
String? mealName,
String? note}) onApplyFilter;
const LogFilterDialog({Key? key, this.date, required this.onApplyFilter})
: super(key: key);
@override
_LogFilterDialogState createState() => _LogFilterDialogState();
}
class _LogFilterDialogState extends State<LogFilterDialog> {
final ScrollController _scrollController = ScrollController();
late DateTime filterStartDate;
late DateTime filterEndDate;
Meal? meal;
List<Meal> _meals = [];
TextEditingController filterStartDateController =
TextEditingController(text: '');
TextEditingController filterEndDateController =
TextEditingController(text: '');
TextEditingController mealController = TextEditingController(text: '');
TextEditingController minMgPerDlController = TextEditingController(text: '');
TextEditingController maxMgPerDlController = TextEditingController(text: '');
TextEditingController minMmolPerLController = TextEditingController(text: '');
TextEditingController maxMmolPerLController = TextEditingController(text: '');
TextEditingController mealNameController = TextEditingController(text: '');
TextEditingController noteController = TextEditingController(text: '');
@override
void dispose() {
_scrollController.dispose();
super.dispose();
}
@override
void initState() {
super.initState();
filterStartDate = widget.date ?? DateTimeUtils.today();
filterEndDate = DateTimeUtils.today();
filterStartDateController.text = DateTimeUtils.displayDate(filterStartDate);
filterEndDateController.text = DateTimeUtils.displayDate(filterEndDate);
_meals = Meal.getAll();
}
@override
Widget build(BuildContext context) {
return AlertDialog(
content: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Row(
children: [
Expanded(
child: DateTimeFormField(
date: filterStartDate,
label: translate(LocalizationKeys.log_filter_startDate),
controller: filterStartDateController,
onChanged: (newDate) {
if (newDate != null) {
filterStartDate =
DateTime(newDate.year, newDate.month, newDate.day);
filterStartDateController.text =
DateTimeUtils.displayDate(filterStartDate);
}
},
),
),
Expanded(
child: Padding(
padding: const EdgeInsets.only(left: 5.0),
child: DateTimeFormField(
date: filterEndDate,
label: translate(LocalizationKeys.log_filter_endDate),
controller: filterEndDateController,
onChanged: (newDate) {
if (newDate != null) {
filterEndDate = DateTime(
newDate.year, newDate.month, newDate.day);
filterEndDateController.text =
DateTimeUtils.displayDate(filterEndDate);
}
},
),
),
)
],
),
Row(
children: Settings.glucoseMeasurement ==
GlucoseMeasurement.mgPerDl
? [
Expanded(
child: NumberFormField(
label: translate(LocalizationKeys.log_filter_minGlucose, args: {
"glucoseMeasurement": Settings.glucoseMeasurementSuffix
}),
controller: minMgPerDlController,
onChanged: (value) {
if (value != null) {
minMgPerDlController.text = value.toString();
}
},
),
),
Expanded(
child: Padding(
padding: const EdgeInsets.only(left: 5.0),
child: NumberFormField(
label: translate(LocalizationKeys.log_filter_maxGlucose, args: {
"glucoseMeasurement": Settings.glucoseMeasurementSuffix
}),
controller: maxMgPerDlController,
onChanged: (value) {
if (value != null) {
maxMgPerDlController.text = value.toString();
}
},
),
),
)
]
: [
Expanded(
child: NumberFormField(
label: translate(LocalizationKeys.log_filter_minGlucose, args: {
"glucoseMeasurement": Settings.glucoseMeasurementSuffix
}),
controller: minMmolPerLController,
onChanged: (value) {
if (value != null) {
minMmolPerLController.text = value.toString();
}
},
),
),
Expanded(
child: Padding(
padding: const EdgeInsets.only(left: 5.0),
child: NumberFormField(
label: translate(LocalizationKeys.log_filter_maxGlucose, args: {
"glucoseMeasurement": Settings.glucoseMeasurementSuffix
}),
controller: maxMmolPerLController,
onChanged: (value) {
if (value != null) {
maxMmolPerLController.text = value.toString();
}
},
),
),
)
],
),
Expanded(
child: AutoCompleteDropdownButton<Meal>(
controller: mealController,
selectedItem: meal,
label: translate(LocalizationKeys.log_filter_meal),
items: _meals,
onChanged: (value) {
setState(() {
meal = value;
});
},
),
),
TextFormField(
controller: mealNameController,
decoration: InputDecoration(
labelText: translate(LocalizationKeys.log_filter_mealNameContains),
),
),
TextFormField(
controller: noteController,
decoration: InputDecoration(
labelText: translate(LocalizationKeys.log_filter_noteContains),
),
),
],
),
actions: <Widget>[
TextButton(
onPressed: () => Navigator.pop(context),
child: Text(translate(LocalizationKeys.general_cancel).toUpperCase()),
),
ElevatedButton(
onPressed: () => widget.onApplyFilter(
startDate: filterStartDate,
endDate: filterEndDate,
minMgPerDl: int.tryParse(minMgPerDlController.text),
maxMgPerDl: int.tryParse(maxMgPerDlController.text),
minMmolPerL: double.tryParse(minMmolPerLController.text),
maxMmolPerL: double.tryParse(maxMmolPerLController.text),
mealId: meal?.id,
mealName: mealNameController.text,
note: noteController.text,
),
child: Text(translate(LocalizationKeys.general_apply).toUpperCase()),
),
]);
}
}

View File

@ -1,3 +1,5 @@
import 'package:diameter/localization_keys.dart';
import 'package:diameter/screens/log/log_filter_dialog.dart';
import 'package:diameter/screens/reports/export.dart';
import 'package:diameter/utils/dialog_utils.dart';
import 'package:diameter/models/glucose_target.dart';
@ -11,6 +13,9 @@ import 'package:diameter/utils/date_time_utils.dart';
import 'package:flutter/material.dart';
import 'dart:math' as math;
import 'package:flutter_translate/flutter_translate.dart';
class LogScreen extends StatefulWidget {
static const String routeName = '/log';
const LogScreen({Key? key}) : super(key: key);
@ -65,7 +70,7 @@ class _LogScreenState extends State<LogScreen> {
void onDelete(LogEntry logEntry) {
LogEntry.remove(logEntry.id);
reload(message: 'Log Entry deleted');
reload(message: translate(LocalizationKeys.log_deleted));
}
void handleDeleteAction(LogEntry logEntry) async {
@ -73,7 +78,7 @@ class _LogScreenState extends State<LogScreen> {
DialogUtils.showConfirmationDialog(
context: context,
onConfirm: () => onDelete(logEntry),
message: 'Are you sure you want to delete this Log Entry?',
message: translate(LocalizationKeys.log_confirmDelete),
);
} else {
onDelete(logEntry);
@ -90,15 +95,48 @@ class _LogScreenState extends State<LogScreen> {
}
}
void onChangeFilter(
{DateTime? startDate,
DateTime? endDate,
int? minMgPerDl,
int? maxMgPerDl,
double? minMmolPerL,
double? maxMmolPerL,
int? mealId,
String? mealName,
String? note}) {
setState(() {
_logEntries = LogEntry.getAllByFilter(
startDate: startDate,
endDate: endDate,
minMgPerDl: minMgPerDl,
maxMgPerDl: maxMgPerDl,
minMmolPerL: minMmolPerL,
maxMmolPerL: maxMmolPerL,
mealId: mealId,
mealName: mealName,
note: note,
);
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Log Entries'),
title: Text(translate(LocalizationKeys.log_title)),
actions: <Widget>[
IconButton(
onPressed: () => onChangeDate(DateTime.now()),
icon: const Icon(Icons.today)),
IconButton(
onPressed: () => showDialog(
context: context,
builder: (context) => LogFilterDialog(
date: _date,
onApplyFilter: onChangeFilter,
)),
icon: const Icon(Icons.filter_list)),
IconButton(
onPressed: () => showDialog(
context: context,
@ -297,7 +335,7 @@ class _LogScreenState extends State<LogScreen> {
? [
Text(
bolus.toStringAsPrecision(3)),
const Text('U',
Text(translate(LocalizationKeys.general_suffixes_units),
textScaleFactor: 0.75),
]
: [],
@ -309,8 +347,12 @@ class _LogScreenState extends State<LogScreen> {
? [
Text(
carbs.toStringAsPrecision(3)),
Text(
'${Settings.nutritionMeasurementSuffix} carbs',
Text(translate(
LocalizationKeys.general_suffixes_carbs,
args: {
"nutritionMeasurementSuffix": Settings.nutritionMeasurementSuffix,
}
),
textScaleFactor: 0.75),
]
: [],
@ -334,9 +376,10 @@ class _LogScreenState extends State<LogScreen> {
},
),
)
: const Center(
: Center(
child: Text(
'You have not created any Log Entries for this date yet!'),
translate(LocalizationKeys.log_empty)
),
),
),
],

View File

@ -1,6 +1,7 @@
import 'package:diameter/components/detail.dart';
import 'package:diameter/components/forms/boolean_form_field.dart';
import 'package:diameter/components/forms/number_form_field.dart';
import 'package:diameter/localization_keys.dart';
import 'package:diameter/utils/dialog_utils.dart';
import 'package:diameter/components/forms/auto_complete_dropdown_button.dart';
import 'package:diameter/components/forms/form_wrapper.dart';
@ -17,6 +18,7 @@ import 'package:diameter/screens/category/meal_portion_type_detail.dart';
import 'package:diameter/screens/category/meal_source_detail.dart';
import 'package:diameter/utils/utils.dart';
import 'package:flutter/material.dart';
import 'package:flutter_translate/flutter_translate.dart';
class MealDetailScreen extends StatefulWidget {
static const String routeName = '/meal';
@ -195,7 +197,9 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
meal.carbsRatioAccuracy.target = _carbsRatioAccuracy;
Meal.put(meal);
Navigator.pop(context, ['${_isNew ? 'New' : ''} Meal Saved', meal]);
Navigator.pop(context, [translate(LocalizationKeys.meal_saved, args: {
"status": _isNew ? translate(LocalizationKeys.meal_new) : '',
}), meal]);
}
setState(() {
_isSaving = false;
@ -340,7 +344,10 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(_isNew ? 'New Meal' : _meal!.value),
title: Text(translate(LocalizationKeys.meal_saved, args: {
"status": _isNew ? translate(LocalizationKeys.meal_new) : '',
"name": _meal?.value,
})),
),
drawer: const Navigation(currentLocation: MealDetailScreen.routeName),
body: Scrollbar(
@ -354,12 +361,12 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
fields: [
TextFormField(
controller: _valueController,
decoration: const InputDecoration(
labelText: 'Name',
decoration: InputDecoration(
labelText: translate(LocalizationKeys.meal_fields_name),
),
validator: (value) {
if (value!.trim().isEmpty) {
return 'Empty name';
return translate(LocalizationKeys.meal_fields_validators_name);
}
return null;
},
@ -370,7 +377,7 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
child: AutoCompleteDropdownButton<MealSource>(
controller: _mealSourceController,
selectedItem: _mealSource,
label: 'Meal Source',
label: translate(LocalizationKeys.meal_fields_mealSource),
items: _mealSources,
onChanged: onSelectMealSource,
),
@ -400,7 +407,7 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
child: AutoCompleteDropdownButton<MealPortionType>(
controller: _mealPortionTypeController,
selectedItem: _mealPortionType,
label: 'Meal Portion Type',
label: translate(LocalizationKeys.meal_fields_mealPortionType),
items: _mealPortionTypes,
onChanged: updateMealPortionType,
),
@ -429,7 +436,7 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
children: [
Expanded(
child: NumberFormField(
label: 'Carbs ratio',
label: translate(LocalizationKeys.meal_fields_carbsRatio),
suffix: '%',
controller: _carbsRatioController,
showSteppers: false,
@ -445,7 +452,7 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
const SizedBox(width: 10),
Expanded(
child: NumberFormField(
label: 'Portion size',
label: translate(LocalizationKeys.meal_fields_portionSize),
suffix: Settings.nutritionMeasurementSuffix,
controller: _portionSizeController,
showSteppers: false,
@ -461,7 +468,7 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
const SizedBox(width: 10),
Expanded(
child: NumberFormField(
label: 'Carbs per portion',
label: translate(LocalizationKeys.meal_fields_carbsPerPortion),
suffix: Settings.nutritionMeasurementSuffix,
controller: _carbsPerPortionController,
showSteppers: false,
@ -479,7 +486,7 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
Expanded(
child: BooleanFormField(
value: _setManually,
label: 'set carbs ratio manually',
label: translate(LocalizationKeys.meal_fields_setManually),
onChanged: (value) {
setState(() {
_setManually = value;
@ -490,8 +497,8 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
),
TextFormField(
controller: _notesController,
decoration: const InputDecoration(
labelText: 'Notes',
decoration: InputDecoration(
labelText: translate(LocalizationKeys.meal_fields_notes),
),
keyboardType: TextInputType.multiline,
minLines: 2,
@ -503,7 +510,7 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
child: Row(
children: [
Text(
'BOLUS DELAY',
translate(LocalizationKeys.meal_fields_delay_title).toUpperCase(),
style: Theme.of(context).textTheme.subtitle2,
),
const Spacer(),
@ -514,9 +521,9 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
children: [
Expanded(
child: TextFormField(
decoration: const InputDecoration(
labelText: 'Duration',
suffixText: ' min',
decoration: InputDecoration(
labelText: translate(LocalizationKeys.meal_fields_delay_duration),
suffixText: translate(LocalizationKeys.general_suffixes_mins),
),
controller: _delayedBolusDurationController,
onChanged: (value) => setState(() {}),
@ -553,7 +560,7 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
children: [
Expanded(
child: Text(
'ADDITIONAL FIELDS',
translate(LocalizationKeys.meal_fields_additional_title).toUpperCase(),
style: Theme.of(context).textTheme.subtitle2,
),
),
@ -574,7 +581,7 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
AutoCompleteDropdownButton<MealCategory>(
controller: _mealCategoryController,
selectedItem: _mealCategory,
label: 'Meal Category',
label: translate(LocalizationKeys.meal_fields_additional_mealCategory),
items: _mealCategories,
onChanged: updateMealCategory,
),
@ -609,7 +616,7 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
AutoCompleteDropdownButton<Accuracy>(
controller: _portionSizeAccuracyController,
selectedItem: _portionSizeAccuracy,
label: 'Portion Size Accuracy',
label: translate(LocalizationKeys.meal_fields_additional_portionSizeAccuracy),
items: _portionSizeAccuracies,
onChanged: updatePortionSizeAccuracy,
),
@ -645,7 +652,7 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
AutoCompleteDropdownButton<Accuracy>(
controller: _carbsRatioAccuracyController,
selectedItem: _carbsRatioAccuracy,
label: 'Carbs Ratio Accuracy',
label: translate(LocalizationKeys.meal_fields_additional_carbsRatioAccuracy),
items: _carbsRatioAccuracies,
onChanged: updateCarbsRatioAccuracy,
),

View File

@ -1,9 +1,11 @@
import 'package:diameter/localization_keys.dart';
import 'package:diameter/utils/dialog_utils.dart';
import 'package:diameter/models/meal.dart';
import 'package:diameter/models/settings.dart';
import 'package:diameter/navigation.dart';
import 'package:diameter/screens/meal/meal_detail.dart';
import 'package:flutter/material.dart';
import 'package:flutter_translate/flutter_translate.dart';
class MealListScreen extends StatefulWidget {
static const String routeName = '/meals';
@ -50,7 +52,7 @@ class _MealListScreenState extends State<MealListScreen> {
void onDelete(Meal meal) {
Meal.remove(meal.id);
reload(message: 'Meal deleted');
reload(message: translate(LocalizationKeys.meal_deleted));
}
void handleDeleteAction(Meal meal) async {
@ -58,7 +60,7 @@ class _MealListScreenState extends State<MealListScreen> {
DialogUtils.showConfirmationDialog(
context: context,
onConfirm: () => onDelete(meal),
message: 'Are you sure you want to delete this Meal?',
message: translate(LocalizationKeys.meal_confirmDelete),
);
} else {
onDelete(meal);
@ -68,7 +70,11 @@ class _MealListScreenState extends State<MealListScreen> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Meals'), actions: <Widget>[
appBar: AppBar(
title: Text(
translate(LocalizationKeys.meal_title)
),
actions: <Widget>[
IconButton(onPressed: reload, icon: const Icon(Icons.refresh))
]),
drawer: const Navigation(currentLocation: MealListScreen.routeName),
@ -84,7 +90,7 @@ class _MealListScreenState extends State<MealListScreen> {
itemCount: _meals.length,
itemBuilder: (context, index) {
final meal = _meals[index];
String portionType = meal.mealPortionType.hasValue ? ' per ${meal.mealPortionType.target!.value}' : '';
String portionType = meal.mealPortionType.hasValue ? ' ${translate(LocalizationKeys.general_per)} ${meal.mealPortionType.target!.value}' : '';
return Card(
child: ListTile(
isThreeLine: true,
@ -118,7 +124,7 @@ class _MealListScreenState extends State<MealListScreen> {
? [
Text(meal.carbsPerPortion!.toStringAsPrecision(3)),
Text(
'${Settings.nutritionMeasurementSuffix} carbs',
'${Settings.nutritionMeasurementSuffix} ${translate(LocalizationKeys.general_suffixes_carbs)}',
textScaleFactor: 0.75),
]
: [],
@ -166,8 +172,8 @@ class _MealListScreenState extends State<MealListScreen> {
);
},
),
): const Center(
child: Text('You have not created any Meals yet!'),
): Center(
child: Text(translate(LocalizationKeys.meal_empty)),
),
),
],

View File

@ -1,4 +1,5 @@
import 'package:charts_flutter/flutter.dart' as charts;
import 'package:diameter/localization_keys.dart';
import 'package:diameter/models/basal.dart';
import 'package:diameter/models/glucose_target.dart';
import 'package:diameter/models/log_bolus.dart';
@ -9,6 +10,7 @@ import 'package:diameter/models/settings.dart';
import 'package:diameter/navigation.dart';
import 'package:diameter/utils/date_time_utils.dart';
import 'package:flutter/material.dart';
import 'package:flutter_translate/flutter_translate.dart';
charts.TimeSeriesChart generateChart(
DateTime date,
@ -48,7 +50,7 @@ charts.TimeSeriesChart generateChart(
return charts.TimeSeriesChart(
[
charts.Series<LogEntry, DateTime>(
id: 'Glucose',
id: translate(LocalizationKeys.reports_ids_glucose),
colorFn: (LogEntry entry, _) => charts.Color.black,
domainFn: (LogEntry entry, _) => entry.time,
measureFn: (LogEntry entry, _) =>
@ -58,7 +60,7 @@ charts.TimeSeriesChart generateChart(
data: logEntries,
)..setAttribute(charts.rendererIdKey, 'glucoseRenderer'),
charts.Series<LogEntry, DateTime>(
id: 'Carbohydrates',
id: translate(LocalizationKeys.reports_ids_carbs),
colorFn: (LogEntry entry, _) =>
charts.MaterialPalette.yellow.shadeDefault,
domainFn: (LogEntry entry, _) => entry.time,
@ -67,7 +69,7 @@ charts.TimeSeriesChart generateChart(
data: showMeals ? logEntries : [],
)..setAttribute(charts.rendererIdKey, 'carbsRenderer'),
charts.Series<Basal, DateTime>(
id: 'Basal',
id: translate(LocalizationKeys.reports_ids_basal),
colorFn: (Basal basal, _) => charts.Color.black.lighter,
domainFn: (Basal basal, _) => basal.startTime,
measureFn: (Basal basal, _) => basal.units,
@ -77,7 +79,7 @@ charts.TimeSeriesChart generateChart(
charts.measureAxisIdKey, charts.Axis.secondaryMeasureAxisId)
..setAttribute(charts.rendererIdKey, 'basalRenderer'),
charts.Series<LogEntry, DateTime>(
id: 'Bolus',
id: translate(LocalizationKeys.reports_ids_bolus),
colorFn: (LogEntry entry, _) =>
charts.MaterialPalette.blue.shadeDefault,
domainFn: (LogEntry entry, _) => entry.time,
@ -260,7 +262,7 @@ class _DailyChartState extends State<DailyChart> {
value: showChart,
onChanged: (_) =>
setState(() => showChart = !showChart),
title: const Text('show Chart'),
title: Text(translate(LocalizationKeys.reports_dailyCharts_showChart)),
controlAffinity: ListTileControlAffinity.leading,
),
Padding(
@ -271,7 +273,7 @@ class _DailyChartState extends State<DailyChart> {
? (_) =>
setState(() => showBolus = !showBolus)
: null,
title: const Text('show Bolus'),
title: Text(translate(LocalizationKeys.reports_dailyCharts_showBolus)),
controlAffinity:
ListTileControlAffinity.leading,
),
@ -284,7 +286,7 @@ class _DailyChartState extends State<DailyChart> {
? (_) =>
setState(() => showBasal = !showBasal)
: null,
title: const Text('show Basal'),
title: Text(translate(LocalizationKeys.reports_dailyCharts_showBasal)),
controlAffinity:
ListTileControlAffinity.leading,
),
@ -297,7 +299,7 @@ class _DailyChartState extends State<DailyChart> {
? (_) =>
setState(() => showMeals = !showMeals)
: null,
title: const Text('show Meals'),
title: Text(translate(LocalizationKeys.reports_dailyCharts_showMeals)),
controlAffinity:
ListTileControlAffinity.leading,
),
@ -307,7 +309,7 @@ class _DailyChartState extends State<DailyChart> {
actions: <Widget>[
ElevatedButton(
onPressed: () => Navigator.pop(context),
child: const Text('CLOSE'),
child: Text(translate(LocalizationKeys.general_close).toUpperCase()),
),
]),
),
@ -378,9 +380,8 @@ class _DailyChartState extends State<DailyChart> {
child: logEntries.isNotEmpty
? generateChart(date, logEntries, logEvents, targets, showMeals,
showBasal, showBolus)
: const Center(
child: Text(
'You have not created any Log Entries for this date yet!'),
: Center(
child: Text(translate(LocalizationKeys.reports_dailyCharts_empty)),
),
),
],

View File

@ -2,6 +2,7 @@ import 'dart:io';
import 'dart:math';
import 'dart:typed_data';
import 'package:diameter/components/forms/date_time_form_field.dart';
import 'package:diameter/localization_keys.dart';
import 'package:diameter/models/glucose_target.dart';
import 'package:diameter/models/log_bolus.dart';
import 'package:diameter/models/log_entry.dart';
@ -12,6 +13,7 @@ import 'package:diameter/screens/reports/daily_chart.dart';
import 'package:diameter/utils/date_time_utils.dart';
import 'package:diameter/utils/utils.dart';
import 'package:flutter/material.dart';
import 'package:flutter_translate/flutter_translate.dart';
import 'package:intl/intl.dart';
import 'package:open_file/open_file.dart';
import 'package:path_provider/path_provider.dart';
@ -121,7 +123,7 @@ class _ExportDialogState extends State<ExportDialog> {
children: [
Expanded(
child: RadioListTile(
title: const Text('single date'),
title: Text(translate(LocalizationKeys.reports_export_singleDate)),
groupValue: exportRange,
value: false,
onChanged: (_) {
@ -132,7 +134,7 @@ class _ExportDialogState extends State<ExportDialog> {
),
Expanded(
child: RadioListTile(
title: const Text('range'),
title: Text(translate(LocalizationKeys.reports_export_range)),
groupValue: exportRange,
value: true,
onChanged: (value) {
@ -148,7 +150,7 @@ class _ExportDialogState extends State<ExportDialog> {
Expanded(
child: DateTimeFormField(
date: exportStartDate,
label: 'Date',
label: translate(LocalizationKeys.reports_export_date),
controller: exportStartDateController,
onChanged: (newDate) {
if (newDate != null) {
@ -166,7 +168,7 @@ class _ExportDialogState extends State<ExportDialog> {
padding: const EdgeInsets.only(left: 5.0),
child: DateTimeFormField(
date: exportEndDate,
label: 'End Date',
label: translate(LocalizationKeys.reports_export_endDate),
controller: exportEndDateController,
onChanged: (newDate) {
if (newDate != null) {
@ -185,7 +187,7 @@ class _ExportDialogState extends State<ExportDialog> {
CheckboxListTile(
value: showChart,
onChanged: (_) => setState(() => showChart = !showChart),
title: const Text('show Chart'),
title: Text(translate(LocalizationKeys.reports_export_showChart)),
controlAffinity: ListTileControlAffinity.leading,
),
Padding(
@ -195,7 +197,7 @@ class _ExportDialogState extends State<ExportDialog> {
onChanged: showChart
? (_) => setState(() => showBolus = !showBolus)
: null,
title: const Text('show Bolus'),
title: Text(translate(LocalizationKeys.reports_export_showBolus)),
controlAffinity: ListTileControlAffinity.leading,
),
),
@ -206,7 +208,7 @@ class _ExportDialogState extends State<ExportDialog> {
onChanged: showChart
? (_) => setState(() => showBasal = !showBasal)
: null,
title: const Text('show Basal'),
title: Text(translate(LocalizationKeys.reports_export_showBasal)),
controlAffinity: ListTileControlAffinity.leading,
),
),
@ -217,14 +219,14 @@ class _ExportDialogState extends State<ExportDialog> {
onChanged: showChart
? (_) => setState(() => showMeals = !showMeals)
: null,
title: const Text('show Meals'),
title: Text(translate(LocalizationKeys.reports_export_showMeals)),
controlAffinity: ListTileControlAffinity.leading,
),
),
CheckboxListTile(
value: showTable,
onChanged: (_) => setState(() => showTable = !showTable),
title: const Text('show Table'),
title: Text(translate(LocalizationKeys.reports_export_showTable)),
controlAffinity: ListTileControlAffinity.leading,
),
],
@ -232,13 +234,13 @@ class _ExportDialogState extends State<ExportDialog> {
actions: <Widget>[
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('CANCEL'),
child: Text(translate(LocalizationKeys.general_cancel).toUpperCase()),
),
ElevatedButton(
onPressed: !_isSaving && (showChart || showTable)
? () => onExport(context)
: null,
child: const Text('EXPORT'),
child: Text(translate(LocalizationKeys.reports_export_export).toUpperCase()),
),
]);
}
@ -273,15 +275,14 @@ pw.Table generateTable(List<LogEntry> logEntries) {
const small = pw.TextStyle(fontSize: 8.0);
final tableHeaders = [
'Time',
'Glucose',
'Bolus',
'Notes',
'Meals',
'Portion Size',
'Carbohydrates',
'Meal Bolus',
'Meal Notes',
translate(LocalizationKeys.reports_export_tableHeaders_time),
translate(LocalizationKeys.reports_export_tableHeaders_glucose),
translate(LocalizationKeys.reports_export_tableHeaders_notes),
translate(LocalizationKeys.reports_export_tableHeaders_meals),
translate(LocalizationKeys.reports_export_tableHeaders_portionSize),
translate(LocalizationKeys.reports_export_tableHeaders_carbs),
translate(LocalizationKeys.reports_export_tableHeaders_mealBolus),
translate(LocalizationKeys.reports_export_tableHeaders_mealNotes),
];
final List<pw.TableRow> data = logEntries.map((logEntry) {
@ -304,7 +305,9 @@ pw.Table generateTable(List<LogEntry> logEntries) {
for (var bolus in boli
.where((bolus) => bolus.meal.targetId == meal.id)
.toList())
'${bolus.units} U${bolus.delay != null ? ' (over ${bolus.delay} min)' : ''}'
'${bolus.units} ${translate(LocalizationKeys.general_suffixes_units)} ${bolus.delay != null ? translate(LocalizationKeys.log_detail_tabs_bolus_delayedBy, args: {
"delay": '${bolus.delay} ${translate(LocalizationKeys.general_suffixes_mins)}'
}) : ''}'
].join(' + '),
notes: meal.notes ?? '',
))
@ -320,7 +323,9 @@ pw.Table generateTable(List<LogEntry> logEntries) {
bolus.meal.targetId != 0 &&
!meals.any((meal) => meal.id == bolus.meal.targetId))
.map((bolus) =>
'${bolus.units} U${bolus.delay != null ? ' (over ${bolus.delay} min)' : ''}${bolus.meal.target != null ? ' (for ${bolus.meal.target!.value})' : ''}')
'${bolus.units} ${translate(LocalizationKeys.general_suffixes_units)} ${bolus.delay != null ? translate(LocalizationKeys.log_detail_tabs_bolus_delayedBy, args: {
"delay": '${bolus.delay} ${translate(LocalizationKeys.general_suffixes_mins)}'
}) : ''}')
.toList();
return pw.TableRow(
@ -358,7 +363,7 @@ pw.Table generateTable(List<LogEntry> logEntries) {
),
// Bolus
pw.Text(
[for (var bolus in glucoseBoli) '${bolus.units} U'].join(' + ')),
[for (var bolus in glucoseBoli) '${bolus.units} ${translate(LocalizationKeys.general_suffixes_units)}'].join(' + ')),
// Notes
pw.Text(logEntry.notes ?? '', style: small),
// Meals
@ -449,7 +454,7 @@ Future<Uint8List> generateLogReport(
return pw.Column(
children: [
pw.Text(
'LOG REPORT',
translate(LocalizationKeys.reports_export_title).toUpperCase(),
style: pw.TextStyle(
fontWeight: pw.FontWeight.bold,
fontSize: 12,

View File

@ -1,6 +1,8 @@
import 'package:diameter/localization_keys.dart';
import 'package:diameter/navigation.dart';
import 'package:diameter/screens/reports/export.dart';
import 'package:flutter/material.dart';
import 'package:flutter_translate/flutter_translate.dart';
class ReportsOverviewScreen extends StatefulWidget {
static const String routeName = '/reports';
@ -23,7 +25,7 @@ class _ReportsOverviewScreenState extends State<ReportsOverviewScreen> {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Reports'),
title: Text(translate(LocalizationKeys.reports_title)),
),
drawer:
const Navigation(currentLocation: ReportsOverviewScreen.routeName),
@ -47,7 +49,7 @@ class _ReportsOverviewScreenState extends State<ReportsOverviewScreen> {
child: Icon(Icons.today, size: 50, color: Theme.of(context).textTheme.subtitle2?.color),
),
Text(
'DAILY REPORT',
translate(LocalizationKeys.reports_sections_dailyReport).toUpperCase(),
style: Theme.of(context).textTheme.subtitle2,
textAlign: TextAlign.center
),
@ -72,7 +74,7 @@ class _ReportsOverviewScreenState extends State<ReportsOverviewScreen> {
child: Icon(Icons.today, size: 50, color: Theme.of(context).textTheme.subtitle2?.color),
),
Text(
'PDF REPORT',
translate(LocalizationKeys.reports_sections_pdfReport).toUpperCase(),
style: Theme.of(context).textTheme.subtitle2,
textAlign: TextAlign.center
),

View File

@ -2,9 +2,9 @@ import 'package:diameter/components/detail.dart';
import 'package:diameter/utils/dialog_utils.dart';
import 'package:diameter/components/forms/auto_complete_dropdown_button.dart';
import 'package:diameter/components/forms/form_wrapper.dart';
import 'package:diameter/models/ingredient.dart';
import 'package:diameter/models/x_ingredient.dart';
import 'package:diameter/models/meal.dart';
import 'package:diameter/models/recipe.dart';
import 'package:diameter/models/x_recipe.dart';
import 'package:diameter/models/settings.dart';
import 'package:diameter/navigation.dart';
import 'package:diameter/screens/meal/meal_detail.dart';

View File

@ -1,9 +1,9 @@
import 'package:diameter/screens/x_recipe/recipe_detail.dart';
import 'package:diameter/utils/dialog_utils.dart';
import 'package:diameter/models/ingredient.dart';
import 'package:diameter/models/recipe.dart';
import 'package:diameter/models/x_ingredient.dart';
import 'package:diameter/models/x_recipe.dart';
import 'package:diameter/models/settings.dart';
import 'package:diameter/navigation.dart';
import 'package:diameter/screens/recipe/recipe_detail.dart';
import 'package:flutter/material.dart';
class RecipeListScreen extends StatefulWidget {

View File

@ -1,11 +1,13 @@
import 'package:diameter/components/forms/boolean_form_field.dart';
import 'package:diameter/components/forms/number_form_field.dart';
import 'package:diameter/localization_keys.dart';
import 'package:diameter/utils/dialog_utils.dart';
import 'package:diameter/components/forms/auto_complete_dropdown_button.dart';
import 'package:diameter/models/settings.dart';
import 'package:diameter/navigation.dart';
import 'package:diameter/utils/utils.dart';
import 'package:flutter/material.dart';
import 'package:flutter_translate/flutter_translate.dart';
import 'package:intl/intl.dart';
class SettingsScreen extends StatefulWidget {
@ -152,19 +154,19 @@ class _SettingsScreenState extends State<SettingsScreen> {
showConfirmationDialogOnDelete: _showConfirmationDialogOnDelete,
showConfirmationDialogOnStopEvent: _showConfirmationDialogOnStopEvent,
));
reload(message: 'Settings updated');
reload(message: translate(LocalizationKeys.settings_updated));
}
void onReset() {
Settings.reset();
reload(message: 'Settings have been reset to default');
reload(message: translate(LocalizationKeys.settings_reset));
}
void handleResetAction() async {
DialogUtils.showConfirmationDialog(
context: context,
onConfirm: onReset,
message: 'Are you sure you want to reset all settings?',
message: translate(LocalizationKeys.settings_confirmReset),
);
}
@ -172,7 +174,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Application Settings'),
title: Text(translate(LocalizationKeys.settings_title)),
),
drawer: const Navigation(currentLocation: SettingsScreen.routeName),
body: SingleChildScrollView(
@ -191,7 +193,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
children: [
Expanded(
child: Text(
'MEASUREMENTS',
translate(LocalizationKeys.settings_sections_measurements).toUpperCase(),
style: Theme.of(context).textTheme.subtitle2,
),
),
@ -208,7 +210,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
AutoCompleteDropdownButton<String>(
controller: _nutritionMeasurementLabelController,
selectedItem: _nutritionMeasurementLabelController.text,
label: 'Preferred Nutrition Measurement',
label: translate(LocalizationKeys.settings_fields_nutritionMeasurement),
items: nutritionMeasurementLabels,
onChanged: (value) {
_nutritionMeasurementLabelController.text =
@ -221,7 +223,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
child: AutoCompleteDropdownButton<String>(
controller: _glucoseMeasurementLabelController,
selectedItem: _glucoseMeasurementLabelController.text,
label: 'Preferred Glucose Measurement',
label: translate(LocalizationKeys.settings_fields_glucoseMeasurement),
items: glucoseMeasurementLabels,
onChanged: (value) {
_glucoseMeasurementLabelController.text =
@ -232,8 +234,8 @@ class _SettingsScreenState extends State<SettingsScreen> {
),
Settings.glucoseMeasurement == GlucoseMeasurement.mgPerDl
? NumberFormField(
label: 'Target glucose',
suffix: 'mg/dl',
label: translate(LocalizationKeys.settings_fields_targetGlucose),
suffix: Settings.glucoseMeasurementSuffix,
controller: _targetGlucoseMgPerDlController,
showSteppers: false,
onChanged: (_) async {
@ -254,8 +256,8 @@ class _SettingsScreenState extends State<SettingsScreen> {
},
)
: NumberFormField(
label: 'Target glucose',
suffix: 'mmol/l',
label: translate(LocalizationKeys.settings_fields_targetGlucose),
suffix: Settings.glucoseMeasurementSuffix,
controller: _targetGlucoseMmolPerLController,
showSteppers: false,
onChanged: (_) async {
@ -277,7 +279,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
child: NumberFormField(
controller: _insulinIncrementsController,
showSteppers: false,
label: 'Insulin increment',
label: translate(LocalizationKeys.settings_fields_insulinIncrement),
onChanged: (value) {
_insulinIncrementsController.text =
(value ?? 0).toString();
@ -287,7 +289,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
NumberFormField(
controller: _nutritionIncrementsController,
showSteppers: false,
label: 'Nutrition increment',
label: translate(LocalizationKeys.settings_fields_nutritionIncrement),
onChanged: (value) {
_nutritionIncrementsController.text =
(value ?? 0).toString();
@ -298,7 +300,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
child: NumberFormField(
controller: _mmolPerLIncrementsController,
showSteppers: false,
label: 'Mmol/L increment',
label: translate(LocalizationKeys.settings_fields_mmolLIncrement),
onChanged: (value) {
_mmolPerLIncrementsController.text =
(value ?? 0).toString();
@ -307,7 +309,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
),
BooleanFormField(
value: _onlyDisplayActiveGlucoseMeasurement,
label: 'only display active glucose measurement',
label: translate(LocalizationKeys.settings_fields_onlyDisplayActive),
onChanged: (value) {
_onlyDisplayActiveGlucoseMeasurement = value;
saveSettings();
@ -317,7 +319,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
value: _displayBothGlucoseMeasurementsInDetailView,
enabled: !_onlyDisplayActiveGlucoseMeasurement,
label:
'display both glucose measurements in detail view',
translate(LocalizationKeys.settings_fields_displayBothDetail),
onChanged: (value) {
_displayBothGlucoseMeasurementsInDetailView = value;
saveSettings();
@ -326,7 +328,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
BooleanFormField(
value: _displayBothGlucoseMeasurementsInListView,
enabled: !_onlyDisplayActiveGlucoseMeasurement,
label: 'display both glucose measurements in list view',
label: translate(LocalizationKeys.settings_fields_displayBothList),
onChanged: (value) {
_displayBothGlucoseMeasurementsInListView = value;
saveSettings();
@ -347,7 +349,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
children: [
Expanded(
child: Text(
'CONFIRMATION PROMPTS',
translate(LocalizationKeys.settings_sections_confirmation).toUpperCase(),
style: Theme.of(context).textTheme.subtitle2,
),
),
@ -364,7 +366,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
BooleanFormField(
value: _showConfirmationDialogOnCancel,
label:
'on cancelling edit or creation of a record if changes have already been made',
translate(LocalizationKeys.settings_fields_confirmOnCancel),
onChanged: (value) {
_showConfirmationDialogOnCancel = value;
saveSettings();
@ -372,7 +374,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
),
BooleanFormField(
value: _showConfirmationDialogOnDelete,
label: 'on deleting a record',
label: translate(LocalizationKeys.settings_fields_confirmOnDelete),
onChanged: (value) {
_showConfirmationDialogOnDelete = value;
saveSettings();
@ -380,7 +382,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
),
BooleanFormField(
value: _showConfirmationDialogOnStopEvent,
label: 'on stopping (ending) an event',
label: translate(LocalizationKeys.settings_fields_confirmOnEndEvent),
onChanged: (value) {
_showConfirmationDialogOnStopEvent = value;
saveSettings();
@ -401,7 +403,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
children: [
Expanded(
child: Text(
'TIME & DATE FORMAT',
translate(LocalizationKeys.settings_sections_dateTimeFormat).toUpperCase(),
style: Theme.of(context).textTheme.subtitle2,
),
),
@ -421,15 +423,9 @@ class _SettingsScreenState extends State<SettingsScreen> {
Expanded(
child: TextFormField(
controller: _dateFormatController,
decoration: const InputDecoration(
labelText: 'Date Format',
decoration: InputDecoration(
labelText: translate(LocalizationKeys.settings_fields_dateFormat),
),
validator: (value) {
if (value!.trim().isEmpty) {
return 'Empty title';
}
return null;
},
),
),
Expanded(
@ -439,7 +435,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text('Example', textScaleFactor: 0.75),
Text(translate(LocalizationKeys.general_example), textScaleFactor: 0.75),
Text(
DateFormat(_dateFormatController.text)
.format(DateTime.now()),
@ -459,15 +455,9 @@ class _SettingsScreenState extends State<SettingsScreen> {
Expanded(
child: TextFormField(
controller: _longDateFormatController,
decoration: const InputDecoration(
labelText: 'Long Date Format',
decoration: InputDecoration(
labelText: translate(LocalizationKeys.settings_fields_longDateFormat),
),
validator: (value) {
if (value!.trim().isEmpty) {
return 'Empty title';
}
return null;
},
),
),
Expanded(
@ -477,7 +467,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text('Example',
Text(translate(LocalizationKeys.general_example),
textScaleFactor: 0.75),
Text(
DateFormat(_longDateFormatController.text)
@ -497,15 +487,9 @@ class _SettingsScreenState extends State<SettingsScreen> {
Expanded(
child: TextFormField(
controller: _timeFormatController,
decoration: const InputDecoration(
labelText: 'Time Format',
decoration: InputDecoration(
labelText: translate(LocalizationKeys.settings_fields_timeFormat),
),
validator: (value) {
if (value!.trim().isEmpty) {
return 'Empty title';
}
return null;
},
),
),
Expanded(
@ -515,7 +499,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text('Example', textScaleFactor: 0.75),
Text(translate(LocalizationKeys.general_example), textScaleFactor: 0.75),
Text(
DateFormat(_timeFormatController.text)
.format(DateTime.now()),
@ -535,15 +519,9 @@ class _SettingsScreenState extends State<SettingsScreen> {
Expanded(
child: TextFormField(
controller: _longTimeFormatController,
decoration: const InputDecoration(
labelText: 'Long Time Format',
decoration: InputDecoration(
labelText: translate(LocalizationKeys.settings_fields_longTimeFormat),
),
validator: (value) {
if (value!.trim().isEmpty) {
return 'Empty title';
}
return null;
},
),
),
Expanded(
@ -553,7 +531,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text('Example',
Text(translate(LocalizationKeys.general_example),
textScaleFactor: 0.75),
Text(
DateFormat(_longTimeFormatController.text)
@ -584,7 +562,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
Icons.settings_backup_restore,
size: 18.0,
),
label: const Text('RESET ALL'),
label: Text(translate(LocalizationKeys.settings_resetAll).toUpperCase()),
),
const Spacer(),
],

View File

@ -1,4 +1,6 @@
import 'package:diameter/localization_keys.dart';
import 'package:flutter/material.dart';
import 'package:flutter_translate/flutter_translate.dart';
class DialogUtils {
static void showCancelConfirmationDialog(
@ -13,30 +15,29 @@ class DialogUtils {
List<Widget> actions = [
TextButton(
onPressed: () => Navigator.pop(context, 'CANCEL'),
child: const Text('CANCEL'),
child: Text(translate(LocalizationKeys.general_cancel).toUpperCase()),
),
];
actions.add(isNew
? ElevatedButton(
onPressed: () => Navigator.pop(context, 'DISCARD'),
child: const Text('DISCARD'),
child: Text(translate(LocalizationKeys.general_discard).toUpperCase()),
)
: TextButton(
onPressed: () => Navigator.pop(context, 'DISCARD'),
child: const Text('DISCARD'),
child: Text(translate(LocalizationKeys.general_discard).toUpperCase()),
));
if (!isNew) {
actions.add(ElevatedButton(
onPressed: () => Navigator.pop(context, 'SAVE'),
child: const Text('SAVE'),
child: Text(translate(LocalizationKeys.general_save).toUpperCase()),
));
}
return AlertDialog(
content: Text(message ??
'You already made some changes. Discard your input?'),
content: Text(message ?? translate(LocalizationKeys.general_confirmDiscard)),
actions: actions,
);
}).then((value) {
@ -52,21 +53,21 @@ class DialogUtils {
static void showConfirmationDialog(
{required BuildContext context,
required void Function() onConfirm,
String message = 'Are you sure you want to delete this record?',
String confirmationLabel = 'DELETE'}) {
String? message,
String? confirmationLabel}) {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
content: Text(message),
content: Text(message ?? translate(LocalizationKeys.general_confirmDelete)),
actions: <Widget>[
TextButton(
onPressed: () => Navigator.pop(context, 'CANCEL'),
child: const Text('CANCEL'),
child: Text(translate(LocalizationKeys.general_cancel).toUpperCase()),
),
ElevatedButton(
onPressed: () => Navigator.pop(context, 'CONFIRM'),
child: Text(confirmationLabel),
child: Text(confirmationLabel ?? translate(LocalizationKeys.general_confirm).toUpperCase()),
),
],
);

Binary file not shown.

Binary file not shown.

View File

@ -1,27 +1,34 @@
# Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile
packages:
_discoveryapis_commons:
dependency: transitive
description:
name: _discoveryapis_commons
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.2"
_fe_analyzer_shared:
dependency: transitive
description:
name: _fe_analyzer_shared
url: "https://pub.dartlang.org"
source: hosted
version: "36.0.0"
version: "38.0.0"
analyzer:
dependency: "direct dev"
description:
name: analyzer
url: "https://pub.dartlang.org"
source: hosted
version: "3.3.1"
version: "3.4.1"
archive:
dependency: transitive
description:
name: archive
url: "https://pub.dartlang.org"
source: hosted
version: "3.2.2"
version: "3.3.0"
args:
dependency: transitive
description:
@ -231,7 +238,7 @@ packages:
name: flex_color_scheme
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.1"
version: "4.2.0"
flutter:
dependency: "direct main"
description: flutter
@ -244,11 +251,23 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.4"
flutter_localizations:
dependency: "direct main"
description: flutter
source: sdk
version: "0.0.0"
flutter_test:
dependency: "direct dev"
description: flutter
source: sdk
version: "0.0.0"
flutter_translate:
dependency: "direct main"
description:
name: flutter_translate
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.1"
flutter_web_plugins:
dependency: transitive
description: flutter
@ -268,6 +287,34 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.2"
google_sign_in:
dependency: "direct main"
description:
name: google_sign_in
url: "https://pub.dartlang.org"
source: hosted
version: "5.2.4"
google_sign_in_platform_interface:
dependency: transitive
description:
name: google_sign_in_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.2"
google_sign_in_web:
dependency: transitive
description:
name: google_sign_in_web
url: "https://pub.dartlang.org"
source: hosted
version: "0.10.0+5"
googleapis:
dependency: "direct main"
description:
name: googleapis
url: "https://pub.dartlang.org"
source: hosted
version: "7.0.0"
graphs:
dependency: transitive
description:
@ -519,7 +566,7 @@ packages:
name: printing
url: "https://pub.dartlang.org"
source: hosted
version: "5.7.4"
version: "5.7.5"
process:
dependency: transitive
description:
@ -555,13 +602,20 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.0"
quiver:
dependency: transitive
description:
name: quiver
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.1+1"
shelf:
dependency: transitive
description:
name: shelf
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.0"
version: "1.3.0"
shelf_web_socket:
dependency: transitive
description:
@ -644,6 +698,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.3.0"
universal_io:
dependency: transitive
description:
name: universal_io
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.4"
vector_math:
dependency: transitive
description:
@ -671,7 +732,7 @@ packages:
name: win32
url: "https://pub.dartlang.org"
source: hosted
version: "2.4.2"
version: "2.5.1"
xdg_directories:
dependency: transitive
description:
@ -694,5 +755,5 @@ packages:
source: hosted
version: "3.1.0"
sdks:
dart: ">=2.15.0 <3.0.0"
dart: ">=2.16.0 <3.0.0"
flutter: ">=2.8.0"

View File

@ -11,16 +11,21 @@ environment:
dependencies:
flutter:
sdk: flutter
flutter_localizations:
sdk: flutter
path_provider: ^2.0.9
cupertino_icons: ^1.0.2
flex_color_scheme: ^3.0.1
flex_color_scheme: ^4.2.0
intl: ^0.17.0
objectbox: ^1.2.0
objectbox_sync_flutter_libs: any
charts_flutter: ^0.12.0
printing: ^5.7.2
pdf: ^3.7.1
open_file: ^3.2.1
flutter_translate: ^3.0.1
googleapis: ^7.0.0
google_sign_in: ^5.2.1
charts_flutter: ^0.12.0
dev_dependencies:
flutter_test:
@ -33,3 +38,5 @@ dev_dependencies:
flutter:
uses-material-design: true
assets:
- assets/i18n/

View File

@ -8,7 +8,7 @@
// import 'package:flutter/material.dart';
// import 'package:flutter_test/flutter_test.dart';
// import 'package:tide/main.dart';
// import 'package:diameter/main.dart';
void main() {
// testWidgets('Counter increments smoke test', (WidgetTester tester) async {

View File

@ -1,6 +1,6 @@
<!DOCTYPE html>
<html>
<head>
<head>
<!--
If you are serving your web app in a path other than the root, change the
href value below to reflect the base path you are serving from.
@ -14,22 +14,22 @@
This is a placeholder for base href that will be replaced by the value of
the `--base-href` argument provided to `flutter build`.
-->
<base href="$FLUTTER_BASE_HREF">
<base href="$FLUTTER_BASE_HREF" />
<meta charset="UTF-8">
<meta content="IE=Edge" http-equiv="X-UA-Compatible">
<meta name="description" content="A new Flutter project.">
<meta charset="UTF-8" />
<meta content="IE=Edge" http-equiv="X-UA-Compatible" />
<meta name="description" content="A new Flutter project." />
<!-- iOS meta tags & icons -->
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="apple-mobile-web-app-title" content="tide">
<link rel="apple-touch-icon" href="icons/Icon-192.png">
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
<meta name="apple-mobile-web-app-title" content="diameter" />
<link rel="apple-touch-icon" href="icons/Icon-192.png" />
<title>tide</title>
<link rel="manifest" href="manifest.json">
</head>
<body>
<title>diameter</title>
<link rel="manifest" href="manifest.json" />
</head>
<body>
<!-- This script installs service_worker.js to provide PWA functionality to
application. For more information, see:
https://developers.google.com/web/fundamentals/primers/service-workers -->
@ -41,42 +41,60 @@
return;
}
scriptLoaded = true;
var scriptTag = document.createElement('script');
scriptTag.src = 'main.dart.js';
scriptTag.type = 'application/javascript';
var scriptTag = document.createElement("script");
scriptTag.src = "main.dart.js";
scriptTag.type = "application/javascript";
document.body.append(scriptTag);
}
if ('serviceWorker' in navigator) {
if ("serviceWorker" in navigator) {
// Service workers are supported. Use them.
window.addEventListener('load', function () {
window.addEventListener("load", function () {
// Wait for registration to finish before dropping the <script> tag.
// Otherwise, the browser will load the script multiple times,
// potentially different versions.
var serviceWorkerUrl = 'flutter_service_worker.js?v=' + serviceWorkerVersion;
navigator.serviceWorker.register(serviceWorkerUrl)
var serviceWorkerUrl =
"flutter_service_worker.js?v=" + serviceWorkerVersion;
navigator.serviceWorker
.register(serviceWorkerUrl)
.then((reg) => {
function waitForActivation(serviceWorker) {
serviceWorker.addEventListener('statechange', () => {
if (serviceWorker.state == 'activated') {
console.log('Installed new service worker.');
serviceWorker.addEventListener(
"statechange",
() => {
if (
serviceWorker.state == "activated"
) {
console.log(
"Installed new service worker."
);
loadMainDartJs();
}
});
}
if (!reg.active && (reg.installing || reg.waiting)) {
);
}
if (
!reg.active &&
(reg.installing || reg.waiting)
) {
// No active web worker and we have installed or are installing
// one for the first time. Simply wait for it to activate.
waitForActivation(reg.installing || reg.waiting);
} else if (!reg.active.scriptURL.endsWith(serviceWorkerVersion)) {
waitForActivation(
reg.installing || reg.waiting
);
} else if (
!reg.active.scriptURL.endsWith(
serviceWorkerVersion
)
) {
// When the app updates the serviceWorkerVersion changes, so we
// need to ask the service worker to update.
console.log('New service worker available.');
console.log("New service worker available.");
reg.update();
waitForActivation(reg.installing);
} else {
// Existing service worker is still good.
console.log('Loading app from service worker.');
console.log("Loading app from service worker.");
loadMainDartJs();
}
});
@ -86,7 +104,7 @@
setTimeout(() => {
if (!scriptLoaded) {
console.warn(
'Failed to load app from service worker. Falling back to plain <script> tag.',
"Failed to load app from service worker. Falling back to plain <script> tag."
);
loadMainDartJs();
}
@ -97,5 +115,5 @@
loadMainDartJs();
}
</script>
</body>
</body>
</html>

View File

@ -89,7 +89,7 @@ BEGIN
BEGIN
BLOCK "040904e4"
BEGIN
VALUE "CompanyName", "com.example" "\0"
VALUE "CompanyName", "at.sarahziesel" "\0"
VALUE "FileDescription", "diameter" "\0"
VALUE "FileVersion", VERSION_AS_STRING "\0"
VALUE "InternalName", "diameter" "\0"