add delayed bolus percentage functionality, improve active profile handling (display and setting), move settings to objectbox, improve cancellation check on log bolus, add warning for already running events, implement accuracy reordering

This commit is contained in:
spinel 2021-12-05 00:44:46 +01:00
parent e42ad61f44
commit a59170940f
43 changed files with 1569 additions and 979 deletions

45
TODO
View File

@ -1,31 +1,11 @@
MAIN TASKS: MAIN TASKS:
General/Framework:
☐ find a general way to deal with duration fields
Accuracies:
☐ implement reordering
Basal/Bolus:
☐ replace active profile picking mode with simple dropdown
☐ indicate both the default rate and the currently active one (according to event)
Log Entry:
☐ check for multiple active events with temporary basal/bolus profiles (smallest covered time frame counts)
☐ provide splitting functionality for overlapping events
☐ provide percentage functionality for delayed bolus
☐ get rid of useless cancellation warnings
Events:
☐ when adding an event later on, prompt if existing boli should be recalculated
☐ show event start AND end times in list
Settings: Settings:
add objectbox class and use instead of shared preferences ☐ fix settings saving
FUTURE TASKS: FUTURE TASKS:
General/Framework: General/Framework:
☐ make all fields readonly if user somehow gets to a deleted record detail view ☐ show indicator and make all fields readonly if user somehow gets to a deleted record detail view
☐ add functionality to delete dead records ☐ add functionality to delete dead records (meaning: set deleted flag and no relations)
☐ clean up controllers (dispose method of each stateful widget) ☐ clean up controllers (dispose method of each stateful widget)
☐ add explanations to each section ☐ add explanations to each section
☐ account for deleted/disabled elements in dropdowns ☐ account for deleted/disabled elements in dropdowns
@ -33,20 +13,35 @@ FUTURE TASKS:
☐ add clear button to dropdown (or all text fields?) ☐ add clear button to dropdown (or all text fields?)
☐ check through all detail forms and set required fields/according messages ☐ check through all detail forms and set required fields/according messages
☐ find a better way to work with multiple measurements (or disable it?) ☐ find a better way to work with multiple measurements (or disable it?)
☐ evaluate if some fields should be readonly instead of completely hidden
☐ implement component for durations
Log Overview: Log Overview:
☐ add pagination ☐ add pagination
☐ apply target color settings to glucose ☐ apply target color settings to glucose
Log Entry:
☐ check if there is still an active bolus when suggesting glucose bolus
Event Types: Event Types:
☐ add pagination
☐ add colors as indicators for log entries (and later graphs in reports) ☐ add colors as indicators for log entries (and later graphs in reports)
☐ implement reminders as push notifications ☐ implement reminders as push notifications
Settings: Settings:
☐ add fields for preferred date and time formats ☐ add fields for preferred date and time formats
☐ add fields for glucose target (as map of cutoff glucose and colors) ☐ add fields for glucose target (as map of cutoff glucose and colors)
☐ add option to hide warning dialogs on cancel or event stop ☐ add option to hide warning dialogs on cancel, delete or event stop
☐ add option to hide extra customization options (ie. changing pre calculated values) ☐ add option to hide extra customization options (ie. changing pre calculated values)?
☐ add setting for decimal places ☐ add setting for decimal places
☐ add field for active insulin duration
Archive: Archive:
✔ add objectbox settings class and use instead of shared preferences @done(21-12-05 00:41) @project(MAIN TASKS.Settings)
✔ provide percentage functionality for delayed bolus @done(21-12-04 21:39) @project(MAIN TASKS.Log Entry)
✔ create two bolus entries accordingly @done(21-12-04 22:12) @project(MAIN TASKS.Log Entry)
✔ replace active profile picking mode with simple dropdown @done(21-12-04 20:10) @project(MAIN TASKS.Basal/Bolus)
✔ indicate both the default rate and the currently active one (according to event) @done(21-12-04 20:10) @project(MAIN TASKS.Basal/Bolus)
✔ get rid of excessive cancellation warnings @done(21-12-04 19:09) @project(MAIN TASKS.Log Entry)
✔ give a warning if event of same type is already running @done(21-12-04 18:50) @project(MAIN TASKS.Events)
✔ implement reordering @started(21-12-03 23:12) @done(21-12-04 17:01) @lasted(17h49m38s) @project(MAIN TASKS.Accuracies)
✔ show event start AND end times in list @done(21-12-03 22:04) @project(MAIN TASKS.Events)
✔ separate events from log entries @done(21-12-01 23:37) @project(MAIN TASKS.Events) ✔ separate events from log entries @done(21-12-01 23:37) @project(MAIN TASKS.Events)
✔ show total bolus and carbs per entry @done(21-12-01 19:50) @project(MAIN TASKS.Log Overview) ✔ show total bolus and carbs per entry @done(21-12-01 19:50) @project(MAIN TASKS.Log Overview)
✔ display boli correctly @done(21-11-30 04:14) @project(MAIN TASKS.Log Entry) ✔ display boli correctly @done(21-11-30 04:14) @project(MAIN TASKS.Log Entry)

View File

