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:
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:
add objectbox class and use instead of shared preferences
☐ fix settings saving
FUTURE TASKS:
General/Framework:
☐ make all fields readonly if user somehow gets to a deleted record detail view
☐ add functionality to delete dead records
☐ show indicator and make all fields readonly if user somehow gets to a deleted record detail view
☐ add functionality to delete dead records (meaning: set deleted flag and no relations)
☐ clean up controllers (dispose method of each stateful widget)
☐ add explanations to each section
☐ account for deleted/disabled elements in dropdowns
@ -33,20 +13,35 @@ FUTURE TASKS:
☐ add clear button to dropdown (or all text fields?)
☐ check through all detail forms and set required fields/according messages
☐ 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:
☐ add pagination
☐ apply target color settings to glucose
Log Entry:
☐ check if there is still an active bolus when suggesting glucose bolus
Event Types:
☐ add pagination
☐ add colors as indicators for log entries (and later graphs in reports)
☐ implement reminders as push notifications
Settings:
☐ add fields for preferred date and time formats
☐ 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 extra customization options (ie. changing pre calculated values)
☐ 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 setting for decimal places
☐ add field for active insulin duration
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)
✔ 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)

View File

@ -49,13 +49,15 @@ class BooleanFormField extends StatefulWidget {
final String label;
final void Function(bool) onChanged;
final bool? enabled;
final EdgeInsets? contentPadding;
const BooleanFormField(
{Key? key,
required this.value,
required this.label,
required this.onChanged,
this.enabled})
this.enabled,
this.contentPadding})
: super(key: key);
@override
@ -67,6 +69,7 @@ class _BooleanFormFieldState extends State<BooleanFormField> {
Widget build(BuildContext context) {
return FormField<bool>(builder: (state) {
return ListTile(
contentPadding: widget.contentPadding,
onTap: () => widget.onChanged(!widget.value),
trailing: Switch(
value: widget.value,
@ -115,7 +118,8 @@ class _DateTimeFormFieldState extends State<DateTimeFormField> {
context: context,
initialDate: widget.date,
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);
},
@ -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/settings.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/config.dart';
import 'package:diameter/screens/basal/basal_profile_list.dart';
import 'package:diameter/screens/bolus/bolus_profile_list.dart';
import 'package:diameter/navigation.dart';
@ -29,17 +27,6 @@ import 'package:diameter/navigation.dart';
late ObjectBox objectBox;
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await Parse().initialize(
keyApplicationId,
keyParseServerUrl,
clientKey: keyClientKey,
debug: true,
coreStore: await CoreStoreSharedPrefsImp.getInstance(),
);
Settings.loadSettingsIntoConfig();
objectBox = await ObjectBox.create();
runApp(

View File

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

View File

@ -1,4 +1,5 @@
import 'package:diameter/main.dart';
import 'package:diameter/models/log_event.dart';
import 'package:objectbox/objectbox.dart';
import 'package:diameter/objectbox.g.dart' show BasalProfile_;
@ -52,6 +53,31 @@ class BasalProfile {
}).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
String toString() {
return name;

View File

@ -1,4 +1,3 @@
import 'package:diameter/config.dart';
import 'package:diameter/main.dart';
import 'package:diameter/models/bolus_profile.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/models/log_bolus.dart';
import 'package:diameter/models/settings.dart';
import 'package:diameter/utils/date_time_utils.dart';
import 'package:objectbox/objectbox.dart';
import 'package:diameter/objectbox.g.dart' show LogEntry_;
@ -43,8 +43,8 @@ class LogEntry {
static bool hasUncorrectedGlucose(int id) {
final entry = box.get(id);
if (((entry?.mgPerDl ?? 0) > moderateGlucoseMgPerDl ||
(entry?.mmolPerL ?? 0) > moderateGlucoseMmolPerL)) {
if (((entry?.mgPerDl ?? 0) > Settings.get().moderateGlucoseMgPerDl ||
(entry?.mmolPerL ?? 0) > Settings.get().moderateGlucoseMmolPerL)) {
return !LogBolus.glucoseBolusForEntryExists(id);
}
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/log_event_type.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)
class LogEvent {
@ -20,6 +20,11 @@ class LogEvent {
int? reminderDuration;
String? notes;
@Transient()
String? title;
@Transient()
bool isEndEvent = false;
// relations
final eventType = ToOne<LogEventType>();
final bolusProfile = ToOne<BolusProfile>();
@ -60,7 +65,7 @@ class LogEvent {
if (dateTime != null) {
QueryBuilder<LogEvent> builder = box.query(
LogEvent_.hasEndTime.equals(true) & LogEvent_.deleted.equals(false))
..order(LogEvent_.time);
..order(LogEvent_.time, flags: Order.descending);
final eventsWithEndTime = builder.build().find();
return eventsWithEndTime.where((event) {
return (!dateTime.isBefore(event.time)) &&
@ -70,21 +75,68 @@ class LogEvent {
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() {
Map<DateTime, List<LogEvent>> dateMap = <DateTime, List<LogEvent>>{};
Map<DateTime, List<LogEvent>> sortedDateMap = <DateTime, List<LogEvent>>{};
QueryBuilder<LogEvent> allByDate = box
.query(LogEvent_.deleted.equals(false))
..order(LogEvent_.time, flags: Order.descending);
List<LogEvent> events = allByDate.build().find();
DateTime? date;
for (LogEvent event in events) {
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

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,
),
ListTile(
title: const Text('Log Entry'),
leading: const Icon(Icons.description),
onTap: () {
selectDestination(Routes.logEntry);
},
selected: Routes.logEntryRoutes.contains(widget.currentLocation),
),
ListTile(
title: const Text('Log Events'),
leading: const Icon(Icons.event),

View File

@ -791,9 +791,88 @@
}
],
"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",
"lastRelationId": "0:0",
"lastSequenceId": "0:0",

View File

@ -23,6 +23,7 @@ import 'models/meal.dart';
import 'models/meal_category.dart';
import 'models/meal_portion_type.dart';
import 'models/meal_source.dart';
import 'models/settings.dart';
export 'package:objectbox/objectbox.dart'; // so that callers only have to import this file
@ -789,6 +790,85 @@ final _entities = <ModelEntity>[
flags: 0)
],
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>[])
];
@ -812,7 +892,7 @@ Future<Store> openStore(
ModelDefinition getObjectBoxModel() {
final model = ModelInfo(
entities: _entities,
lastEntityId: const IdUid(15, 291512798403320400),
lastEntityId: const IdUid(16, 3989341091218179227),
lastIndexId: const IdUid(28, 4563029809754152081),
lastRelationId: const IdUid(0, 0),
lastSequenceId: const IdUid(0, 0),
@ -1545,6 +1625,76 @@ ModelDefinition getObjectBoxModel() {
notes: const fb.StringReader()
.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;
})
};
@ -2046,3 +2196,61 @@ class Accuracy_ {
static final deleted =
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/dialogs.dart';
import 'package:diameter/config.dart';
import 'package:diameter/models/settings.dart';
import 'package:diameter/navigation.dart';
import 'package:flutter/material.dart';
import 'package:diameter/components/forms.dart';
@ -57,14 +57,16 @@ class _AccuracyDetailScreenState extends State<AccuracyDetailScreen> {
_isSaving = true;
});
if (_accuracyForm.currentState!.validate()) {
Accuracy.box.put(Accuracy(
Accuracy accuracy = Accuracy(
id: widget.id,
value: _valueController.text,
forCarbsRatio: _forCarbsRatio,
forPortionSize: _forPortionSize,
confidenceRating: int.tryParse(_confidenceRatingController.text),
notes: _notesController.text,
));
);
Accuracy.box.put(accuracy);
Accuracy.reorder(
accuracy, int.tryParse(_confidenceRatingController.text));
Navigator.pop(context, '${_isNew ? 'New' : ''} Accuracy saved');
}
setState(() {
@ -73,20 +75,20 @@ class _AccuracyDetailScreenState extends State<AccuracyDetailScreen> {
}
void handleCancelAction() {
if (showConfirmationDialogOnCancel &&
(_isNew &&
(_forCarbsRatio ||
_forPortionSize ||
_valueController.text != '' ||
int.tryParse(_confidenceRatingController.text) != null ||
_notesController.text != '')) ||
(!_isNew &&
(_forCarbsRatio != _accuracy!.forCarbsRatio ||
_forPortionSize != _accuracy!.forPortionSize ||
_accuracy!.value != _valueController.text ||
int.tryParse(_confidenceRatingController.text) !=
_accuracy!.confidenceRating ||
(_accuracy!.notes ?? '') != _notesController.text))) {
if (Settings.get().showConfirmationDialogOnCancel &&
(_isNew &&
(_forCarbsRatio ||
_forPortionSize ||
_valueController.text != '' ||
int.tryParse(_confidenceRatingController.text) != null ||
_notesController.text != '')) ||
(!_isNew &&
(_forCarbsRatio != _accuracy!.forCarbsRatio ||
_forPortionSize != _accuracy!.forPortionSize ||
_accuracy!.value != _valueController.text ||
int.tryParse(_confidenceRatingController.text) !=
_accuracy!.confidenceRating ||
(_accuracy!.notes ?? '') != _notesController.text))) {
Dialogs.showCancelConfirmationDialog(
context: context,
isNew: _isNew,

View File

@ -1,5 +1,5 @@
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/screens/accuracy_detail.dart';
import 'package:flutter/material.dart';
@ -46,7 +46,7 @@ class _AccuracyListScreenState extends State<AccuracyListScreen> {
}
void handleDeleteAction(Accuracy accuracy) async {
if (showConfirmationDialogOnDelete) {
if (Settings.get().showConfirmationDialogOnDelete) {
Dialogs.showConfirmationDialog(
context: context,
onConfirm: () => onDelete(accuracy),
@ -83,68 +83,75 @@ class _AccuracyListScreenState extends State<AccuracyListScreen> {
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Expanded(
child: _accuracies.isNotEmpty ? ListView.builder(
padding: const EdgeInsets.only(top: 10.0),
itemCount: _accuracies.length,
itemBuilder: (context, index) {
final accuracy = _accuracies[index];
return ListTile(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
AccuracyDetailScreen(id: accuracy.id),
),
).then((message) => reload(message: message));
},
title: Text(accuracy.value),
leading: Row(
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
icon: const Icon(Icons.reorder),
onPressed: () {
// ignore: todo
// TODO: implement reordering
},
),
],
child: _accuracies.isNotEmpty
? ReorderableListView.builder(
padding: const EdgeInsets.only(top: 10.0),
itemCount: _accuracies.length,
onReorder: (oldIndex, newIndex) {
Accuracy.reorder(_accuracies[oldIndex], newIndex);
reload();
},
itemBuilder: (context, index) {
final accuracy = _accuracies[index];
return ListTile(
key: Key(index.toString()),
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
AccuracyDetailScreen(id: accuracy.id),
),
).then((message) => reload(message: message));
},
title: Text(accuracy.value),
leading: Row(
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(

View File

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

View File

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

View File

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

View File

@ -1,5 +1,6 @@
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:flutter/material.dart';
import 'package:diameter/models/basal_profile.dart';
@ -18,6 +19,8 @@ class _BasalProfileListScreenState extends State<BasalProfileListScreen> {
Widget banner = Container();
bool pickActiveProfileMode = false;
final BasalProfile? _activeProfile = BasalProfile.getActive(DateTime.now());
void refresh({String? message}) {
setState(() {
pickActiveProfileMode = false;
@ -77,7 +80,7 @@ class _BasalProfileListScreenState extends State<BasalProfileListScreen> {
}
void handleDeleteAction(BasalProfile basalProfile) async {
if (showConfirmationDialogOnDelete) {
if (Settings.get().showConfirmationDialogOnDelete) {
Dialogs.showConfirmationDialog(
context: context,
onConfirm: () => onDelete(basalProfile),
@ -88,17 +91,24 @@ class _BasalProfileListScreenState extends State<BasalProfileListScreen> {
}
}
void onPickActive(BasalProfile basalProfile) {
BasalProfile.setAllInactive;
basalProfile.active = true;
BasalProfile.put(basalProfile);
refresh(message: '${basalProfile.name} has been set as your active Profile');
void onPickActive(BasalProfile? basalProfile) {
if (basalProfile != null) {
BasalProfile.setAllInactive;
basalProfile.active = true;
BasalProfile.put(basalProfile);
refresh(
message: '${basalProfile.name} has been set as your active Profile');
}
}
void handlePickActiveProfileAction() {
setState(() {
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)),
forceActionsBelow: true,
actions: [
@ -116,8 +126,8 @@ class _BasalProfileListScreenState extends State<BasalProfileListScreen> {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => BasalProfileDetailScreen(
id: basalProfile?.id ?? 0, active: active),
builder: (context) =>
BasalProfileDetailScreen(id: basalProfile?.id ?? 0, active: active),
),
).then((message) => refresh(message: message));
}
@ -152,42 +162,42 @@ class _BasalProfileListScreenState extends State<BasalProfileListScreen> {
children: [
banner,
Expanded(
child: _basalProfiles.isNotEmpty ? ListView.builder(
itemCount: _basalProfiles.length,
itemBuilder: (context, index) {
final basalProfile = _basalProfiles[index];
return ListTile(
tileColor: basalProfile.active
? Colors.green.shade100
: null,
onTap: () {
pickActiveProfileMode
? onPickActive(basalProfile)
: onEdit(basalProfile);
},
title: Text(
basalProfile.name,
),
subtitle: Text(basalProfile.notes!),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
icon: const Icon(
Icons.delete,
color: Colors.blue,
child: _basalProfiles.isNotEmpty
? ListView.builder(
itemCount: _basalProfiles.length,
itemBuilder: (context, index) {
final basalProfile = _basalProfiles[index];
String activeProfileText = basalProfile.active
? 'Default Profile'
: basalProfile.id == _activeProfile?.id
? 'Current Active Profile'
: '';
if (activeProfileText != '' && (basalProfile.notes ?? '') != '') {
activeProfileText += '\n';
}
return ListTile(
selected: basalProfile.active || basalProfile.id == _activeProfile?.id,
onTap: () => onEdit(basalProfile),
title: Text(basalProfile.name),
subtitle: Text('$activeProfileText${basalProfile.notes ?? ''}'),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
icon: const Icon(
Icons.delete,
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/dialogs.dart';
import 'package:diameter/config.dart';
import 'package:diameter/models/settings.dart';
import 'package:diameter/navigation.dart';
import 'package:diameter/settings.dart';
import 'package:diameter/utils/date_time_utils.dart';
import 'package:diameter/utils/utils.dart';
import 'package:flutter/material.dart';
@ -166,7 +165,7 @@ class _BolusDetailScreenState extends State<BolusDetailScreen> {
}
void handleCancelAction() {
if (showConfirmationDialogOnCancel &&
if (Settings.get().showConfirmationDialogOnCancel &&
((_isNew &&
(_startTime.hour != (widget.suggestedStartTime?.hour ?? 0) ||
_endTime.hour != (widget.suggestedEndTime?.hour ?? 0) ||
@ -297,12 +296,7 @@ class _BolusDetailScreenState extends State<BolusDetailScreen> {
TextFormField(
decoration: InputDecoration(
labelText: 'per carbs',
suffixText: nutritionMeasurement ==
NutritionMeasurement.grams
? 'g'
: nutritionMeasurement == NutritionMeasurement.ounces
? 'oz'
: '',
suffixText: nutritionMeasurementSuffixes[Settings.get().nutritionMeasurement.index],
),
controller: _carbsController,
keyboardType:
@ -316,9 +310,9 @@ class _BolusDetailScreenState extends State<BolusDetailScreen> {
),
Row(
children: [
glucoseMeasurement == GlucoseMeasurement.mgPerDl ||
glucoseDisplayMode == GlucoseDisplayMode.both ||
glucoseDisplayMode ==
Settings.get().glucoseMeasurement == GlucoseMeasurement.mgPerDl ||
Settings.get().glucoseDisplayMode == GlucoseDisplayMode.both ||
Settings.get().glucoseDisplayMode ==
GlucoseDisplayMode.bothForDetail
? Expanded(
child: TextFormField(
@ -343,8 +337,8 @@ class _BolusDetailScreenState extends State<BolusDetailScreen> {
),
)
: Container(),
glucoseDisplayMode == GlucoseDisplayMode.both ||
glucoseDisplayMode ==
Settings.get().glucoseDisplayMode == GlucoseDisplayMode.both ||
Settings.get().glucoseDisplayMode ==
GlucoseDisplayMode.bothForDetail
? IconButton(
onPressed: () => convertBetweenMgPerDlAndMmolPerL(
@ -352,9 +346,9 @@ class _BolusDetailScreenState extends State<BolusDetailScreen> {
icon: const Icon(Icons.calculate),
)
: Container(),
glucoseMeasurement == GlucoseMeasurement.mmolPerL ||
glucoseDisplayMode == GlucoseDisplayMode.both ||
glucoseDisplayMode ==
Settings.get().glucoseMeasurement == GlucoseMeasurement.mmolPerL ||
Settings.get().glucoseDisplayMode == GlucoseDisplayMode.both ||
Settings.get().glucoseDisplayMode ==
GlucoseDisplayMode.bothForDetail
? Expanded(
child: TextFormField(
@ -380,8 +374,8 @@ class _BolusDetailScreenState extends State<BolusDetailScreen> {
),
)
: Container(),
glucoseDisplayMode == GlucoseDisplayMode.both ||
glucoseDisplayMode ==
Settings.get().glucoseDisplayMode == GlucoseDisplayMode.both ||
Settings.get().glucoseDisplayMode ==
GlucoseDisplayMode.bothForDetail
? IconButton(
onPressed: () => convertBetweenMgPerDlAndMmolPerL(

View File

@ -1,6 +1,5 @@
import 'package:diameter/components/dialogs.dart';
import 'package:diameter/config.dart';
import 'package:diameter/settings.dart';
import 'package:diameter/models/settings.dart';
import 'package:diameter/utils/date_time_utils.dart';
import 'package:flutter/material.dart';
import 'package:diameter/models/bolus.dart';
@ -55,7 +54,7 @@ class _BolusListScreenState extends State<BolusListScreen> {
}
void handleDeleteAction(Bolus bolus) async {
if (showConfirmationDialogOnDelete) {
if (Settings.get().showConfirmationDialogOnDelete) {
Dialogs.showConfirmationDialog(
context: context,
onConfirm: () => onDelete(bolus),
@ -128,7 +127,7 @@ class _BolusListScreenState extends State<BolusListScreen> {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
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
? Text(error,
style: const TextStyle(color: Colors.red))

View File

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

View File

@ -1,5 +1,6 @@
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:flutter/material.dart';
import 'package:diameter/models/bolus_profile.dart';
@ -18,6 +19,8 @@ class _BolusProfileListScreenState extends State<BolusProfileListScreen> {
Widget banner = Container();
bool pickActiveProfileMode = false;
final BolusProfile? _activeProfile = BolusProfile.getActive(DateTime.now());
void reload({String? message}) {
setState(() {
pickActiveProfileMode = false;
@ -25,7 +28,7 @@ class _BolusProfileListScreenState extends State<BolusProfileListScreen> {
});
updateBanner();
setState(() {
if (message != null) {
var snackBar = SnackBar(
@ -80,7 +83,7 @@ class _BolusProfileListScreenState extends State<BolusProfileListScreen> {
}
void handleDeleteAction(BolusProfile bolusProfile) async {
if (showConfirmationDialogOnDelete) {
if (Settings.get().showConfirmationDialogOnDelete) {
Dialogs.showConfirmationDialog(
context: context,
onConfirm: () => onDelete(bolusProfile),
@ -91,18 +94,23 @@ class _BolusProfileListScreenState extends State<BolusProfileListScreen> {
}
}
void onPickActive(BolusProfile bolusProfile) {
BolusProfile.setAllInactive;
bolusProfile.active = true;
BolusProfile.put(bolusProfile);
reload(
message: '${bolusProfile.name} has been set as your active Profile');
void onPickActive(BolusProfile? bolusProfile) {
if (bolusProfile != null) {
BolusProfile.setAllInactive;
bolusProfile.active = true;
BolusProfile.put(bolusProfile);
reload(message: '${bolusProfile.name} has been set as your active Profile');
}
}
void handlePickActiveProfileAction() {
setState(() {
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)),
forceActionsBelow: true,
actions: [
@ -120,8 +128,8 @@ class _BolusProfileListScreenState extends State<BolusProfileListScreen> {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => BolusProfileDetailScreen(
id: bolusProfile?.id ?? 0, active: active),
builder: (context) =>
BolusProfileDetailScreen(id: bolusProfile?.id ?? 0, active: active),
),
).then((message) => reload(message: message));
}
@ -156,41 +164,45 @@ class _BolusProfileListScreenState extends State<BolusProfileListScreen> {
children: [
banner,
Expanded(
child: _bolusProfiles.isNotEmpty ? ListView.builder(
itemCount: _bolusProfiles.length,
itemBuilder: (context, index) {
final bolusProfile = _bolusProfiles[index];
return ListTile(
tileColor: bolusProfile.active
? Colors.green.shade100
: null,
onTap: () {
pickActiveProfileMode
? onPickActive(bolusProfile)
: onEdit(bolusProfile);
},
title: Text(
bolusProfile.name,
),
subtitle: Text(bolusProfile.notes!),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
icon: const Icon(
Icons.delete,
color: Colors.blue,
child: _bolusProfiles.isNotEmpty
? ListView.builder(
itemCount: _bolusProfiles.length,
itemBuilder: (context, index) {
final bolusProfile = _bolusProfiles[index];
String activeProfileText = bolusProfile.active
? 'Default Profile'
: bolusProfile.id == _activeProfile?.id
? 'Current Active Profile'
: '';
if (activeProfileText != '' && (bolusProfile.notes ?? '') != '') {
activeProfileText += '\n';
}
return ListTile(
selected: bolusProfile.active || bolusProfile.id == _activeProfile?.id,
onTap: () => onEdit(bolusProfile),
title: Text(
bolusProfile.name,
),
onPressed: () =>
handleDeleteAction(bolusProfile),
),
],
isThreeLine: activeProfileText != '' && (bolusProfile.notes ?? '') != '',
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/config.dart';
import 'package:diameter/models/log_bolus.dart';
import 'package:diameter/models/log_entry.dart';
import 'package:diameter/models/log_meal.dart';
import 'package:diameter/models/settings.dart';
import 'package:diameter/navigation.dart';
import 'package:diameter/screens/log/log_entry/log_entry.dart';
import 'package:diameter/settings.dart';
import 'package:diameter/utils/date_time_utils.dart';
import 'package:flutter/material.dart';
@ -49,7 +48,7 @@ class _LogScreenState extends State<LogScreen> {
}
void handleDeleteAction(LogEntry logEntry) async {
if (showConfirmationDialogOnDelete) {
if (Settings.get().showConfirmationDialogOnDelete) {
Dialogs.showConfirmationDialog(
context: context,
onConfirm: () => onDelete(logEntry),
@ -71,165 +70,125 @@ class _LogScreenState extends State<LogScreen> {
),
drawer: const Navigation(currentLocation: LogScreen.routeName),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
children: [
Expanded(
child: _logEntryDailyMap.isNotEmpty
? SingleChildScrollView(
child: ListView.builder(
shrinkWrap: true,
padding: const EdgeInsets.all(10.0),
itemCount: _logEntryDailyMap.length,
itemBuilder: (context, dateIndex) {
List<DateTime> dateList =
_logEntryDailyMap.keys.toList();
final date = dateList[dateIndex];
final entryList = _logEntryDailyMap[date];
return ListBody(
children: [
Text(DateTimeUtils.displayDate(date)),
entryList != null && entryList.isNotEmpty
? ListView.builder(
shrinkWrap: true,
itemCount: entryList.length,
itemBuilder: (context, index) {
final logEntry = entryList[index];
double bolus =
LogBolus.getTotalBolusForEntry(
logEntry.id);
double carbs =
LogMeal.getTotalCarbsForEntry(
logEntry.id);
return ListTile(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
LogEntryScreen(
id: logEntry.id),
),
).then((message) =>
reload(message: message));
},
title: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Expanded(
child: Text(
DateTimeUtils.displayTime(
logEntry.time),
),
),
Expanded(
child: Column(
children: logEntry
.mgPerDl !=
null &&
(glucoseMeasurement == GlucoseMeasurement.mgPerDl ||
glucoseDisplayMode ==
GlucoseDisplayMode
.both ||
glucoseDisplayMode ==
GlucoseDisplayMode
.bothForList)
? [
Text(logEntry
.mgPerDl
.toString()),
const Text(
'mg/dl',
textScaleFactor:
0.75,
),
]
: [],
),
),
Expanded(
child: Column(
children: logEntry
.mmolPerL !=
null &&
(glucoseMeasurement == GlucoseMeasurement.mmolPerL ||
glucoseDisplayMode ==
GlucoseDisplayMode
.both ||
glucoseDisplayMode ==
GlucoseDisplayMode
.bothForList)
? [
Text(logEntry
.mmolPerL
.toString()),
const Text(
'mmol/l',
textScaleFactor:
0.75,
),
]
: [],
),
),
Expanded(
child: Column(
children: (bolus > 0)
? [
Text(bolus
.toStringAsPrecision(
3)),
const Text('U',
textScaleFactor:
0.75),
]
: [],
),
),
Expanded(
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(),
],
? ListView.builder(
padding: const EdgeInsets.all(10.0),
shrinkWrap: true,
itemCount: _logEntryDailyMap.length,
itemBuilder: (context, dateIndex) {
List<DateTime> dateList = _logEntryDailyMap.keys.toList();
final date = dateList[dateIndex];
final entryList = _logEntryDailyMap[date];
final tiles = <Widget>[];
for (LogEntry logEntry in entryList!) {
double bolus =
LogBolus.getTotalBolusForEntry(logEntry.id);
double carbs =
LogMeal.getTotalCarbsForEntry(logEntry.id);
tiles.add(ListTile(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
LogEntryScreen(id: logEntry.id),
),
).then((message) => reload(message: message));
},
title: Row(
mainAxisAlignment: MainAxisAlignment.start,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Text(
DateTimeUtils.displayTime(logEntry.time),
),
),
Expanded(
child: Column(
children: logEntry.mgPerDl != null &&
(Settings.get().glucoseMeasurement ==
GlucoseMeasurement.mgPerDl ||
Settings.get().glucoseDisplayMode ==
GlucoseDisplayMode.both ||
Settings.get().glucoseDisplayMode ==
GlucoseDisplayMode
.bothForList)
? [
Text(logEntry.mgPerDl.toString()),
const Text(
'mg/dl',
textScaleFactor: 0.75,
),
]
: [],
),
),
Expanded(
child: Column(
children: logEntry.mmolPerL != null &&
(Settings.get().glucoseMeasurement ==
GlucoseMeasurement.mmolPerL ||
Settings.get().glucoseDisplayMode ==
GlucoseDisplayMode.both ||
Settings.get().glucoseDisplayMode ==
GlucoseDisplayMode
.bothForList)
? [
Text(logEntry.mmolPerL.toString()),
const Text(
'mmol/l',
textScaleFactor: 0.75,
),
]
: [],
),
),
Expanded(
child: Column(
children: (bolus > 0)
? [
Text(bolus.toStringAsPrecision(3)),
const Text('U',
textScaleFactor: 0.75),
]
: [],
),
),
Expanded(
child: Column(
children: (carbs > 0)
? [
Text(carbs.toStringAsPrecision(3)),
Text(
nutritionMeasurementSuffixes[Settings.get().nutritionMeasurement.index],
textScaleFactor: 0.75),
]
: [],
),
),
],
),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
onPressed: () => handleDeleteAction(logEntry),
icon: const Icon(Icons.delete,
color: Colors.blue),
)
],
),
));
}
return ListBody(
children: <Widget>[Text(DateTimeUtils.displayDate(date))] + tiles,
);
},
),
},
)
: const Center(
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/dropdown.dart';
import 'package:diameter/components/forms.dart';
import 'package:diameter/config.dart';
import 'package:diameter/models/bolus.dart';
import 'package:diameter/models/log_bolus.dart';
import 'package:diameter/models/log_entry.dart';
import 'package:diameter/models/log_meal.dart';
import 'package:diameter/models/settings.dart';
import 'package:diameter/navigation.dart';
import 'package:diameter/settings.dart';
import 'package:diameter/utils/utils.dart';
import 'package:flutter/material.dart';
@ -34,8 +33,7 @@ class LogBolusDetailScreen extends StatefulWidget {
final int logEntryId;
final int id;
const LogBolusDetailScreen(
{Key? key, this.logEntryId = 0, this.id = 0})
const LogBolusDetailScreen({Key? key, this.logEntryId = 0, this.id = 0})
: super(key: key);
@override
@ -62,10 +60,14 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
final _delayController = TextEditingController(text: '');
final _notesController = TextEditingController(text: '');
final _delayedUnitsController = TextEditingController(text: '');
final _immediateUnitsController = TextEditingController(text: '');
bool _setManually = false;
BolusType _bolusType = BolusType.meal;
LogMeal? _meal;
Bolus? _rate;
double _delayPercentage = 0;
List<LogMeal> _logMeals = [];
@ -76,7 +78,7 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
_logEntry = LogEntry.get(widget.logEntryId);
_logMeals = LogMeal.getAllForEntry(widget.logEntryId);
if (widget.id != 0) {
_carbsController.text = (_logBolus!.carbs ?? '').toString();
_delayController.text = (_logBolus!.delay ?? '').toString();
@ -87,27 +89,46 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
}
_rate ??= Bolus.getRateForTime(_logEntry?.time);
_mgPerDlCurrentController.text =
(_logBolus?.mgPerDlCurrent ?? (LogEntry.hasUncorrectedGlucose(widget.logEntryId) ? _logEntry?.mgPerDl ?? 0 : 0)).toString();
_mgPerDlCurrentController.text = (_logBolus?.mgPerDlCurrent ??
(LogEntry.hasUncorrectedGlucose(widget.logEntryId)
? _logEntry?.mgPerDl ?? 0
: 0))
.toString();
_mgPerDlTargetController.text =
(_logBolus?.mgPerDlTarget ?? moderateGlucoseMgPerDl).toString();
_mgPerDlCorrectionController.text =
(_logBolus?.mgPerDlCorrection ?? max((int.tryParse(_mgPerDlCurrentController.text) ?? 0) - (int.tryParse(_mgPerDlTargetController.text) ?? 0), 0)).toString();
_mmolPerLCurrentController.text =
(_logBolus?.mmolPerLCurrent ?? (LogEntry.hasUncorrectedGlucose(widget.logEntryId) ? _logEntry?.mmolPerL ?? 0 : 0)).toString();
(_logBolus?.mgPerDlTarget ?? Settings.get().moderateGlucoseMgPerDl).toString();
_mgPerDlCorrectionController.text = (_logBolus?.mgPerDlCorrection ??
max(
(int.tryParse(_mgPerDlCurrentController.text) ?? 0) -
(int.tryParse(_mgPerDlTargetController.text) ?? 0),
0))
.toString();
_mmolPerLCurrentController.text = (_logBolus?.mmolPerLCurrent ??
(LogEntry.hasUncorrectedGlucose(widget.logEntryId)
? _logEntry?.mmolPerL ?? 0
: 0))
.toString();
_mmolPerLTargetController.text =
(_logBolus?.mmolPerLTarget ?? moderateGlucoseMmolPerL).toString();
_mmolPerLCorrectionController.text =
(_logBolus?.mmolPerLCorrection ?? max((double.tryParse(_mmolPerLCurrentController.text) ?? 0) - (double.tryParse(_mmolPerLTargetController.text) ?? 0), 0)).toString();
(_logBolus?.mmolPerLTarget ?? Settings.get().moderateGlucoseMmolPerL).toString();
_mmolPerLCorrectionController.text = (_logBolus?.mmolPerLCorrection ??
max(
(double.tryParse(_mmolPerLCurrentController.text) ?? 0) -
(double.tryParse(_mmolPerLTargetController.text) ?? 0),
0))
.toString();
_unitsController.text = (_logBolus?.units ??
(_rate != null && !_setManually ? ((int.tryParse(_mgPerDlCorrectionController.text) ?? 0) / ((_rate!.mgPerDl ?? 0) / _rate!.units)) : 0)
).toString();
(_rate != null && !_setManually
? ((int.tryParse(_mgPerDlCorrectionController.text) ?? 0) /
((_rate!.mgPerDl ?? 0) / _rate!.units))
: 0))
.toString();
if (widget.id == 0 && LogEntry.hasUncorrectedGlucose(widget.logEntryId)) {
_bolusType = BolusType.glucose;
}
updateDelayedRatio();
}
void reload() {
@ -119,6 +140,23 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
_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) {
setState(() {
_meal = meal;
@ -135,6 +173,18 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
_unitsController.text = ((double.tryParse(_carbsController.text) ?? 0) /
(_rate!.carbs / _rate!.units))
.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;
});
if (_logBolusForm.currentState!.validate()) {
LogBolus logBolus = LogBolus(
id: widget.id,
units: double.tryParse(_unitsController.text) ?? 0,
delay: int.tryParse(_delayController.text),
setManually: _setManually,
notes: _notesController.text,
);
LogBolus logBolus;
LogBolus? delayedBolus;
if ((int.tryParse(_delayController.text) ?? 0) != 0 &&
_delayPercentage != 0 &&
_delayPercentage != 100) {
logBolus = LogBolus(
id: widget.id,
units: double.tryParse(_immediateUnitsController.text) ?? 0,
setManually: _setManually,
notes: _notesController.text,
);
delayedBolus = LogBolus(
delay: int.tryParse(_delayController.text),
units: double.tryParse(_delayedUnitsController.text) ?? 0,
setManually: _setManually,
notes: _notesController.text,
);
} else {
logBolus = LogBolus(
id: widget.id,
units: double.tryParse(_unitsController.text) ?? 0,
delay: _delayPercentage == 100
? int.tryParse(_delayController.text)
: null,
setManually: _setManually,
notes: _notesController.text,
);
}
if (_bolusType == BolusType.meal) {
logBolus.carbs = double.tryParse(_carbsController.text);
if (delayedBolus != null) {
delayedBolus.carbs = double.tryParse(_carbsController.text);
}
logBolus.mgPerDlCurrent = null;
logBolus.mmolPerLCurrent = null;
} else {
@ -225,14 +301,37 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
logBolus.mgPerDlTarget = int.tryParse(_mgPerDlTargetController.text);
logBolus.mmolPerLTarget =
double.tryParse(_mmolPerLTargetController.text);
logBolus.mgPerDlCorrection = int.tryParse(_mgPerDlCorrectionController.text);
logBolus.mgPerDlCorrection =
int.tryParse(_mgPerDlCorrectionController.text);
logBolus.mmolPerLCorrection =
double.tryParse(_mmolPerLCorrectionController.text);
if (delayedBolus != null) {
delayedBolus.mgPerDlCurrent =
int.tryParse(_mgPerDlCurrentController.text);
delayedBolus.mmolPerLCurrent =
double.tryParse(_mmolPerLCurrentController.text);
delayedBolus.mgPerDlTarget =
int.tryParse(_mgPerDlTargetController.text);
delayedBolus.mmolPerLTarget =
double.tryParse(_mmolPerLTargetController.text);
delayedBolus.mgPerDlCorrection =
int.tryParse(_mgPerDlCorrectionController.text);
delayedBolus.mmolPerLCorrection =
double.tryParse(_mmolPerLCorrectionController.text);
}
}
logBolus.logEntry.target = _logEntry;
logBolus.meal.target = _meal;
logBolus.rate.target = _rate;
LogBolus.put(logBolus);
if (delayedBolus != null) {
delayedBolus.logEntry.target = _logEntry;
delayedBolus.meal.target = _meal;
delayedBolus.rate.target = _rate;
LogBolus.put(delayedBolus);
}
Navigator.pop(context, '${_isNew ? 'New' : ''} Bolus Saved');
}
setState(() {
@ -241,16 +340,18 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
}
void handleCancelAction() {
if (showConfirmationDialogOnCancel &&
if (Settings.get().showConfirmationDialogOnCancel &&
((_isNew &&
(_unitsController.text != '' ||
_carbsController.text != '' ||
_mgPerDlCurrentController.text != '' ||
_mgPerDlTargetController.text != '' ||
_mgPerDlCorrectionController.text != '' ||
_mmolPerLCurrentController.text != '' ||
_mmolPerLTargetController.text != '' ||
_mmolPerLCorrectionController.text != '' ||
(_carbsController.text != '' ||
(_bolusType == BolusType.glucose &&
(_mgPerDlCurrentController.text !=
(_logEntry?.mgPerDl.toString() ?? '') ||
_mmolPerLCurrentController.text !=
(_logEntry?.mmolPerL.toString() ?? ''))) ||
_mgPerDlTargetController.text !=
Settings.get().moderateGlucoseMgPerDl.toString() ||
_mmolPerLTargetController.text !=
Settings.get().moderateGlucoseMmolPerL.toString() ||
_delayController.text != '' ||
_setManually ||
_notesController.text != '')) ||
@ -297,28 +398,39 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
FormWrapper(
formState: _logBolusForm,
fields: [
TextFormField(
decoration: const InputDecoration(
labelText: 'Bolus Units',
suffixText: ' U',
),
controller: _unitsController,
onChanged: (_) {
setState(() {
_setManually = true;
});
},
keyboardType:
const TextInputType.numberWithOptions(decimal: true),
),
BooleanFormField(
value: _setManually,
label: 'set manually',
onChanged: (value) {
setState(() {
_setManually = value;
});
},
Row(
children: [
Expanded(
child: TextFormField(
decoration: const InputDecoration(
labelText: 'Bolus Units',
suffixText: ' U',
),
controller: _unitsController,
onChanged: (_) {
setState(() {
_setManually = true;
});
updateDelayedRatio();
},
keyboardType: const TextInputType.numberWithOptions(
decimal: true),
),
),
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(
children: [
@ -350,11 +462,11 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
children: _bolusType == BolusType.glucose
? [
Row(
children: glucoseMeasurement ==
children: Settings.get().glucoseMeasurement ==
GlucoseMeasurement.mgPerDl ||
glucoseDisplayMode ==
Settings.get().glucoseDisplayMode ==
GlucoseDisplayMode.both ||
glucoseDisplayMode ==
Settings.get().glucoseDisplayMode ==
GlucoseDisplayMode.bothForDetail
? [
Expanded(
@ -408,9 +520,9 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
),
),
),
glucoseDisplayMode ==
Settings.get().glucoseDisplayMode ==
GlucoseDisplayMode.both ||
glucoseDisplayMode ==
Settings.get().glucoseDisplayMode ==
GlucoseDisplayMode.bothForDetail
? IconButton(
onPressed: () => onChangeGlucose(
@ -424,11 +536,11 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
: [],
),
Row(
children: glucoseMeasurement ==
children: Settings.get().glucoseMeasurement ==
GlucoseMeasurement.mmolPerL ||
glucoseDisplayMode ==
Settings.get().glucoseDisplayMode ==
GlucoseDisplayMode.both ||
glucoseDisplayMode ==
Settings.get().glucoseDisplayMode ==
GlucoseDisplayMode.bothForDetail
? [
Expanded(
@ -483,9 +595,9 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
),
),
),
glucoseDisplayMode ==
Settings.get().glucoseDisplayMode ==
GlucoseDisplayMode.both ||
glucoseDisplayMode ==
Settings.get().glucoseDisplayMode ==
GlucoseDisplayMode.bothForDetail
? IconButton(
onPressed: () => onChangeGlucose(
@ -512,13 +624,7 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
TextFormField(
decoration: InputDecoration(
labelText: 'Carbs',
suffixText: nutritionMeasurement ==
NutritionMeasurement.grams
? 'g'
: nutritionMeasurement ==
NutritionMeasurement.ounces
? 'oz'
: '',
suffixText: nutritionMeasurementSuffixes[Settings.get().nutritionMeasurement.index],
),
controller: _carbsController,
onChanged: (_) => onChangeCarbs(),
@ -533,8 +639,64 @@ class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
suffixText: ' min',
),
controller: _delayController,
onChanged: (value) => setState(() {}),
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(
controller: _notesController,
decoration: const InputDecoration(

View File

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

View File

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

View File

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

View File

@ -1,7 +1,7 @@
import 'package:diameter/components/dialogs.dart';
import 'package:diameter/config.dart';
import 'package:diameter/models/log_entry.dart';
import 'package:diameter/models/log_meal.dart';
import 'package:diameter/models/settings.dart';
import 'package:diameter/screens/log/log_entry/log_meal_detail.dart';
import 'package:flutter/material.dart';
@ -53,7 +53,7 @@ class _LogMealListScreenState extends State<LogMealListScreen> {
}
void handleDeleteAction(LogMeal meal) async {
if (showConfirmationDialogOnDelete) {
if (Settings.get().showConfirmationDialogOnDelete) {
Dialogs.showConfirmationDialog(
context: context,
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/dropdown.dart';
import 'package:diameter/components/forms.dart';
import 'package:diameter/config.dart';
import 'package:diameter/models/basal_profile.dart';
import 'package:diameter/models/bolus_profile.dart';
import 'package:diameter/models/log_event.dart';
import 'package:diameter/models/log_event_type.dart';
import 'package:diameter/models/settings.dart';
import 'package:diameter/navigation.dart';
import 'package:diameter/utils/date_time_utils.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 {
setState(() {
_isSaving = true;
});
if (_logEventForm.currentState!.validate()) {
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');
await checkIfActiveEventOfTypeExistsBeforeSaving();
}
setState(() {
_isSaving = false;
@ -157,7 +196,7 @@ class _LogEventDetailScreenState extends State<LogEventDetailScreen> {
}
void handleCancelAction() {
if (showConfirmationDialogOnCancel &&
if (Settings.get().showConfirmationDialogOnCancel &&
((_isNew &&
(_notesController.text != '' ||
_eventType != null ||
@ -202,55 +241,47 @@ class _LogEventDetailScreenState extends State<LogEventDetailScreen> {
},
),
Row(
children: [
Expanded(
child: Padding(
padding: const EdgeInsets.only(right: 5),
child: DateTimeFormField(
date: _time,
label: _hasEndTime ? 'Start Date' : 'Date',
controller: _dateController,
onChanged: (newTime) {
if (newTime != null) {
setState(() {
_time = DateTime(
newTime.year,
newTime.month,
newTime.day,
_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();
}
},
),
),
),
],
children: [
Expanded(
child: Padding(
padding: const EdgeInsets.only(right: 5),
child: DateTimeFormField(
date: _time,
label: _hasEndTime ? 'Start Date' : 'Date',
controller: _dateController,
onChanged: (newTime) {
if (newTime != null) {
setState(() {
_time = DateTime(newTime.year, newTime.month,
newTime.day, _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();
}
},
),
),
),
],
),
BooleanFormField(
value: _hasEndTime,
onChanged: (value) {
@ -261,57 +292,59 @@ class _LogEventDetailScreenState extends State<LogEventDetailScreen> {
label: 'has end time',
),
Column(
children: _hasEndTime? [
Row(
children: [
Expanded(
child: Padding(
padding: const EdgeInsets.only(right: 5),
child: DateTimeFormField(
date: _endTime ?? now,
label: 'End Date',
controller: _endDateController,
onChanged: (newTime) {
if (newTime != null) {
setState(() {
_endTime = DateTime(
newTime.year,
newTime.month,
newTime.day,
_endTime?.hour ?? 0,
_endTime?.minute ?? 0);
});
updateEndTime();
}
},
),
children: _hasEndTime
? [
Row(
children: [
Expanded(
child: Padding(
padding: const EdgeInsets.only(right: 5),
child: DateTimeFormField(
date: _endTime ?? now,
label: 'End Date',
controller: _endDateController,
onChanged: (newTime) {
if (newTime != null) {
setState(() {
_endTime = DateTime(
newTime.year,
newTime.month,
newTime.day,
_endTime?.hour ?? 0,
_endTime?.minute ?? 0);
});
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(
controller: _reminderDurationController,
keyboardType:

View File

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

View File

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

View File

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

View File

@ -1,6 +1,5 @@
import 'package:diameter/components/dialogs.dart';
// import 'package:diameter/components/progress_indicator.dart';
import 'package:diameter/config.dart';
import 'package:diameter/models/settings.dart';
import 'package:diameter/navigation.dart';
import 'package:diameter/screens/meal/meal_category_detail.dart';
import 'package:flutter/material.dart';
@ -47,7 +46,7 @@ class _MealCategoryListScreenState extends State<MealCategoryListScreen> {
}
void handleDeleteAction(MealCategory mealCategory) async {
if (showConfirmationDialogOnDelete) {
if (Settings.get().showConfirmationDialogOnDelete) {
Dialogs.showConfirmationDialog(
context: context,
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/dropdown.dart';
import 'package:diameter/components/forms.dart';
import 'package:diameter/config.dart';
import 'package:diameter/models/accuracy.dart';
import 'package:diameter/models/meal.dart';
import 'package:diameter/models/meal_category.dart';
import 'package:diameter/models/meal_portion_type.dart';
import 'package:diameter/models/meal_source.dart';
import 'package:diameter/models/settings.dart';
import 'package:diameter/navigation.dart';
import 'package:diameter/settings.dart';
import 'package:diameter/utils/utils.dart';
import 'package:flutter/material.dart';
@ -122,7 +121,7 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
}
void handleCancelAction() {
if (showConfirmationDialogOnCancel &&
if (Settings.get().showConfirmationDialogOnCancel &&
((_isNew &&
(_valueController.text != '' ||
_mealSource != null ||
@ -313,12 +312,7 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
decoration: InputDecoration(
labelText: 'Portion size',
suffixText:
nutritionMeasurement == NutritionMeasurement.grams
? 'g'
: nutritionMeasurement ==
NutritionMeasurement.ounces
? 'oz'
: '',
nutritionMeasurementSuffixes[Settings.get().nutritionMeasurement.index],
alignLabelWithHint: true,
),
controller: _portionSizeController,
@ -354,12 +348,7 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
decoration: InputDecoration(
labelText: 'Carbs per portion',
suffixText:
nutritionMeasurement == NutritionMeasurement.grams
? 'g'
: nutritionMeasurement ==
NutritionMeasurement.ounces
? 'oz'
: '',
nutritionMeasurementSuffixes[Settings.get().nutritionMeasurement.index],
),
controller: _carbsPerPortionController,
keyboardType: const TextInputType.numberWithOptions(

View File

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

View File

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

View File

@ -1,5 +1,5 @@
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/screens/meal/meal_portion_type_detail.dart';
import 'package:flutter/material.dart';
@ -47,7 +47,7 @@ class _MealPortionTypeListScreenState extends State<MealPortionTypeListScreen> {
}
void handleDeleteAction(MealPortionType mealPortionType) async {
if (showConfirmationDialogOnDelete) {
if (Settings.get().showConfirmationDialogOnDelete) {
Dialogs.showConfirmationDialog(
context: context,
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/dropdown.dart';
import 'package:diameter/components/forms.dart';
import 'package:diameter/config.dart';
import 'package:diameter/models/accuracy.dart';
import 'package:diameter/models/meal_category.dart';
import 'package:diameter/models/meal_portion_type.dart';
import 'package:diameter/models/meal_source.dart';
import 'package:diameter/models/settings.dart';
import 'package:diameter/navigation.dart';
import 'package:flutter/material.dart';
@ -90,7 +90,7 @@ class _MealSourceDetailScreenState extends State<MealSourceDetailScreen> {
}
void handleCancelAction() {
if (showConfirmationDialogOnCancel &&
if (Settings.get().showConfirmationDialogOnCancel &&
((_isNew &&
(_valueController.text != '' ||
_defaultCarbsRatioAccuracy != null ||

View File

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

View File

@ -1,88 +1,9 @@
import 'package:diameter/components/dialogs.dart';
import 'package:diameter/components/dropdown.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: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 {
static const String routeName = '/settings';
@ -94,12 +15,73 @@ class SettingsScreen extends StatefulWidget {
}
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() {
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(() {
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
Widget build(BuildContext context) {
return Scaffold(
@ -126,88 +102,78 @@ class _SettingsScreenState extends State<SettingsScreen> {
drawer: const Navigation(currentLocation: SettingsScreen.routeName),
body: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
FormWrapper(
formState: _settingsForm,
fields: [
AutoCompleteDropdownButton<NutritionMeasurement>(
selectedItem: nutritionMeasurement,
label: 'Preferred Nutrition Measurement',
items: NutritionMeasurement.values,
onChanged: (value) {
if (value != null) {
Settings.setNutritionMeasurement(value);
setState(() {
nutritionMeasurement = value;
});
}
},
),
AutoCompleteDropdownButton<GlucoseMeasurement>(
selectedItem: glucoseMeasurement,
label: 'Preferred Glucose Measurement',
items: GlucoseMeasurement.values,
onChanged: (value) {
if (value != null) {
Settings.setGlucoseMeasurement(value);
setState(() {
glucoseMeasurement = value;
});
}
},
),
BooleanFormField(
value: glucoseDisplayMode == GlucoseDisplayMode.activeOnly,
label: 'only display active glucose measurement',
onChanged: (_) {
GlucoseDisplayMode mode =
glucoseDisplayMode == GlucoseDisplayMode.activeOnly
? GlucoseDisplayMode.both
: GlucoseDisplayMode.activeOnly;
Settings.setGlucoseDisplayMode(mode);
setState(() {
glucoseDisplayMode = mode;
});
},
),
BooleanFormField(
value: glucoseDisplayMode == GlucoseDisplayMode.both ||
glucoseDisplayMode == GlucoseDisplayMode.bothForDetail,
enabled: glucoseDisplayMode != GlucoseDisplayMode.activeOnly,
label: 'display both glucose measurements in detail view',
onChanged: (_) {
GlucoseDisplayMode mode = glucoseDisplayMode ==
GlucoseDisplayMode.both
AutoCompleteDropdownButton<String>(
selectedItem: nutritionMeasurementLabels[
_settings.nutritionMeasurement.index],
label: 'Preferred Nutrition Measurement',
items: nutritionMeasurementLabels,
onChanged: (value) {
if (value != null) {
_settings.nutritionMeasurement = NutritionMeasurement.values
.elementAt(nutritionMeasurementLabels.indexOf(value));
saveSettings();
}
},
),
AutoCompleteDropdownButton<String>(
selectedItem:
glucoseMeasurementLabels[_settings.glucoseMeasurement.index],
label: 'Preferred Glucose Measurement',
items: glucoseMeasurementLabels,
onChanged: (value) {
if (value != null) {
_settings.glucoseMeasurement = GlucoseMeasurement.values
.elementAt(glucoseMeasurementLabels.indexOf(value));
saveSettings();
}
},
),
BooleanFormField(
value: _onlyDisplayActiveGlucoseMeasurement,
label: 'only display active glucose measurement',
onChanged: (_) {
GlucoseDisplayMode mode = _settings.glucoseDisplayMode ==
GlucoseDisplayMode.activeOnly
? GlucoseDisplayMode.both
: GlucoseDisplayMode.activeOnly;
_settings.glucoseDisplayMode = mode;
saveSettings();
},
),
BooleanFormField(
value: _displayBothGlucoseMeasurementsInDetailView,
enabled:
!_onlyDisplayActiveGlucoseMeasurement,
label: 'display both glucose measurements in detail view',
onChanged: (_) {
GlucoseDisplayMode mode =
_settings.glucoseDisplayMode == GlucoseDisplayMode.both
? GlucoseDisplayMode.bothForList
: glucoseDisplayMode == GlucoseDisplayMode.bothForList
: _settings.glucoseDisplayMode ==
GlucoseDisplayMode.bothForList
? GlucoseDisplayMode.both
: GlucoseDisplayMode.activeOnly;
Settings.setGlucoseDisplayMode(mode);
setState(() {
glucoseDisplayMode = mode;
});
},
),
BooleanFormField(
value: glucoseDisplayMode == GlucoseDisplayMode.both ||
glucoseDisplayMode == GlucoseDisplayMode.bothForList,
enabled: glucoseDisplayMode != GlucoseDisplayMode.activeOnly,
label: 'display both glucose measurements in list view',
onChanged: (_) {
GlucoseDisplayMode mode = glucoseDisplayMode ==
GlucoseDisplayMode.both
_settings.glucoseDisplayMode = mode;
saveSettings();
},
),
BooleanFormField(
value: _displayBothGlucoseMeasurementsInListView,
enabled:
!_onlyDisplayActiveGlucoseMeasurement,
label: 'display both glucose measurements in list view',
onChanged: (_) {
GlucoseDisplayMode mode =
_settings.glucoseDisplayMode == GlucoseDisplayMode.both
? GlucoseDisplayMode.bothForDetail
: glucoseDisplayMode == GlucoseDisplayMode.bothForDetail
: _settings.glucoseDisplayMode ==
GlucoseDisplayMode.bothForDetail
? GlucoseDisplayMode.both
: GlucoseDisplayMode.activeOnly;
Settings.setGlucoseDisplayMode(mode);
setState(() {
glucoseDisplayMode = mode;
});
},
),
],
_settings.glucoseDisplayMode = mode;
saveSettings();
},
),
],
),

View File

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

View File

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