@ -49,13 +49,15 @@ class BooleanFormField extends StatefulWidget {
final String label; final String label;
final void Function(bool) onChanged; final void Function(bool) onChanged;
final bool? enabled; final bool? enabled;
final EdgeInsets? contentPadding;
const BooleanFormField( const BooleanFormField(
{Key? key, {Key? key,
required this.value, required this.value,
required this.label, required this.label,
required this.onChanged, required this.onChanged,
this.enabled}) this.enabled,
this.contentPadding})
: super(key: key); : super(key: key);
@override @override
@ -67,6 +69,7 @@ class _BooleanFormFieldState extends State<BooleanFormField> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return FormField<bool>(builder: (state) { return FormField<bool>(builder: (state) {
return ListTile( return ListTile(
contentPadding: widget.contentPadding,
onTap: () => widget.onChanged(!widget.value), onTap: () => widget.onChanged(!widget.value),
trailing: Switch( trailing: Switch(
value: widget.value, value: widget.value,
@ -115,7 +118,8 @@ class _DateTimeFormFieldState extends State<DateTimeFormField> {
context: context, context: context,
initialDate: widget.date, initialDate: widget.date,
firstDate: widget.minDate ?? DateTime(2000, 1, 1), firstDate: widget.minDate ?? DateTime(2000, 1, 1),
lastDate: widget.maxDate ?? DateTime.now().add(const Duration(days: 365)), lastDate:
widget.maxDate ?? DateTime.now().add(const Duration(days: 365)),
); );
widget.onChanged(newTime); widget.onChanged(newTime);
}, },
@ -160,4 +164,3 @@ class _TimeOfDayFormFieldState extends State<TimeOfDayFormField> {
); );
} }
} }

View File

@ -1,28 +0,0 @@
import 'package:diameter/settings.dart';
const keyApplicationId = 'DFfD2aeppmqQnVmox02kUZhYOUc7vAtGfunAP7hn';
const keyClientKey = '0ROGEVQP0Id21EMEqK05wJP3nBDuOW5DM5Cpzdt3';
const keyParseServerUrl = 'https://parseapi.back4app.com';
// settings
NutritionMeasurement nutritionMeasurement = NutritionMeasurement.grams;
GlucoseMeasurement glucoseMeasurement = GlucoseMeasurement.mgPerDl;
GlucoseDisplayMode glucoseDisplayMode = GlucoseDisplayMode.bothForList;
DateTime dummyDate = DateTime(2000);
String dateFormat = 'MM/dd/yy';
String? longDateFormat = 'MMMM dd, yyyy';
String timeFormat = 'HH:mm';
String? longTimeFormat = 'HH:mm:ss';
bool showConfirmationDialogOnCancel = true;
bool showConfirmationDialogOnDelete = true;
bool showConfirmationDialogOnStopEvent = true;
int lowGlucoseMgPerDl = 80;
int moderateGlucoseMgPerDl = 140;
int highGlucoseMgPerDl = 240;
double lowGlucoseMmolPerL = 4.44;
double moderateGlucoseMmolPerL = 7.77;
double highGlucoseMmolPerDl = 13.32;

View File

@ -19,9 +19,7 @@ import 'package:diameter/screens/meal/meal_source_detail.dart';
import 'package:diameter/screens/meal/meal_source_list.dart'; import 'package:diameter/screens/meal/meal_source_list.dart';
import 'package:diameter/settings.dart'; import 'package:diameter/settings.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:parse_server_sdk_flutter/parse_server_sdk.dart';
import 'package:diameter/screens/accuracy_list.dart'; import 'package:diameter/screens/accuracy_list.dart';
import 'package:diameter/config.dart';
import 'package:diameter/screens/basal/basal_profile_list.dart'; import 'package:diameter/screens/basal/basal_profile_list.dart';
import 'package:diameter/screens/bolus/bolus_profile_list.dart'; import 'package:diameter/screens/bolus/bolus_profile_list.dart';
import 'package:diameter/navigation.dart'; import 'package:diameter/navigation.dart';
@ -29,17 +27,6 @@ import 'package:diameter/navigation.dart';
late ObjectBox objectBox; late ObjectBox objectBox;
Future<void> main() async { Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();
await Parse().initialize(
keyApplicationId,
keyParseServerUrl,
clientKey: keyClientKey,
debug: true,
coreStore: await CoreStoreSharedPrefsImp.getInstance(),
);
Settings.loadSettingsIntoConfig();
objectBox = await ObjectBox.create(); objectBox = await ObjectBox.create();
runApp( runApp(

View File

@ -31,12 +31,11 @@ class Accuracy {
static void put(Accuracy accuracy) => box.put(accuracy); static void put(Accuracy accuracy) => box.put(accuracy);
static List<Accuracy> getAll() { static List<Accuracy> getAll() {
QueryBuilder<Accuracy> all = box QueryBuilder<Accuracy> all = box.query(Accuracy_.deleted.equals(false))
.query(Accuracy_.deleted.equals(false))
..order(Accuracy_.confidenceRating); ..order(Accuracy_.confidenceRating);
return all.build().find(); return all.build().find();
} }
static void remove(int id) { static void remove(int id) {
final item = box.get(id); final item = box.get(id);
if (item != null) { if (item != null) {
@ -44,21 +43,32 @@ class Accuracy {
box.put(item); box.put(item);
} }
} }
static List<Accuracy> getAllForPortionSize() { static List<Accuracy> getAllForPortionSize() {
QueryBuilder<Accuracy> allForPortionSize = box QueryBuilder<Accuracy> allForPortionSize = box.query(
.query(Accuracy_.forPortionSize.equals(true) & Accuracy_.deleted.equals(false)) Accuracy_.forPortionSize.equals(true) & Accuracy_.deleted.equals(false))
..order(Accuracy_.confidenceRating); ..order(Accuracy_.confidenceRating);
return allForPortionSize.build().find(); return allForPortionSize.build().find();
} }
static List<Accuracy> getAllForCarbsRatio() { static List<Accuracy> getAllForCarbsRatio() {
QueryBuilder<Accuracy> allForCarbsRatio = box QueryBuilder<Accuracy> allForCarbsRatio = box.query(
.query(Accuracy_.forCarbsRatio.equals(true) & Accuracy_.deleted.equals(false)) Accuracy_.forCarbsRatio.equals(true) & Accuracy_.deleted.equals(false))
..order(Accuracy_.confidenceRating); ..order(Accuracy_.confidenceRating);
return allForCarbsRatio.build().find(); return allForCarbsRatio.build().find();
} }
static void reorder(Accuracy accuracy, int? newPosition) {
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);
box.putMany(accuracies.map((item) {
item.confidenceRating = accuracies.indexOf(item);
return item;
}).toList());
}
@override @override
String toString() { String toString() {
return value; return value;

View File

@ -1,4 +1,5 @@
import 'package:diameter/main.dart'; import 'package:diameter/main.dart';
import 'package:diameter/models/log_event.dart';
import 'package:objectbox/objectbox.dart'; import 'package:objectbox/objectbox.dart';
import 'package:diameter/objectbox.g.dart' show BasalProfile_; import 'package:diameter/objectbox.g.dart' show BasalProfile_;
@ -52,6 +53,31 @@ class BasalProfile {
}).toList()); }).toList());
} }
static BasalProfile? getActive(DateTime? dateTime) {
if (dateTime != null) {
List<LogEvent> activeEvents = LogEvent.getAllActiveForTime(dateTime)
.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();
}
if (activeEvents.length == 1) {
return activeEvents.single.basalProfile.target;
}
}
Query<BasalProfile> query = box
.query(BasalProfile_.active
.equals(true)
.and(BasalProfile_.deleted.equals(false)))
.build();
final result = query.find();
return result.length != 1 ? null : result.single;
}
@override @override
String toString() { String toString() {
return name; return name;

View File

@ -1,4 +1,3 @@
import 'package:diameter/config.dart';
import 'package:diameter/main.dart'; import 'package:diameter/main.dart';
import 'package:diameter/models/bolus_profile.dart'; import 'package:diameter/models/bolus_profile.dart';
import 'package:diameter/utils/date_time_utils.dart'; import 'package:diameter/utils/date_time_utils.dart';

View File

@ -1,6 +1,6 @@
import 'package:diameter/config.dart';
import 'package:diameter/main.dart'; import 'package:diameter/main.dart';
import 'package:diameter/models/log_bolus.dart'; import 'package:diameter/models/log_bolus.dart';
import 'package:diameter/models/settings.dart';
import 'package:diameter/utils/date_time_utils.dart'; import 'package:diameter/utils/date_time_utils.dart';
import 'package:objectbox/objectbox.dart'; import 'package:objectbox/objectbox.dart';
import 'package:diameter/objectbox.g.dart' show LogEntry_; import 'package:diameter/objectbox.g.dart' show LogEntry_;
@ -43,8 +43,8 @@ class LogEntry {
static bool hasUncorrectedGlucose(int id) { static bool hasUncorrectedGlucose(int id) {
final entry = box.get(id); final entry = box.get(id);
if (((entry?.mgPerDl ?? 0) > moderateGlucoseMgPerDl || if (((entry?.mgPerDl ?? 0) > Settings.get().moderateGlucoseMgPerDl ||
(entry?.mmolPerL ?? 0) > moderateGlucoseMmolPerL)) { (entry?.mmolPerL ?? 0) > Settings.get().moderateGlucoseMmolPerL)) {
return !LogBolus.glucoseBolusForEntryExists(id); return !LogBolus.glucoseBolusForEntryExists(id);
} }
return false; return false;

View File

@ -3,7 +3,7 @@ import 'package:diameter/models/basal_profile.dart';
import 'package:diameter/models/bolus_profile.dart'; import 'package:diameter/models/bolus_profile.dart';
import 'package:diameter/models/log_event_type.dart'; import 'package:diameter/models/log_event_type.dart';
import 'package:objectbox/objectbox.dart'; import 'package:objectbox/objectbox.dart';
import 'package:diameter/objectbox.g.dart' show LogEvent_; import 'package:diameter/objectbox.g.dart' show LogEvent_, LogEventType_;
@Entity(uid: 4303325892753185970) @Entity(uid: 4303325892753185970)
class LogEvent { class LogEvent {
@ -20,6 +20,11 @@ class LogEvent {
int? reminderDuration; int? reminderDuration;
String? notes; String? notes;
@Transient()
String? title;
@Transient()
bool isEndEvent = false;
// relations // relations
final eventType = ToOne<LogEventType>(); final eventType = ToOne<LogEventType>();
final bolusProfile = ToOne<BolusProfile>(); final bolusProfile = ToOne<BolusProfile>();
@ -60,7 +65,7 @@ class LogEvent {
if (dateTime != null) { if (dateTime != null) {
QueryBuilder<LogEvent> builder = box.query( QueryBuilder<LogEvent> builder = box.query(
LogEvent_.hasEndTime.equals(true) & LogEvent_.deleted.equals(false)) LogEvent_.hasEndTime.equals(true) & LogEvent_.deleted.equals(false))
..order(LogEvent_.time); ..order(LogEvent_.time, flags: Order.descending);
final eventsWithEndTime = builder.build().find(); final eventsWithEndTime = builder.build().find();
return eventsWithEndTime.where((event) { return eventsWithEndTime.where((event) {
return (!dateTime.isBefore(event.time)) && return (!dateTime.isBefore(event.time)) &&
@ -70,21 +75,68 @@ class LogEvent {
return []; return [];
} }
static bool eventTypeExistsForTime(int id, DateTime? dateTime) {
QueryBuilder<LogEvent> builder = box.query(
LogEvent_.hasEndTime.equals(true) & LogEvent_.deleted.equals(false))
..order(LogEvent_.time, flags: Order.descending);
builder.link(LogEvent_.eventType, LogEventType_.id.equals(id));
final eventsWithEndTime = builder.build().find();
if (dateTime != null) {
return eventsWithEndTime.where((event) {
return (!dateTime.isBefore(event.time)) &&
!dateTime.isAfter(event.endTime ?? DateTime.now());
}).isNotEmpty;
}
return eventsWithEndTime.isNotEmpty;
}
static Map<DateTime, List<LogEvent>> getDailyEntryMap() { static Map<DateTime, List<LogEvent>> getDailyEntryMap() {
Map<DateTime, List<LogEvent>> dateMap = <DateTime, List<LogEvent>>{}; Map<DateTime, List<LogEvent>> dateMap = <DateTime, List<LogEvent>>{};
Map<DateTime, List<LogEvent>> sortedDateMap = <DateTime, List<LogEvent>>{};
QueryBuilder<LogEvent> allByDate = box QueryBuilder<LogEvent> allByDate = box
.query(LogEvent_.deleted.equals(false)) .query(LogEvent_.deleted.equals(false))
..order(LogEvent_.time, flags: Order.descending); ..order(LogEvent_.time, flags: Order.descending);
List<LogEvent> events = allByDate.build().find(); List<LogEvent> events = allByDate.build().find();
DateTime? date; DateTime? date;
for (LogEvent event in events) { for (LogEvent event in events) {
date = DateTime.utc(event.time.year, event.time.month, event.time.day); date = DateTime.utc(event.time.year, event.time.month, event.time.day);
dateMap.putIfAbsent(date, () => <LogEvent>[]).add(event); LogEvent startEvent = event;
startEvent.title =
'${event.toString()} ${event.hasEndTime ? '(Start)' : ''}';
dateMap.putIfAbsent(date, () => <LogEvent>[]).add(startEvent);
} }
return dateMap; QueryBuilder<LogEvent> allByEndDate = box
.query(LogEvent_.deleted.equals(false).and(LogEvent_.endTime.notNull()))
..order(LogEvent_.endTime, flags: Order.descending);
List<LogEvent> endEvents = allByEndDate.build().find();
for (LogEvent event in endEvents) {
date = DateTime.utc(
event.endTime!.year, event.endTime!.month, event.endTime!.day);
LogEvent endEvent = event;
endEvent.isEndEvent = true;
endEvent.title = '${event.toString()} (End)';
dateMap.putIfAbsent(date, () => <LogEvent>[]).add(endEvent);
}
final dates = dateMap.keys.toList();
dates.sort();
for (DateTime date in dates.reversed) {
dateMap[date]!.sort((LogEvent a, LogEvent b) {
final dateA = a.isEndEvent ? a.endTime : a.time;
final dateB = b.isEndEvent ? b.endTime : b.time;
return -(dateA!.compareTo(dateB!));
});
sortedDateMap
.putIfAbsent(date, () => <LogEvent>[])
.addAll(dateMap[date]!);
}
return sortedDateMap;
} }
@override @override

95
lib/models/settings.dart Normal file
View File

@ -0,0 +1,95 @@
import 'package:diameter/main.dart';
import 'package:objectbox/objectbox.dart';
enum GlucoseDisplayMode { activeOnly, bothForList, bothForDetail, both }
List<String> glucoseDisplayModeLabels = [
'activeOnly',
'bothForList',
'bothForDetail',
'both',
];
enum GlucoseMeasurement {
mgPerDl,
mmolPerL,
}
List<String> glucoseMeasurementSuffixes = [
'mg/dl',
'mmol/l',
];
List<String> glucoseMeasurementLabels = [
'mgPerDl',
'mmolPerL',
];
enum NutritionMeasurement {
grams,
ounces,
lbs,
}
List<String> nutritionMeasurementSuffixes = [
'g',
'oz',
'lbs',
];
List<String> nutritionMeasurementLabels = [
'grams',
'ounces',
'lbs',
];
@Entity(uid: 3989341091218179227)
class Settings {
static final Box<Settings> box = objectBox.store.box<Settings>();
// properties
int id;
NutritionMeasurement nutritionMeasurement;
GlucoseDisplayMode glucoseDisplayMode;
GlucoseMeasurement glucoseMeasurement;
String dateFormat;
String? longDateFormat;
String timeFormat;
String? longTimeFormat;
bool showConfirmationDialogOnCancel;
bool showConfirmationDialogOnDelete;
bool showConfirmationDialogOnStopEvent;
int lowGlucoseMgPerDl;
int moderateGlucoseMgPerDl;
int highGlucoseMgPerDl;
double lowGlucoseMmolPerL;
double moderateGlucoseMmolPerL;
double highGlucoseMmolPerDl;
// constructor
Settings({
this.id = 0,
this.nutritionMeasurement = NutritionMeasurement.grams,
this.glucoseDisplayMode = GlucoseDisplayMode.bothForList,
this.glucoseMeasurement = GlucoseMeasurement.mgPerDl,
this.dateFormat = 'MM/dd/yy',
this.longDateFormat = 'MMMM dd, yyyy',
this.timeFormat = 'HH:mm',
this.longTimeFormat = 'HH:mm:ss',
this.showConfirmationDialogOnCancel = true,
this.showConfirmationDialogOnDelete = true,
this.showConfirmationDialogOnStopEvent = true,
this.lowGlucoseMgPerDl = 80,
this.moderateGlucoseMgPerDl = 140,
this.highGlucoseMgPerDl = 240,
this.lowGlucoseMmolPerL = 4.44,
this.moderateGlucoseMmolPerL = 7.77,
this.highGlucoseMmolPerDl = 13.32,
});
// methods
static Settings get() => box.getAll().single;
static void put(Settings settings) => box.put(settings);
static void reset() {
box.removeAll();
box.put(Settings());
}
}

View File

@ -99,14 +99,6 @@ class _NavigationState extends State<Navigation> {
}, },
selected: widget.currentLocation == Routes.log, selected: widget.currentLocation == Routes.log,
), ),
ListTile(
title: const Text('Log Entry'),
leading: const Icon(Icons.description),
onTap: () {
selectDestination(Routes.logEntry);
},
selected: Routes.logEntryRoutes.contains(widget.currentLocation),
),
ListTile( ListTile(
title: const Text('Log Events'), title: const Text('Log Events'),
leading: const Icon(Icons.event), leading: const Icon(Icons.event),

View File

@ -791,9 +791,88 @@
} }
], ],
"relations": [] "relations": []
},
{
"id": "16:3989341091218179227",
"lastPropertyId": "14:3282706593658092097",
"name": "Settings",
"properties": [
{
"id": "1:7803753645747063723",
"name": "id",
"type": 6,
"flags": 1
},
{
"id": "2:4703380985530623101",
"name": "dateFormat",
"type": 9
},
{
"id": "3:2983395924801005937",
"name": "longDateFormat",
"type": 9
},
{
"id": "4:2579032794029389590",
"name": "timeFormat",
"type": 9
},
{
"id": "5:3970690908108519507",
"name": "longTimeFormat",
"type": 9
},
{
"id": "6:349893175332801783",
"name": "showConfirmationDialogOnCancel",
"type": 1
},
{
"id": "7:4049915860178079910",
"name": "showConfirmationDialogOnDelete",
"type": 1
},
{
"id": "8:3088241443557186512",
"name": "showConfirmationDialogOnStopEvent",
"type": 1
},
{
"id": "9:310032577683835406",
"name": "lowGlucoseMgPerDl",
"type": 6
},
{
"id": "10:596980591281311896",
"name": "moderateGlucoseMgPerDl",
"type": 6
},
{
"id": "11:5588897884422150510",
"name": "highGlucoseMgPerDl",
"type": 6
},
{
"id": "12:7638848982383620744",
"name": "lowGlucoseMmolPerL",
"type": 8
},
{
"id": "13:3633551763915044903",
"name": "moderateGlucoseMmolPerL",
"type": 8
},
{
"id": "14:3282706593658092097",
"name": "highGlucoseMmolPerDl",
"type": 8
}
],
"relations": []
} }
], ],
"lastEntityId": "15:291512798403320400", "lastEntityId": "16:3989341091218179227",
"lastIndexId": "28:4563029809754152081", "lastIndexId": "28:4563029809754152081",
"lastRelationId": "0:0", "lastRelationId": "0:0",
"lastSequenceId": "0:0", "lastSequenceId": "0:0",

View File

@ -23,6 +23,7 @@ import 'models/meal.dart';
import 'models/meal_category.dart'; import 'models/meal_category.dart';
import 'models/meal_portion_type.dart'; import 'models/meal_portion_type.dart';
import 'models/meal_source.dart'; import 'models/meal_source.dart';
import 'models/settings.dart';
export 'package:objectbox/objectbox.dart'; // so that callers only have to import this file export 'package:objectbox/objectbox.dart'; // so that callers only have to import this file
@ -789,6 +790,85 @@ final _entities = <ModelEntity>[
flags: 0) flags: 0)
], ],
relations: <ModelRelation>[], relations: <ModelRelation>[],
backlinks: <ModelBacklink>[]),
ModelEntity(
id: const IdUid(16, 3989341091218179227),
name: 'Settings',
lastPropertyId: const IdUid(14, 3282706593658092097),
flags: 0,
properties: <ModelProperty>[
ModelProperty(
id: const IdUid(1, 7803753645747063723),
name: 'id',
type: 6,
flags: 1),
ModelProperty(
id: const IdUid(2, 4703380985530623101),
name: 'dateFormat',
type: 9,
flags: 0),
ModelProperty(
id: const IdUid(3, 2983395924801005937),
name: 'longDateFormat',
type: 9,
flags: 0),
ModelProperty(
id: const IdUid(4, 2579032794029389590),
name: 'timeFormat',
type: 9,
flags: 0),
ModelProperty(
id: const IdUid(5, 3970690908108519507),
name: 'longTimeFormat',
type: 9,
flags: 0),
ModelProperty(
id: const IdUid(6, 349893175332801783),
name: 'showConfirmationDialogOnCancel',
type: 1,
flags: 0),
ModelProperty(
id: const IdUid(7, 4049915860178079910),
name: 'showConfirmationDialogOnDelete',
type: 1,
flags: 0),
ModelProperty(
id: const IdUid(8, 3088241443557186512),
name: 'showConfirmationDialogOnStopEvent',
type: 1,
flags: 0),
ModelProperty(
id: const IdUid(9, 310032577683835406),
name: 'lowGlucoseMgPerDl',
type: 6,
flags: 0),
ModelProperty(
id: const IdUid(10, 596980591281311896),
name: 'moderateGlucoseMgPerDl',
type: 6,
flags: 0),
ModelProperty(
id: const IdUid(11, 5588897884422150510),
name: 'highGlucoseMgPerDl',
type: 6,
flags: 0),
ModelProperty(
id: const IdUid(12, 7638848982383620744),
name: 'lowGlucoseMmolPerL',
type: 8,
flags: 0),
ModelProperty(
id: const IdUid(13, 3633551763915044903),
name: 'moderateGlucoseMmolPerL',
type: 8,
flags: 0),
ModelProperty(
id: const IdUid(14, 3282706593658092097),
name: 'highGlucoseMmolPerDl',
type: 8,
flags: 0)
],
relations: <ModelRelation>[],
backlinks: <ModelBacklink>[]) backlinks: <ModelBacklink>[])
]; ];
@ -812,7 +892,7 @@ Future<Store> openStore(
ModelDefinition getObjectBoxModel() { ModelDefinition getObjectBoxModel() {
final model = ModelInfo( final model = ModelInfo(
entities: _entities, entities: _entities,
lastEntityId: const IdUid(15, 291512798403320400), lastEntityId: const IdUid(16, 3989341091218179227),
lastIndexId: const IdUid(28, 4563029809754152081), lastIndexId: const IdUid(28, 4563029809754152081),
lastRelationId: const IdUid(0, 0), lastRelationId: const IdUid(0, 0),
lastSequenceId: const IdUid(0, 0), lastSequenceId: const IdUid(0, 0),
@ -1545,6 +1625,76 @@ ModelDefinition getObjectBoxModel() {
notes: const fb.StringReader() notes: const fb.StringReader()
.vTableGetNullable(buffer, rootOffset, 14)); .vTableGetNullable(buffer, rootOffset, 14));
return object;
}),
Settings: EntityDefinition<Settings>(
model: _entities[14],
toOneRelations: (Settings object) => [],
toManyRelations: (Settings object) => {},
getId: (Settings object) => object.id,
setId: (Settings object, int id) {
object.id = id;
},
objectToFB: (Settings object, fb.Builder fbb) {
final dateFormatOffset = fbb.writeString(object.dateFormat);
final longDateFormatOffset = object.longDateFormat == null
? null
: fbb.writeString(object.longDateFormat!);
final timeFormatOffset = fbb.writeString(object.timeFormat);
final longTimeFormatOffset = object.longTimeFormat == null
? null
: fbb.writeString(object.longTimeFormat!);
fbb.startTable(15);
fbb.addInt64(0, object.id);
fbb.addOffset(1, dateFormatOffset);
fbb.addOffset(2, longDateFormatOffset);
fbb.addOffset(3, timeFormatOffset);
fbb.addOffset(4, longTimeFormatOffset);
fbb.addBool(5, object.showConfirmationDialogOnCancel);
fbb.addBool(6, object.showConfirmationDialogOnDelete);
fbb.addBool(7, object.showConfirmationDialogOnStopEvent);
fbb.addInt64(8, object.lowGlucoseMgPerDl);
fbb.addInt64(9, object.moderateGlucoseMgPerDl);
fbb.addInt64(10, object.highGlucoseMgPerDl);
fbb.addFloat64(11, object.lowGlucoseMmolPerL);
fbb.addFloat64(12, object.moderateGlucoseMmolPerL);
fbb.addFloat64(13, object.highGlucoseMmolPerDl);
fbb.finish(fbb.endTable());
return object.id;
},
objectFromFB: (Store store, ByteData fbData) {
final buffer = fb.BufferContext(fbData);
final rootOffset = buffer.derefObject(0);
final object = Settings(
id: const fb.Int64Reader().vTableGet(buffer, rootOffset, 4, 0),
dateFormat:
const fb.StringReader().vTableGet(buffer, rootOffset, 6, ''),
longDateFormat: const fb.StringReader()
.vTableGetNullable(buffer, rootOffset, 8),
timeFormat:
const fb.StringReader().vTableGet(buffer, rootOffset, 10, ''),
longTimeFormat: const fb.StringReader()
.vTableGetNullable(buffer, rootOffset, 12),
showConfirmationDialogOnCancel: const fb.BoolReader()
.vTableGet(buffer, rootOffset, 14, false),
showConfirmationDialogOnDelete: const fb.BoolReader()
.vTableGet(buffer, rootOffset, 16, false),
showConfirmationDialogOnStopEvent: const fb.BoolReader()
.vTableGet(buffer, rootOffset, 18, false),
lowGlucoseMgPerDl:
const fb.Int64Reader().vTableGet(buffer, rootOffset, 20, 0),
moderateGlucoseMgPerDl:
const fb.Int64Reader().vTableGet(buffer, rootOffset, 22, 0),
highGlucoseMgPerDl:
const fb.Int64Reader().vTableGet(buffer, rootOffset, 24, 0),
lowGlucoseMmolPerL:
const fb.Float64Reader().vTableGet(buffer, rootOffset, 26, 0),
moderateGlucoseMmolPerL:
const fb.Float64Reader().vTableGet(buffer, rootOffset, 28, 0),
highGlucoseMmolPerDl:
const fb.Float64Reader().vTableGet(buffer, rootOffset, 30, 0));
return object; return object;
}) })
}; };
@ -2046,3 +2196,61 @@ class Accuracy_ {
static final deleted = static final deleted =
QueryBooleanProperty<Accuracy>(_entities[13].properties[6]); QueryBooleanProperty<Accuracy>(_entities[13].properties[6]);
} }
/// [Settings] entity fields to define ObjectBox queries.
class Settings_ {
/// see [Settings.id]
static final id = QueryIntegerProperty<Settings>(_entities[14].properties[0]);
/// see [Settings.dateFormat]
static final dateFormat =
QueryStringProperty<Settings>(_entities[14].properties[1]);
/// see [Settings.longDateFormat]
static final longDateFormat =
QueryStringProperty<Settings>(_entities[14].properties[2]);
/// see [Settings.timeFormat]
static final timeFormat =
QueryStringProperty<Settings>(_entities[14].properties[3]);
/// see [Settings.longTimeFormat]
static final longTimeFormat =
QueryStringProperty<Settings>(_entities[14].properties[4]);
/// see [Settings.showConfirmationDialogOnCancel]
static final showConfirmationDialogOnCancel =
QueryBooleanProperty<Settings>(_entities[14].properties[5]);
/// see [Settings.showConfirmationDialogOnDelete]
static final showConfirmationDialogOnDelete =
QueryBooleanProperty<Settings>(_entities[14].properties[6]);
/// see [Settings.showConfirmationDialogOnStopEvent]
static final showConfirmationDialogOnStopEvent =
QueryBooleanProperty<Settings>(_entities[14].properties[7]);
/// see [Settings.lowGlucoseMgPerDl]
static final lowGlucoseMgPerDl =
QueryIntegerProperty<Settings>(_entities[14].properties[8]);
/// see [Settings.moderateGlucoseMgPerDl]
static final moderateGlucoseMgPerDl =
QueryIntegerProperty<Settings>(_entities[14].properties[9]);
/// see [Settings.highGlucoseMgPerDl]
static final highGlucoseMgPerDl =
QueryIntegerProperty<Settings>(_entities[14].properties[10]);
/// see [Settings.lowGlucoseMmolPerL]
static final lowGlucoseMmolPerL =
QueryDoubleProperty<Settings>(_entities[14].properties[11]);
/// see [Settings.moderateGlucoseMmolPerL]
static final moderateGlucoseMmolPerL =
QueryDoubleProperty<Settings>(_entities[14].properties[12]);
/// see [Settings.highGlucoseMmolPerDl]
static final highGlucoseMmolPerDl =
QueryDoubleProperty<Settings>(_entities[14].properties[13]);
}

View File

@ -1,6 +1,6 @@
import 'package:diameter/components/detail.dart'; import 'package:diameter/components/detail.dart';
import 'package:diameter/components/dialogs.dart'; import 'package:diameter/components/dialogs.dart';
import 'package:diameter/config.dart'; import 'package:diameter/models/settings.dart';
import 'package:diameter/navigation.dart'; import 'package:diameter/navigation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:diameter/components/forms.dart'; import 'package:diameter/components/forms.dart';
@ -57,14 +57,16 @@ class _AccuracyDetailScreenState extends State<AccuracyDetailScreen> {
_isSaving = true; _isSaving = true;
}); });
if (_accuracyForm.currentState!.validate()) { if (_accuracyForm.currentState!.validate()) {
Accuracy.box.put(Accuracy( Accuracy accuracy = Accuracy(
id: widget.id, id: widget.id,
value: _valueController.text, value: _valueController.text,
forCarbsRatio: _forCarbsRatio, forCarbsRatio: _forCarbsRatio,
forPortionSize: _forPortionSize, forPortionSize: _forPortionSize,
confidenceRating: int.tryParse(_confidenceRatingController.text),
notes: _notesController.text, notes: _notesController.text,
)); );
Accuracy.box.put(accuracy);
Accuracy.reorder(
accuracy, int.tryParse(_confidenceRatingController.text));
Navigator.pop(context, '${_isNew ? 'New' : ''} Accuracy saved'); Navigator.pop(context, '${_isNew ? 'New' : ''} Accuracy saved');
} }
setState(() { setState(() {
@ -73,20 +75,20 @@ class _AccuracyDetailScreenState extends State<AccuracyDetailScreen> {
} }
void handleCancelAction() { void handleCancelAction() {
if (showConfirmationDialogOnCancel && if (Settings.get().showConfirmationDialogOnCancel &&
(_isNew && (_isNew &&
(_forCarbsRatio || (_forCarbsRatio ||
_forPortionSize || _forPortionSize ||
_valueController.text != '' || _valueController.text != '' ||
int.tryParse(_confidenceRatingController.text) != null || int.tryParse(_confidenceRatingController.text) != null ||
_notesController.text != '')) || _notesController.text != '')) ||
(!_isNew && (!_isNew &&
(_forCarbsRatio != _accuracy!.forCarbsRatio || (_forCarbsRatio != _accuracy!.forCarbsRatio ||
_forPortionSize != _accuracy!.forPortionSize || _forPortionSize != _accuracy!.forPortionSize ||
_accuracy!.value != _valueController.text || _accuracy!.value != _valueController.text ||
int.tryParse(_confidenceRatingController.text) != int.tryParse(_confidenceRatingController.text) !=
_accuracy!.confidenceRating || _accuracy!.confidenceRating ||
(_accuracy!.notes ?? '') != _notesController.text))) { (_accuracy!.notes ?? '') != _notesController.text))) {
Dialogs.showCancelConfirmationDialog( Dialogs.showCancelConfirmationDialog(
context: context, context: context,
isNew: _isNew, isNew: _isNew,

View File

@ -1,5 +1,5 @@
import 'package:diameter/components/dialogs.dart'; import 'package:diameter/components/dialogs.dart';
import 'package:diameter/config.dart'; import 'package:diameter/models/settings.dart';
import 'package:diameter/navigation.dart'; import 'package:diameter/navigation.dart';
import 'package:diameter/screens/accuracy_detail.dart'; import 'package:diameter/screens/accuracy_detail.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -46,7 +46,7 @@ class _AccuracyListScreenState extends State<AccuracyListScreen> {
} }
void handleDeleteAction(Accuracy accuracy) async { void handleDeleteAction(Accuracy accuracy) async {
if (showConfirmationDialogOnDelete) { if (Settings.get().showConfirmationDialogOnDelete) {
Dialogs.showConfirmationDialog( Dialogs.showConfirmationDialog(
context: context, context: context,
onConfirm: () => onDelete(accuracy), onConfirm: () => onDelete(accuracy),
@ -83,68 +83,75 @@ class _AccuracyListScreenState extends State<AccuracyListScreen> {
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[ children: <Widget>[
Expanded( Expanded(
child: _accuracies.isNotEmpty ? ListView.builder( child: _accuracies.isNotEmpty
padding: const EdgeInsets.only(top: 10.0), ? ReorderableListView.builder(
itemCount: _accuracies.length, padding: const EdgeInsets.only(top: 10.0),
itemBuilder: (context, index) { itemCount: _accuracies.length,
final accuracy = _accuracies[index]; onReorder: (oldIndex, newIndex) {
return ListTile( Accuracy.reorder(_accuracies[oldIndex], newIndex);
onTap: () { reload();
Navigator.push( },
context, itemBuilder: (context, index) {
MaterialPageRoute( final accuracy = _accuracies[index];
builder: (context) => return ListTile(
AccuracyDetailScreen(id: accuracy.id), key: Key(index.toString()),
), onTap: () {
).then((message) => reload(message: message)); Navigator.push(
}, context,
title: Text(accuracy.value), MaterialPageRoute(
leading: Row( builder: (context) =>
mainAxisSize: MainAxisSize.min, AccuracyDetailScreen(id: accuracy.id),
children: [ ),
IconButton( ).then((message) => reload(message: message));
icon: const Icon(Icons.reorder), },
onPressed: () { title: Text(accuracy.value),
// ignore: todo leading: Row(
// TODO: implement reordering mainAxisSize: MainAxisSize.min,
}, children: const [
), Icon(Icons.reorder),
], ],
),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
icon: Icon(
Icons.square_foot,
color: accuracy.forPortionSize
? Theme.of(context)
.toggleableActiveColor
: Theme.of(context).highlightColor,
),
onPressed: () =>
handleToggleForPortionSizeAction(
accuracy),
),
IconButton(
icon: Icon(
Icons.pie_chart,
color: accuracy.forCarbsRatio
? Theme.of(context)
.toggleableActiveColor
: Theme.of(context).highlightColor,
),
onPressed: () =>
handleToggleForCarbsRatioAction(
accuracy),
),
const SizedBox(width: 24),
IconButton(
icon: const Icon(Icons.delete),
onPressed: () =>
handleDeleteAction(accuracy),
)
],
),
);
})
: const Center(
child: Text('You have not created any Accuracies yet!'),
), ),
trailing: Row( ),
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
icon: Icon(
Icons.square_foot,
color: accuracy.forPortionSize
? Theme.of(context).toggleableActiveColor
: Theme.of(context).highlightColor,
),
onPressed: () => handleToggleForPortionSizeAction(accuracy),
),
IconButton(
icon: Icon(
Icons.pie_chart,
color: accuracy.forCarbsRatio
? Theme.of(context).toggleableActiveColor
: Theme.of(context).highlightColor,
),
onPressed: () => handleToggleForCarbsRatioAction(accuracy),
),
const SizedBox(width: 24),
IconButton(
icon: const Icon(Icons.delete),
onPressed: () => handleDeleteAction(accuracy),
)
],
),
);
}
) : const Center(
child: Text('You have not created any Accuracies yet!'),
),
),
], ],
), ),
floatingActionButton: FloatingActionButton( floatingActionButton: FloatingActionButton(

View File

@ -1,6 +1,6 @@
import 'package:diameter/components/detail.dart'; import 'package:diameter/components/detail.dart';
import 'package:diameter/components/dialogs.dart'; import 'package:diameter/components/dialogs.dart';
import 'package:diameter/config.dart'; import 'package:diameter/models/settings.dart';
import 'package:diameter/navigation.dart'; import 'package:diameter/navigation.dart';
import 'package:diameter/utils/date_time_utils.dart'; import 'package:diameter/utils/date_time_utils.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -151,7 +151,7 @@ class _BasalDetailScreenState extends State<BasalDetailScreen> {
} }
void handleCancelAction() { void handleCancelAction() {
if (showConfirmationDialogOnCancel && if (Settings.get().showConfirmationDialogOnCancel &&
((_isNew && ((_isNew &&
(_startTime.hour != (widget.suggestedStartTime?.hour ?? 0) || (_startTime.hour != (widget.suggestedStartTime?.hour ?? 0) ||
_endTime.hour != (widget.suggestedEndTime?.hour ?? 0) || _endTime.hour != (widget.suggestedEndTime?.hour ?? 0) ||

View File

@ -1,5 +1,5 @@
import 'package:diameter/components/dialogs.dart'; import 'package:diameter/components/dialogs.dart';
import 'package:diameter/config.dart'; import 'package:diameter/models/settings.dart';
import 'package:diameter/utils/date_time_utils.dart'; import 'package:diameter/utils/date_time_utils.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:diameter/models/basal.dart'; import 'package:diameter/models/basal.dart';
@ -57,7 +57,7 @@ class _BasalListScreenState extends State<BasalListScreen> {
} }
void handleDeleteAction(Basal basal) async { void handleDeleteAction(Basal basal) async {
if (showConfirmationDialogOnDelete) { if (Settings.get().showConfirmationDialogOnDelete) {
Dialogs.showConfirmationDialog( Dialogs.showConfirmationDialog(
context: context, context: context,
onConfirm: () => onDelete(basal), onConfirm: () => onDelete(basal),

View File

@ -1,7 +1,7 @@
import 'package:diameter/components/detail.dart'; import 'package:diameter/components/detail.dart';
import 'package:diameter/components/dialogs.dart'; import 'package:diameter/components/dialogs.dart';
import 'package:diameter/config.dart';
import 'package:diameter/models/basal.dart'; import 'package:diameter/models/basal.dart';
import 'package:diameter/models/settings.dart';
import 'package:diameter/navigation.dart'; import 'package:diameter/navigation.dart';
import 'package:diameter/screens/basal/basal_detail.dart'; import 'package:diameter/screens/basal/basal_detail.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -237,7 +237,7 @@ class _BasalProfileDetailScreenState extends State<BasalProfileDetailScreen> {
} }
void handleCancelAction() { void handleCancelAction() {
if (showConfirmationDialogOnCancel && if (Settings.get().showConfirmationDialogOnCancel &&
(_isNew && (_isNew &&
(_active != widget.active || (_active != widget.active ||
_nameController.text != '' || _nameController.text != '' ||

View File

@ -1,5 +1,6 @@
import 'package:diameter/components/dialogs.dart'; import 'package:diameter/components/dialogs.dart';
import 'package:diameter/config.dart'; import 'package:diameter/components/dropdown.dart';
import 'package:diameter/models/settings.dart';
import 'package:diameter/navigation.dart'; import 'package:diameter/navigation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:diameter/models/basal_profile.dart'; import 'package:diameter/models/basal_profile.dart';
@ -18,6 +19,8 @@ class _BasalProfileListScreenState extends State<BasalProfileListScreen> {
Widget banner = Container(); Widget banner = Container();
bool pickActiveProfileMode = false; bool pickActiveProfileMode = false;
final BasalProfile? _activeProfile = BasalProfile.getActive(DateTime.now());
void refresh({String? message}) { void refresh({String? message}) {
setState(() { setState(() {
pickActiveProfileMode = false; pickActiveProfileMode = false;
@ -77,7 +80,7 @@ class _BasalProfileListScreenState extends State<BasalProfileListScreen> {
} }
void handleDeleteAction(BasalProfile basalProfile) async { void handleDeleteAction(BasalProfile basalProfile) async {
if (showConfirmationDialogOnDelete) { if (Settings.get().showConfirmationDialogOnDelete) {
Dialogs.showConfirmationDialog( Dialogs.showConfirmationDialog(
context: context, context: context,
onConfirm: () => onDelete(basalProfile), onConfirm: () => onDelete(basalProfile),
@ -88,17 +91,24 @@ class _BasalProfileListScreenState extends State<BasalProfileListScreen> {
} }
} }
void onPickActive(BasalProfile basalProfile) { void onPickActive(BasalProfile? basalProfile) {
BasalProfile.setAllInactive; if (basalProfile != null) {
basalProfile.active = true; BasalProfile.setAllInactive;
BasalProfile.put(basalProfile); basalProfile.active = true;
refresh(message: '${basalProfile.name} has been set as your active Profile'); BasalProfile.put(basalProfile);
refresh(
message: '${basalProfile.name} has been set as your active Profile');
}
} }
void handlePickActiveProfileAction() { void handlePickActiveProfileAction() {
setState(() { setState(() {
banner = MaterialBanner( banner = MaterialBanner(
content: const Text('Click one of the profiles to active it.'), content: AutoCompleteDropdownButton(
items: _basalProfiles,
label: 'Default Basal Profile',
onChanged: onPickActive,
),
leading: const CircleAvatar(child: Icon(Icons.info)), leading: const CircleAvatar(child: Icon(Icons.info)),
forceActionsBelow: true, forceActionsBelow: true,
actions: [ actions: [
@ -116,8 +126,8 @@ class _BasalProfileListScreenState extends State<BasalProfileListScreen> {
Navigator.push( Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => BasalProfileDetailScreen( builder: (context) =>
id: basalProfile?.id ?? 0, active: active), BasalProfileDetailScreen(id: basalProfile?.id ?? 0, active: active),
), ),
).then((message) => refresh(message: message)); ).then((message) => refresh(message: message));
} }
@ -152,42 +162,42 @@ class _BasalProfileListScreenState extends State<BasalProfileListScreen> {
children: [ children: [
banner, banner,
Expanded( Expanded(
child: _basalProfiles.isNotEmpty ? ListView.builder( child: _basalProfiles.isNotEmpty
itemCount: _basalProfiles.length, ? ListView.builder(
itemBuilder: (context, index) { itemCount: _basalProfiles.length,
final basalProfile = _basalProfiles[index]; itemBuilder: (context, index) {
final basalProfile = _basalProfiles[index];
return ListTile( String activeProfileText = basalProfile.active
tileColor: basalProfile.active ? 'Default Profile'
? Colors.green.shade100 : basalProfile.id == _activeProfile?.id
: null, ? 'Current Active Profile'
onTap: () { : '';
pickActiveProfileMode if (activeProfileText != '' && (basalProfile.notes ?? '') != '') {
? onPickActive(basalProfile) activeProfileText += '\n';
: onEdit(basalProfile); }
}, return ListTile(
title: Text( selected: basalProfile.active || basalProfile.id == _activeProfile?.id,
basalProfile.name, onTap: () => onEdit(basalProfile),
), title: Text(basalProfile.name),
subtitle: Text(basalProfile.notes!), subtitle: Text('$activeProfileText${basalProfile.notes ?? ''}'),
trailing: Row( trailing: Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
IconButton( IconButton(
icon: const Icon( icon: const Icon(
Icons.delete, Icons.delete,
color: Colors.blue, color: Colors.blue,
),
onPressed: () => handleDeleteAction(basalProfile),
),
],
), ),
onPressed: () => );
handleDeleteAction(basalProfile), },
), )
], : const Center(
child: Text('You have not created any Basal Profiles yet!'),
), ),
);
},
) : const Center(
child: Text('You have not created any Basal Profiles yet!'),
),
), ),
], ],
), ),

View File

@ -1,8 +1,7 @@
import 'package:diameter/components/detail.dart'; import 'package:diameter/components/detail.dart';
import 'package:diameter/components/dialogs.dart'; import 'package:diameter/components/dialogs.dart';
import 'package:diameter/config.dart'; import 'package:diameter/models/settings.dart';
import 'package:diameter/navigation.dart'; import 'package:diameter/navigation.dart';
import 'package:diameter/settings.dart';
import 'package:diameter/utils/date_time_utils.dart'; import 'package:diameter/utils/date_time_utils.dart';
import 'package:diameter/utils/utils.dart'; import 'package:diameter/utils/utils.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -166,7 +165,7 @@ class _BolusDetailScreenState extends State<BolusDetailScreen> {
} }
void handleCancelAction() { void handleCancelAction() {
if (showConfirmationDialogOnCancel && if (Settings.get().showConfirmationDialogOnCancel &&
((_isNew && ((_isNew &&
(_startTime.hour != (widget.suggestedStartTime?.hour ?? 0) || (_startTime.hour != (widget.suggestedStartTime?.hour ?? 0) ||
_endTime.hour != (widget.suggestedEndTime?.hour ?? 0) || _endTime.hour != (widget.suggestedEndTime?.hour ?? 0) ||
@ -297,12 +296,7 @@ class _BolusDetailScreenState extends State<BolusDetailScreen> {
TextFormField( TextFormField(
decoration: InputDecoration( decoration: InputDecoration(
labelText: 'per carbs', labelText: 'per carbs',
suffixText: nutritionMeasurement == suffixText: nutritionMeasurementSuffixes[Settings.get().nutritionMeasurement.index],
NutritionMeasurement.grams
? 'g'
: nutritionMeasurement == NutritionMeasurement.ounces
? 'oz'
: '',
), ),
controller: _carbsController, controller: _carbsController,
keyboardType: keyboardType:
@ -316,9 +310,9 @@ class _BolusDetailScreenState extends State<BolusDetailScreen> {
), ),
Row( Row(
children: [ children: [
glucoseMeasurement == GlucoseMeasurement.mgPerDl || Settings.get().glucoseMeasurement == GlucoseMeasurement.mgPerDl ||
glucoseDisplayMode == GlucoseDisplayMode.both || Settings.get().glucoseDisplayMode == GlucoseDisplayMode.both ||
glucoseDisplayMode == Settings.get().glucoseDisplayMode ==
GlucoseDisplayMode.bothForDetail GlucoseDisplayMode.bothForDetail
? Expanded( ? Expanded(
child: TextFormField( child: TextFormField(
@ -343,8 +337,8 @@ class _BolusDetailScreenState extends State<BolusDetailScreen> {
), ),
) )
: Container(), : Container(),
glucoseDisplayMode == GlucoseDisplayMode.both || Settings.get().glucoseDisplayMode == GlucoseDisplayMode.both ||
glucoseDisplayMode == Settings.get().glucoseDisplayMode ==
GlucoseDisplayMode.bothForDetail GlucoseDisplayMode.bothForDetail
? IconButton( ? IconButton(
onPressed: () => convertBetweenMgPerDlAndMmolPerL( onPressed: () => convertBetweenMgPerDlAndMmolPerL(
@ -352,9 +346,9 @@ class _BolusDetailScreenState extends State<BolusDetailScreen> {
icon: const Icon(Icons.calculate), icon: const Icon(Icons.calculate),
) )
: Container(), : Container(),
glucoseMeasurement == GlucoseMeasurement.mmolPerL || Settings.get().glucoseMeasurement == GlucoseMeasurement.mmolPerL ||
glucoseDisplayMode == GlucoseDisplayMode.both || Settings.get().glucoseDisplayMode == GlucoseDisplayMode.both ||
glucoseDisplayMode == Settings.get().glucoseDisplayMode ==
GlucoseDisplayMode.bothForDetail GlucoseDisplayMode.bothForDetail
? Expanded( ? Expanded(
child: TextFormField( child: TextFormField(
@ -380,8 +374,8 @@ class _BolusDetailScreenState extends State<BolusDetailScreen> {
), ),
) )
: Container(), : Container(),
glucoseDisplayMode == GlucoseDisplayMode.both || Settings.get().glucoseDisplayMode == GlucoseDisplayMode.both ||
glucoseDisplayMode == Settings.get().glucoseDisplayMode ==
GlucoseDisplayMode.bothForDetail GlucoseDisplayMode.bothForDetail
? IconButton( ? IconButton(
onPressed: () => convertBetweenMgPerDlAndMmolPerL( onPressed: () => convertBetweenMgPerDlAndMmolPerL(

View File

@ -1,6 +1,5 @@
import 'package:diameter/components/dialogs.dart'; import 'package:diameter/components/dialogs.dart';
import 'package:diameter/config.dart'; import 'package:diameter/models/settings.dart';
import 'package:diameter/settings.dart';
import 'package:diameter/utils/date_time_utils.dart'; import 'package:diameter/utils/date_time_utils.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:diameter/models/bolus.dart'; import 'package:diameter/models/bolus.dart';
@ -55,7 +54,7 @@ class _BolusListScreenState extends State<BolusListScreen> {
} }
void handleDeleteAction(Bolus bolus) async { void handleDeleteAction(Bolus bolus) async {
if (showConfirmationDialogOnDelete) { if (Settings.get().showConfirmationDialogOnDelete) {
Dialogs.showConfirmationDialog( Dialogs.showConfirmationDialog(
context: context, context: context,
onConfirm: () => onDelete(bolus), onConfirm: () => onDelete(bolus),
@ -128,7 +127,7 @@ class _BolusListScreenState extends State<BolusListScreen> {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Text(
'${bolus.units} U per ${bolus.carbs}${nutritionMeasurement == NutritionMeasurement.grams ? ' g' : ' oz'} carbs/${glucoseMeasurement == GlucoseMeasurement.mgPerDl ? bolus.mgPerDl : bolus.mmolPerL} ${glucoseMeasurement == GlucoseMeasurement.mgPerDl ? 'mg/dl' : 'mmol/l'}'), '${bolus.units} U per ${bolus.carbs}${nutritionMeasurementSuffixes[Settings.get().nutritionMeasurement.index]} carbs/${Settings.get().glucoseMeasurement == GlucoseMeasurement.mgPerDl ? bolus.mgPerDl : bolus.mmolPerL} ${glucoseMeasurementSuffixes[Settings.get().glucoseMeasurement.index]}'),
error != null error != null
? Text(error, ? Text(error,
style: const TextStyle(color: Colors.red)) style: const TextStyle(color: Colors.red))

View File

@ -1,7 +1,7 @@
import 'package:diameter/components/detail.dart'; import 'package:diameter/components/detail.dart';
import 'package:diameter/components/dialogs.dart'; import 'package:diameter/components/dialogs.dart';
import 'package:diameter/config.dart';
import 'package:diameter/models/bolus.dart'; import 'package:diameter/models/bolus.dart';
import 'package:diameter/models/settings.dart';
import 'package:diameter/navigation.dart'; import 'package:diameter/navigation.dart';
import 'package:diameter/screens/bolus/bolus_detail.dart'; import 'package:diameter/screens/bolus/bolus_detail.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -238,7 +238,7 @@ class _BolusProfileDetailScreenState extends State<BolusProfileDetailScreen> {
} }
void handleCancelAction() { void handleCancelAction() {
if (showConfirmationDialogOnCancel && if (Settings.get().showConfirmationDialogOnCancel &&
(_isNew && (_isNew &&
(_active != widget.active || (_active != widget.active ||
_nameController.text != '' || _nameController.text != '' ||

View File

@ -1,5 +1,6 @@
import 'package:diameter/components/dialogs.dart'; import 'package:diameter/components/dialogs.dart';
import 'package:diameter/config.dart'; import 'package:diameter/components/dropdown.dart';
import 'package:diameter/models/settings.dart';
import 'package:diameter/navigation.dart'; import 'package:diameter/navigation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:diameter/models/bolus_profile.dart'; import 'package:diameter/models/bolus_profile.dart';
@ -18,6 +19,8 @@ class _BolusProfileListScreenState extends State<BolusProfileListScreen> {
Widget banner = Container(); Widget banner = Container();
bool pickActiveProfileMode = false; bool pickActiveProfileMode = false;
final BolusProfile? _activeProfile = BolusProfile.getActive(DateTime.now());
void reload({String? message}) { void reload({String? message}) {
setState(() { setState(() {
pickActiveProfileMode = false; pickActiveProfileMode = false;
@ -25,7 +28,7 @@ class _BolusProfileListScreenState extends State<BolusProfileListScreen> {
}); });
updateBanner(); updateBanner();
setState(() { setState(() {
if (message != null) { if (message != null) {
var snackBar = SnackBar( var snackBar = SnackBar(
@ -80,7 +83,7 @@ class _BolusProfileListScreenState extends State<BolusProfileListScreen> {
} }
void handleDeleteAction(BolusProfile bolusProfile) async { void handleDeleteAction(BolusProfile bolusProfile) async {
if (showConfirmationDialogOnDelete) { if (Settings.get().showConfirmationDialogOnDelete) {
Dialogs.showConfirmationDialog( Dialogs.showConfirmationDialog(
context: context, context: context,
onConfirm: () => onDelete(bolusProfile), onConfirm: () => onDelete(bolusProfile),
@ -91,18 +94,23 @@ class _BolusProfileListScreenState extends State<BolusProfileListScreen> {
} }
} }
void onPickActive(BolusProfile bolusProfile) { void onPickActive(BolusProfile? bolusProfile) {
BolusProfile.setAllInactive; if (bolusProfile != null) {
bolusProfile.active = true; BolusProfile.setAllInactive;
BolusProfile.put(bolusProfile); bolusProfile.active = true;
reload( BolusProfile.put(bolusProfile);
message: '${bolusProfile.name} has been set as your active Profile'); reload(message: '${bolusProfile.name} has been set as your active Profile');
}
} }
void handlePickActiveProfileAction() { void handlePickActiveProfileAction() {
setState(() { setState(() {
banner = MaterialBanner( banner = MaterialBanner(
content: const Text('Click one of the profiles to active it.'), content: AutoCompleteDropdownButton(
items: _bolusProfiles,
label: 'Default Basal Profile',
onChanged: onPickActive,
),
leading: const CircleAvatar(child: Icon(Icons.info)), leading: const CircleAvatar(child: Icon(Icons.info)),
forceActionsBelow: true, forceActionsBelow: true,
actions: [ actions: [
@ -120,8 +128,8 @@ class _BolusProfileListScreenState extends State<BolusProfileListScreen> {
Navigator.push( Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => BolusProfileDetailScreen( builder: (context) =>
id: bolusProfile?.id ?? 0, active: active), BolusProfileDetailScreen(id: bolusProfile?.id ?? 0, active: active),
), ),
).then((message) => reload(message: message)); ).then((message) => reload(message: message));
} }
@ -156,41 +164,45 @@ class _BolusProfileListScreenState extends State<BolusProfileListScreen> {
children: [ children: [
banner, banner,
Expanded( Expanded(
child: _bolusProfiles.isNotEmpty ? ListView.builder( child: _bolusProfiles.isNotEmpty
itemCount: _bolusProfiles.length, ? ListView.builder(
itemBuilder: (context, index) { itemCount: _bolusProfiles.length,
final bolusProfile = _bolusProfiles[index]; itemBuilder: (context, index) {
return ListTile( final bolusProfile = _bolusProfiles[index];
tileColor: bolusProfile.active String activeProfileText = bolusProfile.active
? Colors.green.shade100 ? 'Default Profile'
: null, : bolusProfile.id == _activeProfile?.id
onTap: () { ? 'Current Active Profile'
pickActiveProfileMode : '';
? onPickActive(bolusProfile) if (activeProfileText != '' && (bolusProfile.notes ?? '') != '') {
: onEdit(bolusProfile); activeProfileText += '\n';
}, }
title: Text( return ListTile(
bolusProfile.name, selected: bolusProfile.active || bolusProfile.id == _activeProfile?.id,
), onTap: () => onEdit(bolusProfile),
subtitle: Text(bolusProfile.notes!), title: Text(
trailing: Row( bolusProfile.name,
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
icon: const Icon(
Icons.delete,
color: Colors.blue,
), ),
onPressed: () => isThreeLine: activeProfileText != '' && (bolusProfile.notes ?? '') != '',
handleDeleteAction(bolusProfile), subtitle: Text('$activeProfileText${bolusProfile.notes ?? ''}'),
), trailing: Row(
], mainAxisSize: MainAxisSize.min,
children: [
IconButton(
icon: const Icon(
Icons.delete,
color: Colors.blue,
),
onPressed: () => handleDeleteAction(bolusProfile),
),
],
),
);
},
)
: const Center(
child: Text('You have not created any Bolus Profiles yet!'),
), ),
);
},
) : const Center(
child: Text('You have not created any Bolus Profiles yet!'),
),
), ),
], ],
), ),

View File

@ -1,11 +1,10 @@
import 'package:diameter/components/dialogs.dart'; import 'package:diameter/components/dialogs.dart';
import 'package:diameter/config.dart';
import 'package:diameter/models/log_bolus.dart'; import 'package:diameter/models/log_bolus.dart';
import 'package:diameter/models/log_entry.dart'; import 'package:diameter/models/log_entry.dart';
import 'package:diameter/models/log_meal.dart'; import 'package:diameter/models/log_meal.dart';
import 'package:diameter/models/settings.dart';
import 'package:diameter/navigation.dart'; import 'package:diameter/navigation.dart';
import 'package:diameter/screens/log/log_entry/log_entry.dart'; import 'package:diameter/screens/log/log_entry/log_entry.dart';
import 'package:diameter/settings.dart';
import 'package:diameter/utils/date_time_utils.dart'; import 'package:diameter/utils/date_time_utils.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -49,7 +48,7 @@ class _LogScreenState extends State<LogScreen> {
} }
void handleDeleteAction(LogEntry logEntry) async { void handleDeleteAction(LogEntry logEntry) async {
if (showConfirmationDialogOnDelete) { if (Settings.get().showConfirmationDialogOnDelete) {
Dialogs.showConfirmationDialog( Dialogs.showConfirmationDialog(
context: context, context: context,
onConfirm: () => onDelete(logEntry), onConfirm: () => onDelete(logEntry),
@ -71,165 +70,125 @@ class _LogScreenState extends State<LogScreen> {
), ),
drawer: const Navigation(currentLocation: LogScreen.routeName), drawer: const Navigation(currentLocation: LogScreen.routeName),
body: Column( body: Column(
mainAxisAlignment: MainAxisAlignment.center, children: [
children: <Widget>[
Expanded( Expanded(
child: _logEntryDailyMap.isNotEmpty child: _logEntryDailyMap.isNotEmpty
? SingleChildScrollView( ? ListView.builder(
child: ListView.builder( padding: const EdgeInsets.all(10.0),
shrinkWrap: true, shrinkWrap: true,
padding: const EdgeInsets.all(10.0), itemCount: _logEntryDailyMap.length,
itemCount: _logEntryDailyMap.length, itemBuilder: (context, dateIndex) {
itemBuilder: (context, dateIndex) { List<DateTime> dateList = _logEntryDailyMap.keys.toList();
List<DateTime> dateList = final date = dateList[dateIndex];
_logEntryDailyMap.keys.toList(); final entryList = _logEntryDailyMap[date];
final date = dateList[dateIndex]; final tiles = <Widget>[];
final entryList = _logEntryDailyMap[date]; for (LogEntry logEntry in entryList!) {
return ListBody( double bolus =
children: [ LogBolus.getTotalBolusForEntry(logEntry.id);
Text(DateTimeUtils.displayDate(date)), double carbs =
entryList != null && entryList.isNotEmpty LogMeal.getTotalCarbsForEntry(logEntry.id);
? ListView.builder( tiles.add(ListTile(
shrinkWrap: true, onTap: () {
itemCount: entryList.length, Navigator.push(
itemBuilder: (context, index) { context,
final logEntry = entryList[index]; MaterialPageRoute(
double bolus = builder: (context) =>
LogBolus.getTotalBolusForEntry( LogEntryScreen(id: logEntry.id),
logEntry.id); ),
double carbs = ).then((message) => reload(message: message));
LogMeal.getTotalCarbsForEntry( },
logEntry.id); title: Row(
return ListTile( mainAxisAlignment: MainAxisAlignment.start,
onTap: () { crossAxisAlignment: CrossAxisAlignment.start,
Navigator.push( children: [
context, Expanded(
MaterialPageRoute( child: Text(
builder: (context) => DateTimeUtils.displayTime(logEntry.time),
LogEntryScreen( ),
id: logEntry.id), ),
), Expanded(
).then((message) => child: Column(
reload(message: message)); children: logEntry.mgPerDl != null &&
}, (Settings.get().glucoseMeasurement ==
title: Row( GlucoseMeasurement.mgPerDl ||
mainAxisAlignment: MainAxisAlignment.start, Settings.get().glucoseDisplayMode ==
crossAxisAlignment: GlucoseDisplayMode.both ||
CrossAxisAlignment.start, Settings.get().glucoseDisplayMode ==
children: [ GlucoseDisplayMode
Expanded( .bothForList)
child: Text( ? [
DateTimeUtils.displayTime( Text(logEntry.mgPerDl.toString()),
logEntry.time), const Text(
), 'mg/dl',
), textScaleFactor: 0.75,
Expanded( ),
child: Column( ]
children: logEntry : [],
.mgPerDl != ),
null && ),
(glucoseMeasurement == GlucoseMeasurement.mgPerDl || Expanded(
glucoseDisplayMode == child: Column(
GlucoseDisplayMode children: logEntry.mmolPerL != null &&
.both || (Settings.get().glucoseMeasurement ==
glucoseDisplayMode == GlucoseMeasurement.mmolPerL ||
GlucoseDisplayMode Settings.get().glucoseDisplayMode ==
.bothForList) GlucoseDisplayMode.both ||
? [ Settings.get().glucoseDisplayMode ==
Text(logEntry GlucoseDisplayMode
.mgPerDl .bothForList)
.toString()), ? [
const Text( Text(logEntry.mmolPerL.toString()),
'mg/dl', const Text(
textScaleFactor: 'mmol/l',
0.75, textScaleFactor: 0.75,
), ),
] ]
: [], : [],
), ),
), ),
Expanded( Expanded(
child: Column( child: Column(
children: logEntry children: (bolus > 0)
.mmolPerL != ? [
null && Text(bolus.toStringAsPrecision(3)),
(glucoseMeasurement == GlucoseMeasurement.mmolPerL || const Text('U',
glucoseDisplayMode == textScaleFactor: 0.75),
GlucoseDisplayMode ]
.both || : [],
glucoseDisplayMode == ),
GlucoseDisplayMode ),
.bothForList) Expanded(
? [ child: Column(
Text(logEntry children: (carbs > 0)
.mmolPerL ? [
.toString()), Text(carbs.toStringAsPrecision(3)),
const Text( Text(
'mmol/l', nutritionMeasurementSuffixes[Settings.get().nutritionMeasurement.index],
textScaleFactor: textScaleFactor: 0.75),
0.75, ]
), : [],
] ),
: [], ),
), ],
), ),
Expanded( trailing: Row(
child: Column( mainAxisSize: MainAxisSize.min,
children: (bolus > 0) children: [
? [ IconButton(
Text(bolus onPressed: () => handleDeleteAction(logEntry),
.toStringAsPrecision( icon: const Icon(Icons.delete,
3)), color: Colors.blue),
const Text('U', )
textScaleFactor: ],
0.75), ),
] ));
: [],
), }
), return ListBody(
Expanded( children: <Widget>[Text(DateTimeUtils.displayDate(date))] + tiles,
child: Column(
children: (carbs > 0)
? [
Text(carbs
.toStringAsPrecision(
3)),
Text(
nutritionMeasurement ==
NutritionMeasurement
.grams
? 'g carbs'
: nutritionMeasurement ==
NutritionMeasurement
.ounces
? 'oz carbs'
: '',
textScaleFactor:
0.75),
]
: [],
),
),
],
),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
onPressed: () =>
handleDeleteAction(logEntry),
icon: const Icon(Icons.delete,
color: Colors.blue),
)
],
),
);
})
: Container(),
],
); );
}, },
),
) )
: const Center( : const Center(
child: Text('You have not created any Log Entries yet!'), child: Text('You have not created any Log Entries yet!'),

View File

@ -4,13 +4,12 @@ import 'package:diameter/components/detail.dart';
import 'package:diameter/components/dialogs.dart'; import 'package:diameter/components/dialogs.dart';
import 'package:diameter/components/dropdown.dart'; import 'package:diameter/components/dropdown.dart';
import 'package:diameter/components/forms.dart'; import 'package:diameter/components/forms.dart';
import 'package:diameter/config.dart';
import 'package:diameter/models/bolus.dart'; import 'package:diameter/models/bolus.dart';
import 'package:diameter/models/log_bolus.dart'; import 'package:diameter/models/log_bolus.dart';
import 'package:diameter/models/log_entry.dart'; import 'package:diameter/models/log_entry.dart';
import 'package:diameter/models/log_meal.dart'; import 'package:diameter/models/log_meal.dart';
import 'package:diameter/models/settings.dart';
import 'package:diameter/navigation.dart'; import 'package:diameter/navigation.dart';
import 'package:diameter/settings.dart';
import 'package:diameter/utils/utils.dart'; import 'package:diameter/utils/utils.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -34,8 +33,7 @@ class LogBolusDetailScreen extends StatefulWidget {
final int logEntryId; final int logEntryId;
final int id; final int id;
const LogBolusDetailScreen( const LogBolusDetailScreen({Key? key, this.logEntryId = 0, this.id = 0})
{Key? key, this.logEntryId = 0, this.id = 0})
: super(key: key); : super(key: key);
@override @override
@ -62,10 +60,14 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
final _delayController = TextEditingController(text: ''); final _delayController = TextEditingController(text: '');
final _notesController = TextEditingController(text: ''); final _notesController = TextEditingController(text: '');
final _delayedUnitsController = TextEditingController(text: '');
final _immediateUnitsController = TextEditingController(text: '');
bool _setManually = false; bool _setManually = false;
BolusType _bolusType = BolusType.meal; BolusType _bolusType = BolusType.meal;
LogMeal? _meal; LogMeal? _meal;
Bolus? _rate; Bolus? _rate;
double _delayPercentage = 0;
List<LogMeal> _logMeals = []; List<LogMeal> _logMeals = [];
@ -76,7 +78,7 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
_logEntry = LogEntry.get(widget.logEntryId); _logEntry = LogEntry.get(widget.logEntryId);
_logMeals = LogMeal.getAllForEntry(widget.logEntryId); _logMeals = LogMeal.getAllForEntry(widget.logEntryId);
if (widget.id != 0) { if (widget.id != 0) {
_carbsController.text = (_logBolus!.carbs ?? '').toString(); _carbsController.text = (_logBolus!.carbs ?? '').toString();
_delayController.text = (_logBolus!.delay ?? '').toString(); _delayController.text = (_logBolus!.delay ?? '').toString();
@ -87,27 +89,46 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
} }
_rate ??= Bolus.getRateForTime(_logEntry?.time); _rate ??= Bolus.getRateForTime(_logEntry?.time);
_mgPerDlCurrentController.text = _mgPerDlCurrentController.text = (_logBolus?.mgPerDlCurrent ??
(_logBolus?.mgPerDlCurrent ?? (LogEntry.hasUncorrectedGlucose(widget.logEntryId) ? _logEntry?.mgPerDl ?? 0 : 0)).toString(); (LogEntry.hasUncorrectedGlucose(widget.logEntryId)
? _logEntry?.mgPerDl ?? 0
: 0))
.toString();
_mgPerDlTargetController.text = _mgPerDlTargetController.text =
(_logBolus?.mgPerDlTarget ?? moderateGlucoseMgPerDl).toString(); (_logBolus?.mgPerDlTarget ?? Settings.get().moderateGlucoseMgPerDl).toString();
_mgPerDlCorrectionController.text = _mgPerDlCorrectionController.text = (_logBolus?.mgPerDlCorrection ??
(_logBolus?.mgPerDlCorrection ?? max((int.tryParse(_mgPerDlCurrentController.text) ?? 0) - (int.tryParse(_mgPerDlTargetController.text) ?? 0), 0)).toString(); max(
_mmolPerLCurrentController.text = (int.tryParse(_mgPerDlCurrentController.text) ?? 0) -
(_logBolus?.mmolPerLCurrent ?? (LogEntry.hasUncorrectedGlucose(widget.logEntryId) ? _logEntry?.mmolPerL ?? 0 : 0)).toString(); (int.tryParse(_mgPerDlTargetController.text) ?? 0),
0))
.toString();
_mmolPerLCurrentController.text = (_logBolus?.mmolPerLCurrent ??
(LogEntry.hasUncorrectedGlucose(widget.logEntryId)
? _logEntry?.mmolPerL ?? 0
: 0))
.toString();
_mmolPerLTargetController.text = _mmolPerLTargetController.text =
(_logBolus?.mmolPerLTarget ?? moderateGlucoseMmolPerL).toString(); (_logBolus?.mmolPerLTarget ?? Settings.get().moderateGlucoseMmolPerL).toString();
_mmolPerLCorrectionController.text = _mmolPerLCorrectionController.text = (_logBolus?.mmolPerLCorrection ??
(_logBolus?.mmolPerLCorrection ?? max((double.tryParse(_mmolPerLCurrentController.text) ?? 0) - (double.tryParse(_mmolPerLTargetController.text) ?? 0), 0)).toString(); max(
(double.tryParse(_mmolPerLCurrentController.text) ?? 0) -
(double.tryParse(_mmolPerLTargetController.text) ?? 0),
0))
.toString();
_unitsController.text = (_logBolus?.units ?? _unitsController.text = (_logBolus?.units ??
(_rate != null && !_setManually ? ((int.tryParse(_mgPerDlCorrectionController.text) ?? 0) / ((_rate!.mgPerDl ?? 0) / _rate!.units)) : 0) (_rate != null && !_setManually
).toString(); ? ((int.tryParse(_mgPerDlCorrectionController.text) ?? 0) /
((_rate!.mgPerDl ?? 0) / _rate!.units))
: 0))
.toString();
if (widget.id == 0 && LogEntry.hasUncorrectedGlucose(widget.logEntryId)) { if (widget.id == 0 && LogEntry.hasUncorrectedGlucose(widget.logEntryId)) {
_bolusType = BolusType.glucose; _bolusType = BolusType.glucose;
} }
updateDelayedRatio();
} }
void reload() { void reload() {
@ -119,6 +140,23 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
_isNew = _logBolus == null; _isNew = _logBolus == null;
} }
void updateDelayedRatio() {
if (_unitsController.text != '') {
setState(() {
_delayedUnitsController.text =
((double.tryParse(_unitsController.text) ?? 0) *
_delayPercentage /
100)
.toString();
_immediateUnitsController.text =
((double.tryParse(_unitsController.text) ?? 0) *
(100 - _delayPercentage) /
100)
.toString();
});
}
}
void onSelectMeal(LogMeal meal) { void onSelectMeal(LogMeal meal) {
setState(() { setState(() {
_meal = meal; _meal = meal;
@ -135,6 +173,18 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
_unitsController.text = ((double.tryParse(_carbsController.text) ?? 0) / _unitsController.text = ((double.tryParse(_carbsController.text) ?? 0) /
(_rate!.carbs / _rate!.units)) (_rate!.carbs / _rate!.units))
.toString(); .toString();
if (_unitsController.text != '') {
_delayedUnitsController.text =
((double.tryParse(_unitsController.text) ?? 0) *
_delayPercentage /
100)
.toString();
_immediateUnitsController.text =
((double.tryParse(_unitsController.text) ?? 0) *
(100 - _delayPercentage) /
100)
.toString();
}
} }
}); });
} }
@ -206,15 +256,41 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
_isSaving = true; _isSaving = true;
}); });
if (_logBolusForm.currentState!.validate()) { if (_logBolusForm.currentState!.validate()) {
LogBolus logBolus = LogBolus( LogBolus logBolus;
id: widget.id, LogBolus? delayedBolus;
units: double.tryParse(_unitsController.text) ?? 0,
delay: int.tryParse(_delayController.text), if ((int.tryParse(_delayController.text) ?? 0) != 0 &&
setManually: _setManually, _delayPercentage != 0 &&
notes: _notesController.text, _delayPercentage != 100) {
); logBolus = LogBolus(
id: widget.id,
units: double.tryParse(_immediateUnitsController.text) ?? 0,
setManually: _setManually,
notes: _notesController.text,
);
delayedBolus = LogBolus(
delay: int.tryParse(_delayController.text),
units: double.tryParse(_delayedUnitsController.text) ?? 0,
setManually: _setManually,
notes: _notesController.text,
);
} else {
logBolus = LogBolus(
id: widget.id,
units: double.tryParse(_unitsController.text) ?? 0,
delay: _delayPercentage == 100
? int.tryParse(_delayController.text)
: null,
setManually: _setManually,
notes: _notesController.text,
);
}
if (_bolusType == BolusType.meal) { if (_bolusType == BolusType.meal) {
logBolus.carbs = double.tryParse(_carbsController.text); logBolus.carbs = double.tryParse(_carbsController.text);
if (delayedBolus != null) {
delayedBolus.carbs = double.tryParse(_carbsController.text);
}
logBolus.mgPerDlCurrent = null; logBolus.mgPerDlCurrent = null;
logBolus.mmolPerLCurrent = null; logBolus.mmolPerLCurrent = null;
} else { } else {
@ -225,14 +301,37 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
logBolus.mgPerDlTarget = int.tryParse(_mgPerDlTargetController.text); logBolus.mgPerDlTarget = int.tryParse(_mgPerDlTargetController.text);
logBolus.mmolPerLTarget = logBolus.mmolPerLTarget =
double.tryParse(_mmolPerLTargetController.text); double.tryParse(_mmolPerLTargetController.text);
logBolus.mgPerDlCorrection = int.tryParse(_mgPerDlCorrectionController.text); logBolus.mgPerDlCorrection =
int.tryParse(_mgPerDlCorrectionController.text);
logBolus.mmolPerLCorrection = logBolus.mmolPerLCorrection =
double.tryParse(_mmolPerLCorrectionController.text); double.tryParse(_mmolPerLCorrectionController.text);
if (delayedBolus != null) {
delayedBolus.mgPerDlCurrent =
int.tryParse(_mgPerDlCurrentController.text);
delayedBolus.mmolPerLCurrent =
double.tryParse(_mmolPerLCurrentController.text);
delayedBolus.mgPerDlTarget =
int.tryParse(_mgPerDlTargetController.text);
delayedBolus.mmolPerLTarget =
double.tryParse(_mmolPerLTargetController.text);
delayedBolus.mgPerDlCorrection =
int.tryParse(_mgPerDlCorrectionController.text);
delayedBolus.mmolPerLCorrection =
double.tryParse(_mmolPerLCorrectionController.text);
}
} }
logBolus.logEntry.target = _logEntry; logBolus.logEntry.target = _logEntry;
logBolus.meal.target = _meal; logBolus.meal.target = _meal;
logBolus.rate.target = _rate; logBolus.rate.target = _rate;
LogBolus.put(logBolus); LogBolus.put(logBolus);
if (delayedBolus != null) {
delayedBolus.logEntry.target = _logEntry;
delayedBolus.meal.target = _meal;
delayedBolus.rate.target = _rate;
LogBolus.put(delayedBolus);
}
Navigator.pop(context, '${_isNew ? 'New' : ''} Bolus Saved'); Navigator.pop(context, '${_isNew ? 'New' : ''} Bolus Saved');
} }
setState(() { setState(() {
@ -241,16 +340,18 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
} }
void handleCancelAction() { void handleCancelAction() {
if (showConfirmationDialogOnCancel && if (Settings.get().showConfirmationDialogOnCancel &&
((_isNew && ((_isNew &&
(_unitsController.text != '' || (_carbsController.text != '' ||
_carbsController.text != '' || (_bolusType == BolusType.glucose &&
_mgPerDlCurrentController.text != '' || (_mgPerDlCurrentController.text !=
_mgPerDlTargetController.text != '' || (_logEntry?.mgPerDl.toString() ?? '') ||
_mgPerDlCorrectionController.text != '' || _mmolPerLCurrentController.text !=
_mmolPerLCurrentController.text != '' || (_logEntry?.mmolPerL.toString() ?? ''))) ||
_mmolPerLTargetController.text != '' || _mgPerDlTargetController.text !=
_mmolPerLCorrectionController.text != '' || Settings.get().moderateGlucoseMgPerDl.toString() ||
_mmolPerLTargetController.text !=
Settings.get().moderateGlucoseMmolPerL.toString() ||
_delayController.text != '' || _delayController.text != '' ||
_setManually || _setManually ||
_notesController.text != '')) || _notesController.text != '')) ||
@ -297,28 +398,39 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
FormWrapper( FormWrapper(
formState: _logBolusForm, formState: _logBolusForm,
fields: [ fields: [
TextFormField( Row(
decoration: const InputDecoration( children: [
labelText: 'Bolus Units', Expanded(
suffixText: ' U', child: TextFormField(
), decoration: const InputDecoration(
controller: _unitsController, labelText: 'Bolus Units',
onChanged: (_) { suffixText: ' U',
setState(() { ),
_setManually = true; controller: _unitsController,
}); onChanged: (_) {
}, setState(() {
keyboardType: _setManually = true;
const TextInputType.numberWithOptions(decimal: true), });
), updateDelayedRatio();
BooleanFormField( },
value: _setManually, keyboardType: const TextInputType.numberWithOptions(
label: 'set manually', decimal: true),
onChanged: (value) { ),
setState(() { ),
_setManually = value; Expanded(
}); child: BooleanFormField(
}, contentPadding: const EdgeInsets.only(
left: 10.0, right: 10.0, top: 10.0),
value: _setManually,
label: 'set manually',
onChanged: (value) {
setState(() {
_setManually = value;
});
},
),
),
],
), ),
Row( Row(
children: [ children: [
@ -350,11 +462,11 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
children: _bolusType == BolusType.glucose children: _bolusType == BolusType.glucose
? [ ? [
Row( Row(
children: glucoseMeasurement == children: Settings.get().glucoseMeasurement ==
GlucoseMeasurement.mgPerDl || GlucoseMeasurement.mgPerDl ||
glucoseDisplayMode == Settings.get().glucoseDisplayMode ==
GlucoseDisplayMode.both || GlucoseDisplayMode.both ||
glucoseDisplayMode == Settings.get().glucoseDisplayMode ==
GlucoseDisplayMode.bothForDetail GlucoseDisplayMode.bothForDetail
? [ ? [
Expanded( Expanded(
@ -408,9 +520,9 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
), ),
), ),
), ),
glucoseDisplayMode == Settings.get().glucoseDisplayMode ==
GlucoseDisplayMode.both || GlucoseDisplayMode.both ||
glucoseDisplayMode == Settings.get().glucoseDisplayMode ==
GlucoseDisplayMode.bothForDetail GlucoseDisplayMode.bothForDetail
? IconButton( ? IconButton(
onPressed: () => onChangeGlucose( onPressed: () => onChangeGlucose(
@ -424,11 +536,11 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
: [], : [],
), ),
Row( Row(
children: glucoseMeasurement == children: Settings.get().glucoseMeasurement ==
GlucoseMeasurement.mmolPerL || GlucoseMeasurement.mmolPerL ||
glucoseDisplayMode == Settings.get().glucoseDisplayMode ==
GlucoseDisplayMode.both || GlucoseDisplayMode.both ||
glucoseDisplayMode == Settings.get().glucoseDisplayMode ==
GlucoseDisplayMode.bothForDetail GlucoseDisplayMode.bothForDetail
? [ ? [
Expanded( Expanded(
@ -483,9 +595,9 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
), ),
), ),
), ),
glucoseDisplayMode == Settings.get().glucoseDisplayMode ==
GlucoseDisplayMode.both || GlucoseDisplayMode.both ||
glucoseDisplayMode == Settings.get().glucoseDisplayMode ==
GlucoseDisplayMode.bothForDetail GlucoseDisplayMode.bothForDetail
? IconButton( ? IconButton(
onPressed: () => onChangeGlucose( onPressed: () => onChangeGlucose(
@ -512,13 +624,7 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
TextFormField( TextFormField(
decoration: InputDecoration( decoration: InputDecoration(
labelText: 'Carbs', labelText: 'Carbs',
suffixText: nutritionMeasurement == suffixText: nutritionMeasurementSuffixes[Settings.get().nutritionMeasurement.index],
NutritionMeasurement.grams
? 'g'
: nutritionMeasurement ==
NutritionMeasurement.ounces
? 'oz'
: '',
), ),
controller: _carbsController, controller: _carbsController,
onChanged: (_) => onChangeCarbs(), onChanged: (_) => onChangeCarbs(),
@ -533,8 +639,64 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
suffixText: ' min', suffixText: ' min',
), ),
controller: _delayController, controller: _delayController,
onChanged: (value) => setState(() {}),
keyboardType: const TextInputType.numberWithOptions(), keyboardType: const TextInputType.numberWithOptions(),
), ),
(int.tryParse(_delayController.text) ?? 0) != 0
? Slider(
label: '${_delayPercentage.floor().toString()}%',
divisions: 100,
value: _delayPercentage,
min: 0,
max: 100,
onChanged: _delayController.text != ''
? (value) {
setState(() {
_delayPercentage = value;
});
updateDelayedRatio();
}
: null,
)
: Container(),
Row(
children: (int.tryParse(_delayController.text) ?? 0) != 0
? [
Expanded(
child: Padding(
padding: const EdgeInsets.only(right: 5.0),
child: TextFormField(
decoration: const InputDecoration(
labelText: 'Immediate Bolus',
suffixText: ' U',
),
controller: _immediateUnitsController,
readOnly: true,
enabled: (int.tryParse(_delayController.text) ??
0) !=
0,
),
),
),
Expanded(
child: Padding(
padding: const EdgeInsets.only(left: 5.0),
child: TextFormField(
decoration: const InputDecoration(
labelText: 'Delayed Bolus',
suffixText: ' U',
),
controller: _delayedUnitsController,
readOnly: true,
enabled: (int.tryParse(_delayController.text) ??
0) !=
0,
),
),
),
]
: [],
),
TextFormField( TextFormField(
controller: _notesController, controller: _notesController,
decoration: const InputDecoration( decoration: const InputDecoration(

View File

@ -1,10 +1,9 @@
import 'package:diameter/components/dialogs.dart'; import 'package:diameter/components/dialogs.dart';
import 'package:diameter/config.dart';
import 'package:diameter/models/log_bolus.dart'; import 'package:diameter/models/log_bolus.dart';
import 'package:diameter/models/log_entry.dart'; import 'package:diameter/models/log_entry.dart';
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_bolus_detail.dart';
import 'package:diameter/screens/log/log_entry/log_meal_detail.dart'; import 'package:diameter/screens/log/log_entry/log_meal_detail.dart';
import 'package:diameter/settings.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class LogBolusListScreen extends StatefulWidget { class LogBolusListScreen extends StatefulWidget {
@ -58,7 +57,7 @@ class _LogBolusListScreenState extends State<LogBolusListScreen> {
} }
void handleDeleteAction(LogBolus logBolus) async { void handleDeleteAction(LogBolus logBolus) async {
if (showConfirmationDialogOnDelete) { if (Settings.get().showConfirmationDialogOnDelete) {
Dialogs.showConfirmationDialog( Dialogs.showConfirmationDialog(
context: context, context: context,
onConfirm: () => onDelete(logBolus), onConfirm: () => onDelete(logBolus),
@ -92,12 +91,15 @@ class _LogBolusListScreenState extends State<LogBolusListScreen> {
itemCount: widget.logBoli.length, itemCount: widget.logBoli.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {
final bolus = widget.logBoli[index]; final bolus = widget.logBoli[index];
String titleText = '${bolus.units} U ${(bolus.delay ?? 0) != 0
? ' (delayed by ${bolus.delay} min)'
: ''}';
return ListTile( return ListTile(
onTap: () => handleEditAction(bolus), onTap: () => handleEditAction(bolus),
title: Text('${bolus.units} U'), title: Text(titleText),
subtitle: Text(bolus.carbs != null ? subtitle: Text(bolus.carbs != null ?
'for ${bolus.meal.target.toString()} (${bolus.carbs}${nutritionMeasurement == NutritionMeasurement.grams ? ' g' : ' oz'} carbs)' 'for ${bolus.meal.target.toString()} (${bolus.carbs}${nutritionMeasurementSuffixes[Settings.get().nutritionMeasurement.index]} carbs)'
: 'to correct ${glucoseMeasurement == GlucoseMeasurement.mgPerDl ? bolus.mgPerDlCorrection : bolus.mmolPerLCorrection} ${glucoseMeasurement == GlucoseMeasurement.mgPerDl ? 'mg/dl' : 'mmol/l'}'), : 'to correct ${Settings.get().glucoseMeasurement == GlucoseMeasurement.mgPerDl ? bolus.mgPerDlCorrection : bolus.mmolPerLCorrection} ${glucoseMeasurementSuffixes[Settings.get().glucoseMeasurement.index]}'),
trailing: Row( trailing: Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [

View File

@ -1,16 +1,15 @@
import 'package:diameter/components/detail.dart'; import 'package:diameter/components/detail.dart';
import 'package:diameter/components/dialogs.dart'; import 'package:diameter/components/dialogs.dart';
import 'package:diameter/components/forms.dart'; import 'package:diameter/components/forms.dart';
import 'package:diameter/config.dart';
import 'package:diameter/models/log_bolus.dart'; import 'package:diameter/models/log_bolus.dart';
import 'package:diameter/models/log_entry.dart'; import 'package:diameter/models/log_entry.dart';
import 'package:diameter/models/log_meal.dart'; import 'package:diameter/models/log_meal.dart';
import 'package:diameter/models/settings.dart';
import 'package:diameter/navigation.dart'; import 'package:diameter/navigation.dart';
import 'package:diameter/screens/log/log_entry/log_bolus_detail.dart'; import 'package:diameter/screens/log/log_entry/log_bolus_detail.dart';
import 'package:diameter/screens/log/log_entry/log_bolus_list.dart'; import 'package:diameter/screens/log/log_entry/log_bolus_list.dart';
import 'package:diameter/screens/log/log_entry/log_meal_detail.dart'; import 'package:diameter/screens/log/log_entry/log_meal_detail.dart';
import 'package:diameter/screens/log/log_entry/log_meal_list.dart'; import 'package:diameter/screens/log/log_entry/log_meal_list.dart';
import 'package:diameter/settings.dart';
import 'package:diameter/utils/date_time_utils.dart'; import 'package:diameter/utils/date_time_utils.dart';
import 'package:diameter/utils/utils.dart'; import 'package:diameter/utils/utils.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -175,7 +174,7 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
} }
void handleCancelAction() { void handleCancelAction() {
if (showConfirmationDialogOnCancel && if (Settings.get().showConfirmationDialogOnCancel &&
((_isNew && ((_isNew &&
(int.tryParse(_mgPerDlController.text) != null || (int.tryParse(_mgPerDlController.text) != null ||
double.tryParse(_mmolPerLController.text) != null || double.tryParse(_mmolPerLController.text) != null ||
@ -311,10 +310,10 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
), ),
Row( Row(
children: [ children: [
glucoseMeasurement == GlucoseMeasurement.mgPerDl || Settings.get().glucoseMeasurement == GlucoseMeasurement.mgPerDl ||
glucoseDisplayMode == Settings.get().glucoseDisplayMode ==
GlucoseDisplayMode.both || GlucoseDisplayMode.both ||
glucoseDisplayMode == Settings.get().glucoseDisplayMode ==
GlucoseDisplayMode.bothForDetail GlucoseDisplayMode.bothForDetail
? Expanded( ? Expanded(
child: TextFormField( child: TextFormField(
@ -341,8 +340,8 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
), ),
) )
: Container(), : Container(),
glucoseDisplayMode == GlucoseDisplayMode.both || Settings.get().glucoseDisplayMode == GlucoseDisplayMode.both ||
glucoseDisplayMode == Settings.get().glucoseDisplayMode ==
GlucoseDisplayMode.bothForDetail GlucoseDisplayMode.bothForDetail
? IconButton( ? IconButton(
onPressed: () => onPressed: () =>
@ -352,10 +351,10 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
icon: const Icon(Icons.calculate), icon: const Icon(Icons.calculate),
) )
: Container(), : Container(),
glucoseMeasurement == GlucoseMeasurement.mmolPerL || Settings.get().glucoseMeasurement == GlucoseMeasurement.mmolPerL ||
glucoseDisplayMode == Settings.get().glucoseDisplayMode ==
GlucoseDisplayMode.both || GlucoseDisplayMode.both ||
glucoseDisplayMode == Settings.get().glucoseDisplayMode ==
GlucoseDisplayMode.bothForDetail GlucoseDisplayMode.bothForDetail
? Expanded( ? Expanded(
child: TextFormField( child: TextFormField(
@ -383,8 +382,8 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
), ),
) )
: Container(), : Container(),
glucoseDisplayMode == GlucoseDisplayMode.both || Settings.get().glucoseDisplayMode == GlucoseDisplayMode.both ||
glucoseDisplayMode == Settings.get().glucoseDisplayMode ==
GlucoseDisplayMode.bothForDetail GlucoseDisplayMode.bothForDetail
? IconButton( ? IconButton(
onPressed: () => onPressed: () =>

View File

@ -2,15 +2,14 @@ import 'package:diameter/components/detail.dart';
import 'package:diameter/components/dialogs.dart'; import 'package:diameter/components/dialogs.dart';
import 'package:diameter/components/dropdown.dart'; import 'package:diameter/components/dropdown.dart';
import 'package:diameter/components/forms.dart'; import 'package:diameter/components/forms.dart';
import 'package:diameter/config.dart';
import 'package:diameter/models/accuracy.dart'; import 'package:diameter/models/accuracy.dart';
import 'package:diameter/models/log_meal.dart'; import 'package:diameter/models/log_meal.dart';
import 'package:diameter/models/meal.dart'; import 'package:diameter/models/meal.dart';
import 'package:diameter/models/meal_category.dart'; import 'package:diameter/models/meal_category.dart';
import 'package:diameter/models/meal_portion_type.dart'; import 'package:diameter/models/meal_portion_type.dart';
import 'package:diameter/models/meal_source.dart'; import 'package:diameter/models/meal_source.dart';
import 'package:diameter/models/settings.dart';
import 'package:diameter/navigation.dart'; import 'package:diameter/navigation.dart';
import 'package:diameter/settings.dart';
import 'package:diameter/utils/utils.dart'; import 'package:diameter/utils/utils.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -145,7 +144,7 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
} }
void handleCancelAction() { void handleCancelAction() {
if (showConfirmationDialogOnCancel && if (Settings.get().showConfirmationDialogOnCancel &&
((_isNew && ((_isNew &&
(_valueController.text != '' || (_valueController.text != '' ||
_meal != null || _meal != null ||
@ -325,12 +324,7 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
decoration: InputDecoration( decoration: InputDecoration(
labelText: 'Portion size', labelText: 'Portion size',
suffixText: suffixText:
nutritionMeasurement == NutritionMeasurement.grams nutritionMeasurementSuffixes[Settings.get().nutritionMeasurement.index],
? 'g'
: nutritionMeasurement ==
NutritionMeasurement.ounces
? 'oz'
: '',
alignLabelWithHint: true, alignLabelWithHint: true,
), ),
controller: _portionSizeController, controller: _portionSizeController,
@ -366,12 +360,7 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
decoration: InputDecoration( decoration: InputDecoration(
labelText: 'Carbs per portion', labelText: 'Carbs per portion',
suffixText: suffixText:
nutritionMeasurement == NutritionMeasurement.grams nutritionMeasurementSuffixes[Settings.get().nutritionMeasurement.index],
? 'g'
: nutritionMeasurement ==
NutritionMeasurement.ounces
? 'oz'
: '',
), ),
controller: _carbsPerPortionController, controller: _carbsPerPortionController,
keyboardType: const TextInputType.numberWithOptions( keyboardType: const TextInputType.numberWithOptions(

View File

@ -1,7 +1,7 @@
import 'package:diameter/components/dialogs.dart'; import 'package:diameter/components/dialogs.dart';
import 'package:diameter/config.dart';
import 'package:diameter/models/log_entry.dart'; import 'package:diameter/models/log_entry.dart';
import 'package:diameter/models/log_meal.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:diameter/screens/log/log_entry/log_meal_detail.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -53,7 +53,7 @@ class _LogMealListScreenState extends State<LogMealListScreen> {
} }
void handleDeleteAction(LogMeal meal) async { void handleDeleteAction(LogMeal meal) async {
if (showConfirmationDialogOnDelete) { if (Settings.get().showConfirmationDialogOnDelete) {
Dialogs.showConfirmationDialog( Dialogs.showConfirmationDialog(
context: context, context: context,
onConfirm: () => onDelete(meal), onConfirm: () => onDelete(meal),

View File

@ -2,11 +2,11 @@ import 'package:diameter/components/detail.dart';
import 'package:diameter/components/dialogs.dart'; import 'package:diameter/components/dialogs.dart';
import 'package:diameter/components/dropdown.dart'; import 'package:diameter/components/dropdown.dart';
import 'package:diameter/components/forms.dart'; import 'package:diameter/components/forms.dart';
import 'package:diameter/config.dart';
import 'package:diameter/models/basal_profile.dart'; import 'package:diameter/models/basal_profile.dart';
import 'package:diameter/models/bolus_profile.dart'; import 'package:diameter/models/bolus_profile.dart';
import 'package:diameter/models/log_event.dart'; import 'package:diameter/models/log_event.dart';
import 'package:diameter/models/log_event_type.dart'; import 'package:diameter/models/log_event_type.dart';
import 'package:diameter/models/settings.dart';
import 'package:diameter/navigation.dart'; import 'package:diameter/navigation.dart';
import 'package:diameter/utils/date_time_utils.dart'; import 'package:diameter/utils/date_time_utils.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -132,24 +132,63 @@ class _LogEventDetailScreenState extends State<LogEventDetailScreen> {
}); });
} }
Future<void> checkIfActiveEventOfTypeExistsBeforeSaving() async {
if (_eventType != null && LogEvent.eventTypeExistsForTime(_eventType!.id, _time)) {
await showDialog(
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?'),
actions: [
TextButton(
onPressed: () => Navigator.pop(context, 'DISCARD'),
child: const Text('DISCARD'),
),
TextButton(
onPressed: () => Navigator.pop(context, 'EDIT'),
child: const Text('KEEP EDITING'),
),
ElevatedButton(
onPressed: () => Navigator.pop(context, 'SAVE'),
child: const Text('SAVE'),
)
],
);
}).then((value) async {
if (value == 'DISCARD') {
Navigator.pop(context);
} else if (value == 'SAVE') {
onSave();
}
});
} else {
onSave();
}
}
void onSave() {
LogEvent event = LogEvent(
id: widget.id,
time: _time,
endTime: _endTime,
hasEndTime: _hasEndTime,
reminderDuration: int.tryParse(_reminderDurationController.text),
notes: _notesController.text,
);
event.eventType.target = _eventType;
event.basalProfile.target = _basalProfile;
event.bolusProfile.target = _bolusProfile;
LogEvent.put(event);
Navigator.pop(context, '${_isNew ? 'New' : ''} Event Saved');
}
void handleSaveAction() async { void handleSaveAction() async {
setState(() { setState(() {
_isSaving = true; _isSaving = true;
}); });
if (_logEventForm.currentState!.validate()) { if (_logEventForm.currentState!.validate()) {
LogEvent event = LogEvent( await checkIfActiveEventOfTypeExistsBeforeSaving();
id: widget.id,
time: _time,
endTime: _endTime,
hasEndTime: _hasEndTime,
reminderDuration: int.tryParse(_reminderDurationController.text),
notes: _notesController.text,
);
event.eventType.target = _eventType;
event.basalProfile.target = _basalProfile;
event.bolusProfile.target = _bolusProfile;
LogEvent.put(event);
Navigator.pop(context, '${_isNew ? 'New' : ''} Event Saved');
} }
setState(() { setState(() {
_isSaving = false; _isSaving = false;
@ -157,7 +196,7 @@ class _LogEventDetailScreenState extends State<LogEventDetailScreen> {
} }
void handleCancelAction() { void handleCancelAction() {
if (showConfirmationDialogOnCancel && if (Settings.get().showConfirmationDialogOnCancel &&
((_isNew && ((_isNew &&
(_notesController.text != '' || (_notesController.text != '' ||
_eventType != null || _eventType != null ||
@ -202,55 +241,47 @@ class _LogEventDetailScreenState extends State<LogEventDetailScreen> {
}, },
), ),
Row( Row(
children: [ children: [
Expanded( Expanded(
child: Padding( child: Padding(
padding: const EdgeInsets.only(right: 5), padding: const EdgeInsets.only(right: 5),
child: DateTimeFormField( child: DateTimeFormField(
date: _time, date: _time,
label: _hasEndTime ? 'Start Date' : 'Date', label: _hasEndTime ? 'Start Date' : 'Date',
controller: _dateController, controller: _dateController,
onChanged: (newTime) { onChanged: (newTime) {
if (newTime != null) { if (newTime != null) {
setState(() { setState(() {
_time = DateTime( _time = DateTime(newTime.year, newTime.month,
newTime.year, newTime.day, _time.hour, _time.minute);
newTime.month, });
newTime.day, updateTime();
_time.hour, }
_time.minute); },
}); ),
updateTime();
}
},
),
),
),
Expanded(
child: Padding(
padding: const EdgeInsets.only(left: 5),
child: TimeOfDayFormField(
time: TimeOfDay.fromDateTime(_time),
label: _hasEndTime ? 'Start Time' : 'Time',
controller: _timeController,
onChanged: (newTime) {
if (newTime != null) {
setState(() {
_time = DateTime(
_time.year,
_time.month,
_time.day,
newTime.hour,
newTime.minute);
});
updateTime();
}
},
),
),
),
],
), ),
),
Expanded(
child: Padding(
padding: const EdgeInsets.only(left: 5),
child: TimeOfDayFormField(
time: TimeOfDay.fromDateTime(_time),
label: _hasEndTime ? 'Start Time' : 'Time',
controller: _timeController,
onChanged: (newTime) {
if (newTime != null) {
setState(() {
_time = DateTime(_time.year, _time.month,
_time.day, newTime.hour, newTime.minute);
});
updateTime();
}
},
),
),
),
],
),
BooleanFormField( BooleanFormField(
value: _hasEndTime, value: _hasEndTime,
onChanged: (value) { onChanged: (value) {
@ -261,57 +292,59 @@ class _LogEventDetailScreenState extends State<LogEventDetailScreen> {
label: 'has end time', label: 'has end time',
), ),
Column( Column(
children: _hasEndTime? [ children: _hasEndTime
Row( ? [
children: [ Row(
Expanded( children: [
child: Padding( Expanded(
padding: const EdgeInsets.only(right: 5), child: Padding(
child: DateTimeFormField( padding: const EdgeInsets.only(right: 5),
date: _endTime ?? now, child: DateTimeFormField(
label: 'End Date', date: _endTime ?? now,
controller: _endDateController, label: 'End Date',
onChanged: (newTime) { controller: _endDateController,
if (newTime != null) { onChanged: (newTime) {
setState(() { if (newTime != null) {
_endTime = DateTime( setState(() {
newTime.year, _endTime = DateTime(
newTime.month, newTime.year,
newTime.day, newTime.month,
_endTime?.hour ?? 0, newTime.day,
_endTime?.minute ?? 0); _endTime?.hour ?? 0,
}); _endTime?.minute ?? 0);
updateEndTime(); });
} updateEndTime();
}, }
), },
),
),
),
Expanded(
child: Padding(
padding: const EdgeInsets.only(left: 5),
child: TimeOfDayFormField(
time: TimeOfDay.fromDateTime(
_endTime ?? now),
label: 'End Time',
controller: _endTimeController,
onChanged: (newTime) {
if (newTime != null) {
setState(() {
_endTime = DateTime(
_endTime?.year ?? now.year,
_endTime?.month ?? now.month,
_endTime?.day ?? now.day,
newTime.hour,
newTime.minute);
});
updateEndTime();
}
},
),
),
),
],
), ),
),
Expanded(
child: Padding(
padding: const EdgeInsets.only(left: 5),
child: TimeOfDayFormField(
time: TimeOfDay.fromDateTime(_endTime ?? now),
label: 'End Time',
controller: _endTimeController,
onChanged: (newTime) {
if (newTime != null) {
setState(() {
_endTime = DateTime(
_endTime?.year ?? now.year,
_endTime?.month ?? now.month,
_endTime?.day ?? now.day,
newTime.hour,
newTime.minute);
});
updateEndTime();
}
},
),
),
),
],
),
TextFormField( TextFormField(
controller: _reminderDurationController, controller: _reminderDurationController,
keyboardType: keyboardType:

View File

@ -1,6 +1,6 @@
import 'package:diameter/components/dialogs.dart'; import 'package:diameter/components/dialogs.dart';
import 'package:diameter/config.dart';
import 'package:diameter/models/log_event.dart'; import 'package:diameter/models/log_event.dart';
import 'package:diameter/models/settings.dart';
import 'package:diameter/screens/log/log_event/log_event_detail.dart'; import 'package:diameter/screens/log/log_event/log_event_detail.dart';
import 'package:diameter/utils/date_time_utils.dart'; import 'package:diameter/utils/date_time_utils.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -71,7 +71,7 @@ class _LogEventListScreenState extends State<LogEventListScreen> {
} }
void handleDeleteAction(LogEvent logEvent) async { void handleDeleteAction(LogEvent logEvent) async {
if (showConfirmationDialogOnDelete) { if (Settings.get().showConfirmationDialogOnDelete) {
Dialogs.showConfirmationDialog( Dialogs.showConfirmationDialog(
context: context, context: context,
onConfirm: () => onDelete(logEvent), onConfirm: () => onDelete(logEvent),
@ -89,7 +89,7 @@ class _LogEventListScreenState extends State<LogEventListScreen> {
} }
void handleStopAction(LogEvent event) async { void handleStopAction(LogEvent event) async {
if (showConfirmationDialogOnStopEvent) { if (Settings.get().showConfirmationDialogOnStopEvent) {
Dialogs.showConfirmationDialog( Dialogs.showConfirmationDialog(
context: context, context: context,
onConfirm: () => onStop(event), onConfirm: () => onStop(event),
@ -101,7 +101,6 @@ class _LogEventListScreenState extends State<LogEventListScreen> {
} }
} }
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
@ -113,73 +112,101 @@ class _LogEventListScreenState extends State<LogEventListScreen> {
), ),
drawer: const Navigation(currentLocation: LogEventListScreen.routeName), drawer: const Navigation(currentLocation: LogEventListScreen.routeName),
body: Column( body: Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
Expanded( Expanded(
child: _logEventDailyMap.isNotEmpty child: _logEventDailyMap.isNotEmpty
? SingleChildScrollView( ? ListView.builder(
child: ListView.builder( shrinkWrap: true,
shrinkWrap: true, padding: const EdgeInsets.all(10.0),
padding: const EdgeInsets.all(10.0), itemCount: _logEventDailyMap.length,
itemCount: _logEventDailyMap.length, itemBuilder: (context, dateIndex) {
itemBuilder: (context, dateIndex) { List<DateTime?> dateList = (_activeEvents.isNotEmpty
List<DateTime?> dateList = <DateTime?>[null] + ? <DateTime?>[null]
: <DateTime?>[]) +
_logEventDailyMap.keys.toList(); _logEventDailyMap.keys.toList();
final date = dateList[dateIndex]; final date = dateList[dateIndex];
final eventList = date != null ? _logEventDailyMap[date] : _activeEvents; final eventList = date != null
return ListBody( ? _logEventDailyMap[date]
children: [ : _activeEvents;
Text(DateTimeUtils.displayDate(date, fallback: 'Active Events')), final tiles = <Widget>[];
eventList != null && eventList.isNotEmpty if (eventList != null) {
? ListView.builder( for (LogEvent event in eventList) {
shrinkWrap: true, tiles.add(ListTile(
itemCount: eventList.length, onTap: () {
itemBuilder: (context, index) { handleEditAction(event);
final event = eventList[index]; },
return ListTile( title: Row(
onTap: () { crossAxisAlignment:
handleEditAction(event); CrossAxisAlignment.center,
}, mainAxisSize: MainAxisSize.max,
title: Row( children: [
mainAxisSize: MainAxisSize.max, Expanded(
children: [ child: Text(date == null
Expanded( ? DateTimeUtils
child: Text(event.eventType.target?.value ?? '')), .displayDateTime(
], event.time)
), : DateTimeUtils.displayTime(
subtitle: Text( event.isEndEvent
'${DateTimeUtils.displayDateTime(event.time)}${event.hasEndTime ? ' - ${DateTimeUtils.displayDateTime(event.endTime, fallback: '(ongoing)')}' : ''}'), ? event.endTime
trailing: Row( : event.time))),
mainAxisSize: MainAxisSize.min, const SizedBox(width: 24),
children: [ Expanded(
event.hasEndTime && event.endTime == null ? IconButton( child: Text(event.title ??
icon: const Icon( event.eventType.target?.value ??
Icons.stop, ''),
color: Colors.blue, ),
), ],
onPressed: () => handleStopAction(event), ),
) : Container(), trailing: Row(
const SizedBox(width: 24), mainAxisSize: MainAxisSize.min,
IconButton( children: [
icon: const Icon( event.hasEndTime &&
Icons.delete, event.endTime == null
color: Colors.blue, ? IconButton(
), icon: const Icon(
onPressed: () => handleDeleteAction(event), Icons.stop,
), color: Colors.blue,
], ),
), onPressed: () =>
); handleStopAction(event),
}) : Container(), )
], : const SizedBox(width: 50),
); IconButton(
}, icon: const Icon(
), Icons.edit,
) : const Center( color: Colors.blue,
child: Text('There are no Events!'), ),
), onPressed: () =>
), handleEditAction(event),
], ),
IconButton(
icon: const Icon(
Icons.delete,
color: Colors.blue,
),
onPressed: () =>
handleDeleteAction(event),
),
],
),
));
}
}
return eventList != null && eventList.isNotEmpty ? ListBody(
children: <Widget>[
Text(DateTimeUtils.displayDate(date,
fallback: 'Active Events'))
] + tiles,
) : Container();
},
)
: const Center(
child: Text('There are no Events!'),
),
),
],
), ),
floatingActionButton: FloatingActionButton( floatingActionButton: FloatingActionButton(
onPressed: handleAddNewEvent, onPressed: handleAddNewEvent,

View File

@ -2,10 +2,10 @@ import 'package:diameter/components/detail.dart';
import 'package:diameter/components/dialogs.dart'; import 'package:diameter/components/dialogs.dart';
import 'package:diameter/components/dropdown.dart'; import 'package:diameter/components/dropdown.dart';
import 'package:diameter/components/forms.dart'; import 'package:diameter/components/forms.dart';
import 'package:diameter/config.dart';
import 'package:diameter/models/basal_profile.dart'; import 'package:diameter/models/basal_profile.dart';
import 'package:diameter/models/bolus_profile.dart'; import 'package:diameter/models/bolus_profile.dart';
import 'package:diameter/models/log_event_type.dart'; import 'package:diameter/models/log_event_type.dart';
import 'package:diameter/models/settings.dart';
import 'package:diameter/navigation.dart'; import 'package:diameter/navigation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -90,7 +90,7 @@ class _EventTypeDetailScreenState extends State<EventTypeDetailScreen> {
void handleCancelAction() { void handleCancelAction() {
bool isNew = _logEventType == null; bool isNew = _logEventType == null;
if (showConfirmationDialogOnCancel && if (Settings.get().showConfirmationDialogOnCancel &&
((isNew && ((isNew &&
(_valueController.text != '' || (_valueController.text != '' ||
int.tryParse(_defaultReminderDurationController.text) != int.tryParse(_defaultReminderDurationController.text) !=

View File

@ -1,7 +1,7 @@
import 'package:diameter/components/detail.dart'; import 'package:diameter/components/detail.dart';
import 'package:diameter/components/dialogs.dart'; import 'package:diameter/components/dialogs.dart';
import 'package:diameter/components/forms.dart'; import 'package:diameter/components/forms.dart';
import 'package:diameter/config.dart'; import 'package:diameter/models/settings.dart';
import 'package:diameter/navigation.dart'; import 'package:diameter/navigation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:diameter/models/meal_category.dart'; import 'package:diameter/models/meal_category.dart';
@ -57,7 +57,7 @@ class _MealCategoryDetailScreenState extends State<MealCategoryDetailScreen> {
} }
void handleCancelAction() { void handleCancelAction() {
if (showConfirmationDialogOnCancel && if (Settings.get().showConfirmationDialogOnCancel &&
(_isNew && (_isNew &&
(_valueController.text != '' || _notesController.text != '')) || (_valueController.text != '' || _notesController.text != '')) ||
(!_isNew && (!_isNew &&

View File

@ -1,6 +1,5 @@
import 'package:diameter/components/dialogs.dart'; import 'package:diameter/components/dialogs.dart';
// import 'package:diameter/components/progress_indicator.dart'; import 'package:diameter/models/settings.dart';
import 'package:diameter/config.dart';
import 'package:diameter/navigation.dart'; import 'package:diameter/navigation.dart';
import 'package:diameter/screens/meal/meal_category_detail.dart'; import 'package:diameter/screens/meal/meal_category_detail.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -47,7 +46,7 @@ class _MealCategoryListScreenState extends State<MealCategoryListScreen> {
} }
void handleDeleteAction(MealCategory mealCategory) async { void handleDeleteAction(MealCategory mealCategory) async {
if (showConfirmationDialogOnDelete) { if (Settings.get().showConfirmationDialogOnDelete) {
Dialogs.showConfirmationDialog( Dialogs.showConfirmationDialog(
context: context, context: context,
onConfirm: () => onDelete(mealCategory), onConfirm: () => onDelete(mealCategory),

View File

@ -2,14 +2,13 @@ import 'package:diameter/components/detail.dart';
import 'package:diameter/components/dialogs.dart'; import 'package:diameter/components/dialogs.dart';
import 'package:diameter/components/dropdown.dart'; import 'package:diameter/components/dropdown.dart';
import 'package:diameter/components/forms.dart'; import 'package:diameter/components/forms.dart';
import 'package:diameter/config.dart';
import 'package:diameter/models/accuracy.dart'; import 'package:diameter/models/accuracy.dart';
import 'package:diameter/models/meal.dart'; import 'package:diameter/models/meal.dart';
import 'package:diameter/models/meal_category.dart'; import 'package:diameter/models/meal_category.dart';
import 'package:diameter/models/meal_portion_type.dart'; import 'package:diameter/models/meal_portion_type.dart';
import 'package:diameter/models/meal_source.dart'; import 'package:diameter/models/meal_source.dart';
import 'package:diameter/models/settings.dart';
import 'package:diameter/navigation.dart'; import 'package:diameter/navigation.dart';
import 'package:diameter/settings.dart';
import 'package:diameter/utils/utils.dart'; import 'package:diameter/utils/utils.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -122,7 +121,7 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
} }
void handleCancelAction() { void handleCancelAction() {
if (showConfirmationDialogOnCancel && if (Settings.get().showConfirmationDialogOnCancel &&
((_isNew && ((_isNew &&
(_valueController.text != '' || (_valueController.text != '' ||
_mealSource != null || _mealSource != null ||
@ -313,12 +312,7 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
decoration: InputDecoration( decoration: InputDecoration(
labelText: 'Portion size', labelText: 'Portion size',
suffixText: suffixText:
nutritionMeasurement == NutritionMeasurement.grams nutritionMeasurementSuffixes[Settings.get().nutritionMeasurement.index],
? 'g'
: nutritionMeasurement ==
NutritionMeasurement.ounces
? 'oz'
: '',
alignLabelWithHint: true, alignLabelWithHint: true,
), ),
controller: _portionSizeController, controller: _portionSizeController,
@ -354,12 +348,7 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
decoration: InputDecoration( decoration: InputDecoration(
labelText: 'Carbs per portion', labelText: 'Carbs per portion',
suffixText: suffixText:
nutritionMeasurement == NutritionMeasurement.grams nutritionMeasurementSuffixes[Settings.get().nutritionMeasurement.index],
? 'g'
: nutritionMeasurement ==
NutritionMeasurement.ounces
? 'oz'
: '',
), ),
controller: _carbsPerPortionController, controller: _carbsPerPortionController,
keyboardType: const TextInputType.numberWithOptions( keyboardType: const TextInputType.numberWithOptions(

View File

@ -1,6 +1,6 @@
import 'package:diameter/components/dialogs.dart'; import 'package:diameter/components/dialogs.dart';
import 'package:diameter/config.dart';
import 'package:diameter/models/meal.dart'; import 'package:diameter/models/meal.dart';
import 'package:diameter/models/settings.dart';
import 'package:diameter/navigation.dart'; import 'package:diameter/navigation.dart';
import 'package:diameter/screens/meal/meal_detail.dart'; import 'package:diameter/screens/meal/meal_detail.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -46,7 +46,7 @@ class _MealListScreenState extends State<MealListScreen> {
} }
void handleDeleteAction(Meal meal) async { void handleDeleteAction(Meal meal) async {
if (showConfirmationDialogOnDelete) { if (Settings.get().showConfirmationDialogOnDelete) {
Dialogs.showConfirmationDialog( Dialogs.showConfirmationDialog(
context: context, context: context,
onConfirm: () => onDelete(meal), onConfirm: () => onDelete(meal),

View File

@ -1,7 +1,7 @@
import 'package:diameter/components/detail.dart'; import 'package:diameter/components/detail.dart';
import 'package:diameter/components/dialogs.dart'; import 'package:diameter/components/dialogs.dart';
import 'package:diameter/components/forms.dart'; import 'package:diameter/components/forms.dart';
import 'package:diameter/config.dart'; import 'package:diameter/models/settings.dart';
import 'package:diameter/navigation.dart'; import 'package:diameter/navigation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:diameter/models/meal_portion_type.dart'; import 'package:diameter/models/meal_portion_type.dart';
@ -60,7 +60,7 @@ class _MealPortionTypeDetailScreenState
} }
void handleCancelAction() { void handleCancelAction() {
if (showConfirmationDialogOnCancel && if (Settings.get().showConfirmationDialogOnCancel &&
((_isNew && ((_isNew &&
(_valueController.text != '' || _notesController.text != '')) || (_valueController.text != '' || _notesController.text != '')) ||
(!_isNew && (!_isNew &&

View File

@ -1,5 +1,5 @@
import 'package:diameter/components/dialogs.dart'; import 'package:diameter/components/dialogs.dart';
import 'package:diameter/config.dart'; import 'package:diameter/models/settings.dart';
import 'package:diameter/navigation.dart'; import 'package:diameter/navigation.dart';
import 'package:diameter/screens/meal/meal_portion_type_detail.dart'; import 'package:diameter/screens/meal/meal_portion_type_detail.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -47,7 +47,7 @@ class _MealPortionTypeListScreenState extends State<MealPortionTypeListScreen> {
} }
void handleDeleteAction(MealPortionType mealPortionType) async { void handleDeleteAction(MealPortionType mealPortionType) async {
if (showConfirmationDialogOnDelete) { if (Settings.get().showConfirmationDialogOnDelete) {
Dialogs.showConfirmationDialog( Dialogs.showConfirmationDialog(
context: context, context: context,
onConfirm: () => onDelete(mealPortionType), onConfirm: () => onDelete(mealPortionType),

View File

@ -2,11 +2,11 @@ import 'package:diameter/components/detail.dart';
import 'package:diameter/components/dialogs.dart'; import 'package:diameter/components/dialogs.dart';
import 'package:diameter/components/dropdown.dart'; import 'package:diameter/components/dropdown.dart';
import 'package:diameter/components/forms.dart'; import 'package:diameter/components/forms.dart';
import 'package:diameter/config.dart';
import 'package:diameter/models/accuracy.dart'; import 'package:diameter/models/accuracy.dart';
import 'package:diameter/models/meal_category.dart'; import 'package:diameter/models/meal_category.dart';
import 'package:diameter/models/meal_portion_type.dart'; import 'package:diameter/models/meal_portion_type.dart';
import 'package:diameter/models/meal_source.dart'; import 'package:diameter/models/meal_source.dart';
import 'package:diameter/models/settings.dart';
import 'package:diameter/navigation.dart'; import 'package:diameter/navigation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -90,7 +90,7 @@ class _MealSourceDetailScreenState extends State<MealSourceDetailScreen> {
} }
void handleCancelAction() { void handleCancelAction() {
if (showConfirmationDialogOnCancel && if (Settings.get().showConfirmationDialogOnCancel &&
((_isNew && ((_isNew &&
(_valueController.text != '' || (_valueController.text != '' ||
_defaultCarbsRatioAccuracy != null || _defaultCarbsRatioAccuracy != null ||

View File

@ -1,6 +1,6 @@
import 'package:diameter/components/dialogs.dart'; import 'package:diameter/components/dialogs.dart';
import 'package:diameter/config.dart';
import 'package:diameter/models/meal_source.dart'; import 'package:diameter/models/meal_source.dart';
import 'package:diameter/models/settings.dart';
import 'package:diameter/navigation.dart'; import 'package:diameter/navigation.dart';
import 'package:diameter/screens/meal/meal_source_detail.dart'; import 'package:diameter/screens/meal/meal_source_detail.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -46,7 +46,7 @@ class _MealSourceListScreenState extends State<MealSourceListScreen> {
} }
void handleDeleteAction(MealSource mealSource) async { void handleDeleteAction(MealSource mealSource) async {
if (showConfirmationDialogOnDelete) { if (Settings.get().showConfirmationDialogOnDelete) {
Dialogs.showConfirmationDialog( Dialogs.showConfirmationDialog(
context: context, context: context,
onConfirm: () => onDelete(mealSource), onConfirm: () => onDelete(mealSource),

View File

@ -1,88 +1,9 @@
import 'package:diameter/components/dialogs.dart'; import 'package:diameter/components/dialogs.dart';
import 'package:diameter/components/dropdown.dart'; import 'package:diameter/components/dropdown.dart';
import 'package:diameter/components/forms.dart'; import 'package:diameter/components/forms.dart';
import 'package:diameter/config.dart'; import 'package:diameter/models/settings.dart';
import 'package:diameter/navigation.dart'; import 'package:diameter/navigation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
enum GlucoseDisplayMode { activeOnly, bothForList, bothForDetail, both }
enum GlucoseMeasurement {
mgPerDl,
mmolPerL,
}
enum NutritionMeasurement {
grams,
ounces,
cups,
}
class Settings {
static void loadSettingsIntoConfig() async {
nutritionMeasurement = await getNutritionMeasurement();
glucoseMeasurement = await getGlucoseMeasurement();
glucoseDisplayMode = await getGlucoseDisplayMode();
}
static Future<GlucoseDisplayMode> getGlucoseDisplayMode() async {
final settings = await SharedPreferences.getInstance();
int? index = settings.getInt('glucoseDisplayMode');
return index != null && index < GlucoseDisplayMode.values.length
? GlucoseDisplayMode.values[index]
: GlucoseDisplayMode.bothForList;
}
static Future<GlucoseMeasurement> getGlucoseMeasurement() async {
final settings = await SharedPreferences.getInstance();
int? index = settings.getInt('glucoseMeasurement');
return index != null && index < GlucoseMeasurement.values.length
? GlucoseMeasurement.values[index]
: GlucoseMeasurement.mgPerDl;
}
static Future<NutritionMeasurement> getNutritionMeasurement() async {
final settings = await SharedPreferences.getInstance();
int? index = settings.getInt('nutritionMeasurement');
return index != null && index < NutritionMeasurement.values.length
? NutritionMeasurement.values[index]
: NutritionMeasurement.grams;
}
static void setGlucoseDisplayMode(
GlucoseDisplayMode? glucoseDisplayMode) async {
final settings = await SharedPreferences.getInstance();
if (glucoseDisplayMode != null) {
settings.setInt('glucoseDisplayMode', glucoseDisplayMode.index);
}
}
static void setGlucoseMeasurement(
GlucoseMeasurement? glucoseMeasurement) async {
final settings = await SharedPreferences.getInstance();
if (glucoseMeasurement != null) {
settings.setInt('preferredGlucoseMeasurement', glucoseMeasurement.index);
}
}
static void setNutritionMeasurement(
NutritionMeasurement? nutritionMeasurement) async {
final settings = await SharedPreferences.getInstance();
if (nutritionMeasurement != null) {
settings.setInt(
'preferredNutritionMeasurement', nutritionMeasurement.index);
}
}
static void resetAll() async {
final settings = await SharedPreferences.getInstance();
settings.remove('glucoseDisplayMode');
settings.remove('preferredGlucoseMeasurement');
settings.remove('preferredNutritionMeasurement');
}
}
class SettingsScreen extends StatefulWidget { class SettingsScreen extends StatefulWidget {
static const String routeName = '/settings'; static const String routeName = '/settings';
@ -94,12 +15,73 @@ class SettingsScreen extends StatefulWidget {
} }
class _SettingsScreenState extends State<SettingsScreen> { class _SettingsScreenState extends State<SettingsScreen> {
final GlobalKey<FormState> _settingsForm = GlobalKey<FormState>(); late Settings _settings;
late bool _onlyDisplayActiveGlucoseMeasurement;
late bool _displayBothGlucoseMeasurementsInDetailView;
late bool _displayBothGlucoseMeasurementsInListView;
@override
void initState() {
super.initState();
reload();
}
void onReset() { void onReset() {
Settings.resetAll(); Settings.reset();
reload(message: 'Settings have been reset to default');
}
void saveSettings() {
Settings.put(Settings(
id: _settings.id,
nutritionMeasurement: _settings.nutritionMeasurement,
glucoseDisplayMode: _settings.glucoseDisplayMode,
glucoseMeasurement: _settings.glucoseMeasurement,
dateFormat: _settings.dateFormat,
longDateFormat: _settings.longDateFormat,
timeFormat: _settings.timeFormat,
longTimeFormat: _settings.longTimeFormat,
showConfirmationDialogOnCancel: _settings.showConfirmationDialogOnCancel,
showConfirmationDialogOnDelete: _settings.showConfirmationDialogOnDelete,
showConfirmationDialogOnStopEvent:
_settings.showConfirmationDialogOnStopEvent,
lowGlucoseMgPerDl: _settings.lowGlucoseMgPerDl,
moderateGlucoseMgPerDl: _settings.moderateGlucoseMgPerDl,
highGlucoseMgPerDl: _settings.highGlucoseMgPerDl,
lowGlucoseMmolPerL: _settings.lowGlucoseMmolPerL,
moderateGlucoseMmolPerL: _settings.moderateGlucoseMmolPerL,
highGlucoseMmolPerDl: _settings.highGlucoseMmolPerDl,
));
reload(message: 'Settings updated');
}
void reload({String? message}) {
setState(() { setState(() {
Settings.loadSettingsIntoConfig(); _settings = Settings.get();
});
setState(() {
_onlyDisplayActiveGlucoseMeasurement =
_settings.glucoseDisplayMode == GlucoseDisplayMode.activeOnly;
_displayBothGlucoseMeasurementsInDetailView =
_settings.glucoseDisplayMode == GlucoseDisplayMode.both ||
_settings.glucoseDisplayMode == GlucoseDisplayMode.bothForDetail;
_displayBothGlucoseMeasurementsInListView =
_settings.glucoseDisplayMode == GlucoseDisplayMode.both ||
_settings.glucoseDisplayMode == GlucoseDisplayMode.bothForList;
});
setState(() {
if (message != null) {
var snackBar = SnackBar(
content: Text(message),
duration: const Duration(seconds: 2),
);
ScaffoldMessenger.of(context)
..removeCurrentSnackBar()
..showSnackBar(snackBar);
}
}); });
} }
@ -111,12 +93,6 @@ class _SettingsScreenState extends State<SettingsScreen> {
); );
} }
@override
initState() {
super.initState();
Settings.loadSettingsIntoConfig();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
@ -126,88 +102,78 @@ class _SettingsScreenState extends State<SettingsScreen> {
drawer: const Navigation(currentLocation: SettingsScreen.routeName), drawer: const Navigation(currentLocation: SettingsScreen.routeName),
body: SingleChildScrollView( body: SingleChildScrollView(
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[ children: <Widget>[
FormWrapper( AutoCompleteDropdownButton<String>(
formState: _settingsForm, selectedItem: nutritionMeasurementLabels[
fields: [ _settings.nutritionMeasurement.index],
AutoCompleteDropdownButton<NutritionMeasurement>( label: 'Preferred Nutrition Measurement',
selectedItem: nutritionMeasurement, items: nutritionMeasurementLabels,
label: 'Preferred Nutrition Measurement', onChanged: (value) {
items: NutritionMeasurement.values, if (value != null) {
onChanged: (value) { _settings.nutritionMeasurement = NutritionMeasurement.values
if (value != null) { .elementAt(nutritionMeasurementLabels.indexOf(value));
Settings.setNutritionMeasurement(value); saveSettings();
setState(() { }
nutritionMeasurement = value; },
}); ),
} AutoCompleteDropdownButton<String>(
}, selectedItem:
), glucoseMeasurementLabels[_settings.glucoseMeasurement.index],
AutoCompleteDropdownButton<GlucoseMeasurement>( label: 'Preferred Glucose Measurement',
selectedItem: glucoseMeasurement, items: glucoseMeasurementLabels,
label: 'Preferred Glucose Measurement', onChanged: (value) {
items: GlucoseMeasurement.values, if (value != null) {
onChanged: (value) { _settings.glucoseMeasurement = GlucoseMeasurement.values
if (value != null) { .elementAt(glucoseMeasurementLabels.indexOf(value));
Settings.setGlucoseMeasurement(value); saveSettings();
setState(() { }
glucoseMeasurement = value; },
}); ),
} BooleanFormField(
}, value: _onlyDisplayActiveGlucoseMeasurement,
), label: 'only display active glucose measurement',
BooleanFormField( onChanged: (_) {
value: glucoseDisplayMode == GlucoseDisplayMode.activeOnly, GlucoseDisplayMode mode = _settings.glucoseDisplayMode ==
label: 'only display active glucose measurement', GlucoseDisplayMode.activeOnly
onChanged: (_) { ? GlucoseDisplayMode.both
GlucoseDisplayMode mode = : GlucoseDisplayMode.activeOnly;
glucoseDisplayMode == GlucoseDisplayMode.activeOnly _settings.glucoseDisplayMode = mode;
? GlucoseDisplayMode.both saveSettings();
: GlucoseDisplayMode.activeOnly; },
Settings.setGlucoseDisplayMode(mode); ),
setState(() { BooleanFormField(
glucoseDisplayMode = mode; value: _displayBothGlucoseMeasurementsInDetailView,
}); enabled:
}, !_onlyDisplayActiveGlucoseMeasurement,
), label: 'display both glucose measurements in detail view',
BooleanFormField( onChanged: (_) {
value: glucoseDisplayMode == GlucoseDisplayMode.both || GlucoseDisplayMode mode =
glucoseDisplayMode == GlucoseDisplayMode.bothForDetail, _settings.glucoseDisplayMode == GlucoseDisplayMode.both
enabled: glucoseDisplayMode != GlucoseDisplayMode.activeOnly,
label: 'display both glucose measurements in detail view',
onChanged: (_) {
GlucoseDisplayMode mode = glucoseDisplayMode ==
GlucoseDisplayMode.both
? GlucoseDisplayMode.bothForList ? GlucoseDisplayMode.bothForList
: glucoseDisplayMode == GlucoseDisplayMode.bothForList : _settings.glucoseDisplayMode ==
GlucoseDisplayMode.bothForList
? GlucoseDisplayMode.both ? GlucoseDisplayMode.both
: GlucoseDisplayMode.activeOnly; : GlucoseDisplayMode.activeOnly;
Settings.setGlucoseDisplayMode(mode); _settings.glucoseDisplayMode = mode;
setState(() { saveSettings();
glucoseDisplayMode = mode; },
}); ),
}, BooleanFormField(
), value: _displayBothGlucoseMeasurementsInListView,
BooleanFormField( enabled:
value: glucoseDisplayMode == GlucoseDisplayMode.both || !_onlyDisplayActiveGlucoseMeasurement,
glucoseDisplayMode == GlucoseDisplayMode.bothForList, label: 'display both glucose measurements in list view',
enabled: glucoseDisplayMode != GlucoseDisplayMode.activeOnly, onChanged: (_) {
label: 'display both glucose measurements in list view', GlucoseDisplayMode mode =
onChanged: (_) { _settings.glucoseDisplayMode == GlucoseDisplayMode.both
GlucoseDisplayMode mode = glucoseDisplayMode ==
GlucoseDisplayMode.both
? GlucoseDisplayMode.bothForDetail ? GlucoseDisplayMode.bothForDetail
: glucoseDisplayMode == GlucoseDisplayMode.bothForDetail : _settings.glucoseDisplayMode ==
GlucoseDisplayMode.bothForDetail
? GlucoseDisplayMode.both ? GlucoseDisplayMode.both
: GlucoseDisplayMode.activeOnly; : GlucoseDisplayMode.activeOnly;
Settings.setGlucoseDisplayMode(mode); _settings.glucoseDisplayMode = mode;
setState(() { saveSettings();
glucoseDisplayMode = mode; },
});
},
),
],
), ),
], ],
), ),

View File

@ -1,14 +1,16 @@
import 'package:diameter/config.dart'; import 'package:diameter/models/settings.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
final DateTime dummyDate = DateTime(2000);
class DateTimeUtils { class DateTimeUtils {
static String displayDateTime(DateTime? date, {String fallback = ''}) { static String displayDateTime(DateTime? date, {String fallback = ''}) {
if (date == null) { if (date == null) {
return fallback; return fallback;
} }
DateTime localDate = date.toLocal(); DateTime localDate = date.toLocal();
final DateFormat formatter = DateFormat('$dateFormat $timeFormat'); final DateFormat formatter = DateFormat('${Settings.get().dateFormat} ${Settings.get().timeFormat}');
return formatter.format(localDate); return formatter.format(localDate);
} }
@ -17,7 +19,7 @@ class DateTimeUtils {
return fallback; return fallback;
} }
DateTime localDate = date.toLocal(); DateTime localDate = date.toLocal();
final DateFormat formatter = DateFormat(longDateFormat ?? dateFormat); final DateFormat formatter = DateFormat(Settings.get().longDateFormat ?? Settings.get().dateFormat);
return formatter.format(localDate); return formatter.format(localDate);
} }
@ -28,7 +30,7 @@ class DateTimeUtils {
} }
DateTime localDate = date.toLocal(); DateTime localDate = date.toLocal();
final DateFormat formatter = DateFormat( final DateFormat formatter = DateFormat(
longFormat == true ? longTimeFormat ?? timeFormat : timeFormat); longFormat == true ? Settings.get().longTimeFormat ?? Settings.get().timeFormat : Settings.get().timeFormat);
return formatter.format(localDate); return formatter.format(localDate);
} }
@ -38,7 +40,7 @@ class DateTimeUtils {
return fallback; return fallback;
} }
final DateFormat formatter = DateFormat( final DateFormat formatter = DateFormat(
longFormat == true ? longTimeFormat ?? timeFormat : timeFormat); longFormat == true ? Settings.get().longTimeFormat ?? Settings.get().timeFormat : Settings.get().timeFormat);
return formatter.format(convertTimeOfDayToDateTime(time)); return formatter.format(convertTimeOfDayToDateTime(time));
} }

View File

@ -7,14 +7,14 @@ packages:
name: _fe_analyzer_shared name: _fe_analyzer_shared
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "30.0.0" version: "31.0.0"
analyzer: analyzer:
dependency: transitive dependency: transitive
description: description:
name: analyzer name: analyzer
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.7.0" version: "2.8.0"
args: args:
dependency: transitive dependency: transitive
description: description:
@ -63,14 +63,14 @@ packages:
name: build_resolvers name: build_resolvers
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.0.4" version: "2.0.5"
build_runner: build_runner:
dependency: "direct dev" dependency: "direct dev"
description: description:
name: build_runner name: build_runner
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.1.4" version: "2.1.5"
build_runner_core: build_runner_core:
dependency: transitive dependency: transitive
description: description:
@ -154,7 +154,7 @@ packages:
name: connectivity_plus_linux name: connectivity_plus_linux
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.1.0" version: "1.1.1"
connectivity_plus_macos: connectivity_plus_macos:
dependency: transitive dependency: transitive
description: description:
@ -168,7 +168,7 @@ packages:
name: connectivity_plus_platform_interface name: connectivity_plus_platform_interface
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.1.0" version: "1.1.1"
connectivity_plus_web: connectivity_plus_web:
dependency: transitive dependency: transitive
description: description:
@ -203,7 +203,7 @@ packages:
name: cupertino_icons name: cupertino_icons
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.0.3" version: "1.0.4"
dart_style: dart_style:
dependency: transitive dependency: transitive
description: description:
@ -217,14 +217,14 @@ packages:
name: dbus name: dbus
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.5.6" version: "0.6.6"
dio: dio:
dependency: transitive dependency: transitive
description: description:
name: dio name: dio
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "4.0.1" version: "4.0.4"
fake_async: fake_async:
dependency: transitive dependency: transitive
description: description:
@ -358,7 +358,7 @@ packages:
name: json_annotation name: json_annotation
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "4.3.0" version: "4.4.0"
lints: lints:
dependency: transitive dependency: transitive
description: description:
@ -414,28 +414,28 @@ packages:
name: nm name: nm
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.3.0" version: "0.4.1"
objectbox: objectbox:
dependency: "direct main" dependency: "direct main"
description: description:
name: objectbox name: objectbox
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.2.0" version: "1.3.0"
objectbox_flutter_libs: objectbox_flutter_libs:
dependency: "direct main" dependency: "direct main"
description: description:
name: objectbox_flutter_libs name: objectbox_flutter_libs
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.2.0" version: "1.3.0"
objectbox_generator: objectbox_generator:
dependency: "direct dev" dependency: "direct dev"
description: description:
name: objectbox_generator name: objectbox_generator
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.2.0" version: "1.3.0"
package_config: package_config:
dependency: transitive dependency: transitive
description: description:
@ -512,21 +512,35 @@ packages:
name: path_provider name: path_provider
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.0.6" version: "2.0.7"
path_provider_android:
dependency: transitive
description:
name: path_provider_android
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.9"
path_provider_ios:
dependency: transitive
description:
name: path_provider_ios
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.7"
path_provider_linux: path_provider_linux:
dependency: transitive dependency: transitive
description: description:
name: path_provider_linux name: path_provider_linux
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.1.0" version: "2.1.2"
path_provider_macos: path_provider_macos:
dependency: transitive dependency: transitive
description: description:
name: path_provider_macos name: path_provider_macos
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.0.2" version: "2.0.4"
path_provider_platform_interface: path_provider_platform_interface:
dependency: transitive dependency: transitive
description: description:
@ -540,14 +554,7 @@ packages:
name: path_provider_windows name: path_provider_windows
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.0.3" version: "2.0.4"
pedantic:
dependency: transitive
description:
name: pedantic
url: "https://pub.dartlang.org"
source: hosted
version: "1.11.1"
petitparser: petitparser:
dependency: transitive dependency: transitive
description: description:
@ -561,7 +568,7 @@ packages:
name: platform name: platform
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "3.0.2" version: "3.1.0"
plugin_platform_interface: plugin_platform_interface:
dependency: transitive dependency: transitive
description: description:
@ -624,6 +631,20 @@ packages:
name: shared_preferences name: shared_preferences
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.0.9"
shared_preferences_android:
dependency: transitive
description:
name: shared_preferences_android
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.9"
shared_preferences_ios:
dependency: transitive
description:
name: shared_preferences_ios
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.8" version: "2.0.8"
shared_preferences_linux: shared_preferences_linux:
dependency: transitive dependency: transitive
@ -631,7 +652,7 @@ packages:
name: shared_preferences_linux name: shared_preferences_linux
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.0.2" version: "2.0.3"
shared_preferences_macos: shared_preferences_macos:
dependency: transitive dependency: transitive
description: description:
@ -659,7 +680,7 @@ packages:
name: shared_preferences_windows name: shared_preferences_windows
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.0.2" version: "2.0.3"
shelf: shelf:
dependency: transitive dependency: transitive
description: description:
@ -685,7 +706,7 @@ packages:
name: source_gen name: source_gen
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.1.1" version: "1.2.0"
source_span: source_span:
dependency: transitive dependency: transitive
description: description:
@ -804,7 +825,7 @@ packages:
name: win32 name: win32
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.2.10" version: "2.3.1"
xdg_directories: xdg_directories:
dependency: transitive dependency: transitive
description: description: