switch from back4app to local store using objectbox

This commit is contained in:
spinel 2021-11-07 21:13:28 +01:00
parent ca89ab4eff
commit f7d727e070
54 changed files with 4751 additions and 3553 deletions

View File

@ -1,4 +1,4 @@
# tide # Diameter
A new Flutter project. A new Flutter project.

3
TODO
View File

@ -3,4 +3,5 @@ Todo:
☐ add active/deleted flag to all data models ☐ add active/deleted flag to all data models
☐ account for deleted/disabled elements in dropdowns ☐ account for deleted/disabled elements in dropdowns
☐ place dropdown items right below their input ☐ place dropdown items right below their input
☐ use local database instead of back4app ✔ use local database instead of back4app @done(21-11-07 18:53)

View File

@ -65,4 +65,5 @@ flutter {
dependencies { dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation "com.github.parse-community.Parse-SDK-Android:parse:2.0.3"
} }

View File

@ -15,6 +15,7 @@ allprojects {
repositories { repositories {
google() google()
mavenCentral() mavenCentral()
maven { url "https://jitpack.io" }
} }
} }

View File

@ -1,5 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart'; // import 'package:flutter/widgets.dart';
class ViewWithProgressIndicator extends StatefulWidget { class ViewWithProgressIndicator extends StatefulWidget {
final AsyncSnapshot snapshot; final AsyncSnapshot snapshot;
@ -28,10 +28,6 @@ class _ViewWithProgressIndicatorState extends State<ViewWithProgressIndicator> {
switch (widget.snapshot.connectionState) { switch (widget.snapshot.connectionState) {
case ConnectionState.none: case ConnectionState.none:
case ConnectionState.waiting: case ConnectionState.waiting:
Widget progressIndicator = Container();
// Future.delayed(const Duration(milliseconds: 100)).then((_) => {
// progressIndicator = const CircularProgressIndicator()
// });
return Container( return Container(
alignment: Alignment.center, alignment: Alignment.center,
padding: widget.padding, padding: widget.padding,

View File

@ -1,4 +1,5 @@
import 'package:diameter/components/app_theme.dart'; import 'package:diameter/components/app_theme.dart';
import 'package:diameter/object_box.dart';
import 'package:diameter/screens/accuracy_detail.dart'; import 'package:diameter/screens/accuracy_detail.dart';
import 'package:diameter/screens/basal/basal_profile_detail.dart'; import 'package:diameter/screens/basal/basal_profile_detail.dart';
import 'package:diameter/screens/bolus/bolus_profile_detail.dart'; import 'package:diameter/screens/bolus/bolus_profile_detail.dart';
@ -24,14 +25,22 @@ import 'package:diameter/screens/basal/basal_profiles_list.dart';
import 'package:diameter/screens/bolus/bolus_profile_list.dart'; import 'package:diameter/screens/bolus/bolus_profile_list.dart';
import 'package:diameter/navigation.dart'; import 'package:diameter/navigation.dart';
void main() async { late ObjectBox objectBox;
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();
await Parse().initialize(keyApplicationId, keyParseServerUrl, await Parse().initialize(
clientKey: keyClientKey, debug: true); keyApplicationId,
keyParseServerUrl,
clientKey: keyClientKey,
debug: true,
coreStore: await CoreStoreSharedPrefsImp.getInstance(),
);
Settings.loadSettingsIntoConfig(); Settings.loadSettingsIntoConfig();
objectBox = await ObjectBox.create();
runApp( runApp(
MaterialApp( MaterialApp(
theme: AppTheme.makeTheme(AppTheme.lightTheme), theme: AppTheme.makeTheme(AppTheme.lightTheme),

View File

@ -1,126 +1,45 @@
import 'package:parse_server_sdk_flutter/parse_server_sdk.dart'; import 'package:diameter/main.dart';
import 'package:diameter/objectbox.g.dart';
@Entity()
class Accuracy { class Accuracy {
late String? objectId; static final Box<Accuracy> box = objectBox.store.box<Accuracy>();
late String value;
late bool forCarbsRatio = false;
late bool forPortionSize = false;
late int? confidenceRating;
late String? notes;
Accuracy(ParseObject? object) { int id;
if (object != null) { String value;
objectId = object.get<String>('objectId'); bool forCarbsRatio;
value = object.get<String>('value')!; bool forPortionSize;
forCarbsRatio = object.get<bool>('forCarbsRatio')!; int? confidenceRating;
forPortionSize = object.get<bool>('forPortionSize')!; String? notes;
confidenceRating = object.get<num>('confidenceRating') != null
? object.get<num>('confidenceRating')!.toInt() Accuracy({
: null; this.id = 0,
notes = object.get<String>('notes'); this.value = '',
this.forCarbsRatio = false,
this.forPortionSize = false,
this.confidenceRating,
this.notes,
});
static Accuracy? get(int id) => box.get(id);
static List<Accuracy> getAll() {
QueryBuilder<Accuracy> all = box.query()..order(Accuracy_.confidenceRating);
return all.build().find();
} }
static void put(Accuracy accuracy) => box.put(accuracy);
static void remove(int id) => box.remove(id);
static List<Accuracy> getAllForPortionSize() {
QueryBuilder<Accuracy> allForPortionSize = box
.query(Accuracy_.forPortionSize.equals(true))
..order(Accuracy_.confidenceRating);
return allForPortionSize.build().find();
} }
static Future<List<Accuracy>> fetchAll() async { static List<Accuracy> getAllForCarbsRatio() {
QueryBuilder<ParseObject> query = QueryBuilder<Accuracy> allForCarbsRatio = box
QueryBuilder<ParseObject>(ParseObject('Accuracy')); .query(Accuracy_.forCarbsRatio.equals(true))
final ParseResponse apiResponse = await query.query(); ..order(Accuracy_.confidenceRating);
return allForCarbsRatio.build().find();
if (apiResponse.success && apiResponse.results != null) {
return apiResponse.results!
.map((e) => Accuracy(e as ParseObject))
.toList();
} else {
return [];
}
}
static Future<List<Accuracy>> fetchAllForCarbsRatio() async {
QueryBuilder<ParseObject> query =
QueryBuilder<ParseObject>(ParseObject('Accuracy'))
..whereEqualTo('forCarbsRatio', true);
final ParseResponse apiResponse = await query.query();
if (apiResponse.success && apiResponse.results != null) {
return apiResponse.results!
.map((e) => Accuracy(e as ParseObject))
.toList();
} else {
return [];
}
}
static Future<List<Accuracy>> fetchAllForPortionSize() async {
QueryBuilder<ParseObject> query =
QueryBuilder<ParseObject>(ParseObject('Accuracy'))
..whereEqualTo('forPortionSize', true);
final ParseResponse apiResponse = await query.query();
if (apiResponse.success && apiResponse.results != null) {
return apiResponse.results!
.map((e) => Accuracy(e as ParseObject))
.toList();
} else {
return [];
}
}
static Future<Accuracy?> get(String objectId) async {
QueryBuilder<ParseObject> query =
QueryBuilder<ParseObject>(ParseObject('Accuracy'))
..whereEqualTo('objectId', objectId);
final ParseResponse apiResponse = await query.query();
if (apiResponse.success && apiResponse.results != null) {
return Accuracy(apiResponse.result.first);
}
}
static Future<void> save({
required String value,
bool forCarbsRatio = false,
bool forPortionSize = false,
int? confidenceRating,
String? notes,
}) async {
final accuracy = ParseObject('Accuracy')
..set('value', value)
..set('forCarbsRatio', forCarbsRatio)
..set('forPortionSize', forPortionSize)
..set('confidenceRating', confidenceRating)
..set('notes', notes);
await accuracy.save();
}
static Future<void> update(
String objectId, {
String? value,
bool? forCarbsRatio,
bool? forPortionSize,
int? confidenceRating,
String? notes,
}) async {
var accuracy = ParseObject('Accuracy')..objectId = objectId;
if (value != null) {
accuracy.set('value', value);
}
if (forCarbsRatio != null) {
accuracy.set('forCarbsRatio', forCarbsRatio);
}
if (forPortionSize != null) {
accuracy.set('forPortionSize', forPortionSize);
}
if (confidenceRating != null) {
accuracy.set('confidenceRating', confidenceRating);
}
if (notes != null) {
accuracy.set('notes', notes);
}
await accuracy.save();
}
Future<void> delete() async {
var accuracy = ParseObject('Accuracy')..objectId = objectId;
await accuracy.delete();
} }
} }

View File

@ -1,115 +1,37 @@
// import 'package:diameter/utils/date_time_utils.dart'; import 'package:diameter/main.dart';
// import 'package:flutter/material.dart';
import 'package:parse_server_sdk_flutter/parse_server_sdk.dart';
import 'package:diameter/models/basal_profile.dart'; import 'package:diameter/models/basal_profile.dart';
import 'package:diameter/components/data_table.dart'; import 'package:diameter/objectbox.g.dart';
class Basal extends DataTableContent { @Entity()
late String? objectId; class Basal {
late DateTime startTime; static final Box<Basal> box = objectBox.store.box<Basal>();
late DateTime endTime;
late double units;
late String basalProfile;
Basal(ParseObject? object) { int id;
if (object != null) {
objectId = object.get<String>('objectId');
startTime = object.get<DateTime>('startTime')!.toLocal();
endTime = object.get<DateTime>('endTime')!.toLocal();
units = object.get<num>('units')! / 100;
basalProfile =
object.get<ParseObject>('basalProfile')!.get<String>('objectId')!;
}
}
static Future<Basal?> get(String objectId) async { @Property(type: PropertyType.date)
QueryBuilder<ParseObject> query = DateTime startTime;
QueryBuilder<ParseObject>(ParseObject('Basal'))
..whereEqualTo('objectId', objectId);
final ParseResponse apiResponse = await query.query();
if (apiResponse.success && apiResponse.results != null) { @Property(type: PropertyType.date)
return Basal(apiResponse.result.first); DateTime endTime;
}
}
static Future<List<Basal>> fetchAllForBasalProfile( double units;
BasalProfile basalProfile) async {
QueryBuilder<ParseObject> query =
QueryBuilder<ParseObject>(ParseObject('Basal'))
..whereEqualTo(
'basalProfile',
(ParseObject('BasalProfile')..objectId = basalProfile.objectId!)
.toPointer())
..orderByAscending('startTime');
final ParseResponse apiResponse = await query.query();
if (apiResponse.success && apiResponse.results != null) { final basalProfile = ToOne<BasalProfile>();
return apiResponse.results!.map((e) => Basal(e as ParseObject)).toList();
} else {
return [];
}
}
static Future<void> save({ Basal({
required DateTime startTime, this.id = 0,
required DateTime endTime, required this.startTime,
required double units, required this.endTime,
required String basalProfile, this.units = 0,
}) async { });
final basal = ParseObject('Basal')
..set('startTime', startTime.toUtc())
..set('endTime', endTime.toUtc())
..set('units', units * 100)
..set('basalProfile',
(ParseObject('BasalProfile')..objectId = basalProfile).toPointer());
await basal.save();
}
static Future<void> update( static Basal? get(int id) => box.get(id);
String objectId, { static void put(Basal basal) => box.put(basal);
DateTime? startTime, static void remove(int id) => box.remove(id);
DateTime? endTime,
double? units,
}) async {
var basal = ParseObject('Basal')..objectId = objectId;
if (startTime != null) {
basal.set('startTime', startTime.toUtc());
}
if (endTime != null) {
basal.set('endTime', endTime.toUtc());
}
if (units != null) {
basal.set('units', units * 100);
}
await basal.save();
}
Future<void> delete() async { static List<Basal> getAllForProfile(int id) {
var basal = ParseObject('Basal')..objectId = objectId; QueryBuilder<Basal> builder = box.query()..order(Basal_.startTime);
await basal.delete(); builder.link(Basal_.basalProfile, BasalProfile_.id.equals(id));
return builder.build().find();
} }
//
// @override
// List<DataCell> asDataTableCells(List<Widget>? actions) {
// return [
// DataCell(Text(DateTimeUtils.displayTime(startTime))),
// DataCell(Text(DateTimeUtils.displayTime(endTime))),
// DataCell(Text('${units.toString()} U')),
// DataCell(
// Row(
// children: actions ?? [],
// ),
// ),
// ];
// }
//
// static List<DataColumn> asDataTableColumns() {
// return [
// const DataColumn(label: Expanded(child: Text('Start Time'))),
// const DataColumn(label: Expanded(child: Text('End Time'))),
// const DataColumn(label: Expanded(child: Text('Units'))),
// const DataColumn(label: Expanded(child: Text('Actions'))),
// ];
// }
} }

View File

@ -1,101 +1,37 @@
import 'package:parse_server_sdk_flutter/parse_server_sdk.dart'; import 'package:diameter/main.dart';
import 'package:diameter/models/basal.dart'; import 'package:diameter/objectbox.g.dart';
@Entity()
class BasalProfile { class BasalProfile {
late String? objectId; static final Box<BasalProfile> box = objectBox.store.box<BasalProfile>();
late String name;
late bool active = false;
late Future<List<Basal>> basalRates;
late String? notes;
BasalProfile(ParseObject? object) { int id;
if (object != null) { String name;
objectId = object.get<String>('objectId'); bool active;
name = object.get<String>('name')!; String? notes;
active = object.get<bool>('active')!;
basalRates = Basal.fetchAllForBasalProfile(this); BasalProfile({
notes = object.get<String>('notes'); this.id = 0,
} this.name = '',
this.active = false,
this.notes,
});
static BasalProfile? get(int id) => box.get(id);
static List<BasalProfile> getAll() => box.getAll();
static void put(BasalProfile basalProfile) => box.put(basalProfile);
static void remove(int id) => box.remove(id);
static int activeCount() {
Query<BasalProfile> query =
box.query(BasalProfile_.active.equals(true)).build();
return query.find().length;
} }
static Future<List<BasalProfile>> fetchAll() async { static void setAllInactive() {
QueryBuilder<ParseObject> query = box.putMany(box.getAll().map((element) {
QueryBuilder<ParseObject>(ParseObject('BasalProfile')); element.active = false;
final ParseResponse apiResponse = await query.query(); return element;
}).toList());
if (apiResponse.success && apiResponse.results != null) {
return apiResponse.results!
.map((e) => BasalProfile(e as ParseObject))
.toList();
} else {
return [];
}
}
static Future<BasalProfile?> get(String objectId) async {
QueryBuilder<ParseObject> query =
QueryBuilder<ParseObject>(ParseObject('BasalProfile'))
..whereEqualTo('objectId', objectId);
final ParseResponse apiResponse = await query.query();
if (apiResponse.success && apiResponse.results != null) {
return BasalProfile(apiResponse.result.first);
}
}
static Future<int> getActiveCount() async {
QueryBuilder<ParseObject> query =
QueryBuilder<ParseObject>(ParseObject('BasalProfile'))
..whereEqualTo('active', true);
final ParseResponse apiResponse = await query.query();
if (apiResponse.success && apiResponse.results != null) {
return apiResponse.results!.length;
} else {
return 0;
}
}
static Future<void> setAllInactive({String? exception}) async {
QueryBuilder<ParseObject> query =
QueryBuilder<ParseObject>(ParseObject('BasalProfile'));
final ParseResponse apiResponse = await query.query();
if (apiResponse.success && apiResponse.results != null) {
for (var basalProfile in apiResponse.results as List<ParseObject>) {
basalProfile.set(
'active', basalProfile.objectId == exception ? true : false);
await basalProfile.save();
}
}
}
static Future<void> save(
{required String name, bool active = false, String? notes}) async {
final basalProfile = ParseObject('BasalProfile')
..set('name', name)
..set('active', active)
..set('notes', notes);
await basalProfile.save();
}
static Future<void> update(String objectId,
{String? name, bool? active, String? notes}) async {
var basalProfile = ParseObject('BasalProfile')..objectId = objectId;
if (name != null) {
basalProfile.set('name', name);
}
if (active != null) {
basalProfile.set('active', active);
}
if (notes != null) {
basalProfile.set('notes', notes);
}
await basalProfile.save();
}
Future<void> delete() async {
var basalProfile = ParseObject('BasalProfile')..objectId = objectId;
await basalProfile.delete();
} }
} }

View File

@ -1,201 +1,40 @@
// import 'package:diameter/config.dart'; import 'package:diameter/main.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';
import 'package:parse_server_sdk_flutter/parse_server_sdk.dart';
import 'package:diameter/components/data_table.dart';
import 'package:diameter/models/bolus_profile.dart'; import 'package:diameter/models/bolus_profile.dart';
import 'package:diameter/objectbox.g.dart';
class Bolus extends DataTableContent { @Entity()
late String? objectId; class Bolus {
late DateTime startTime; static final Box<Bolus> box = objectBox.store.box<Bolus>();
late DateTime endTime;
late double units;
late double carbs;
late int? mgPerDl;
late double? mmolPerL;
late String bolusProfile;
Bolus(ParseObject? object) { int id;
if (object != null) { @Property(type: PropertyType.date)
objectId = object.get<String>('objectId'); DateTime startTime;
startTime = object.get<DateTime>('startTime')!.toLocal(); @Property(type: PropertyType.date)
endTime = object.get<DateTime>('endTime')!.toLocal(); DateTime endTime;
units = object.get<num>('units')! / 100; double units;
carbs = object.get<num>('carbs')!.toDouble(); double carbs;
mgPerDl = object.get<num>('mgPerDl') != null int? mgPerDl;
? object.get<num>('mgPerDl')!.toInt() double? mmolPerL;
: null;
mmolPerL = object.get<num>('mmolPerL') != null
? object.get<num>('mmolPerL')! / 100
: null;
bolusProfile =
object.get<ParseObject>('bolusProfile')!.get<String>('objectId')!;
}
}
static Future<Bolus?> get(String objectId) async { final bolusProfile = ToOne<BolusProfile>();
QueryBuilder<ParseObject> query =
QueryBuilder<ParseObject>(ParseObject('Bolus'))
..whereEqualTo('objectId', objectId);
final ParseResponse apiResponse = await query.query(); Bolus({
this.id = 0,
required this.startTime,
required this.endTime,
this.units = 0,
this.carbs = 0,
this.mgPerDl,
this.mmolPerL,
});
if (apiResponse.success && apiResponse.results != null) { static Bolus? get(int id) => box.get(id);
return Bolus(apiResponse.result.first); static void put(Bolus bolus) => box.put(bolus);
} static void remove(int id) => box.remove(id);
}
static Future<List<Bolus>> fetchAllForBolusProfile( static List<Bolus> getAllForProfile(int id) {
BolusProfile bolusProfile) async { QueryBuilder<Bolus> builder = box.query()..order(Bolus_.startTime);
QueryBuilder<ParseObject> query = builder.link(Bolus_.bolusProfile, BolusProfile_.id.equals(id));
QueryBuilder<ParseObject>(ParseObject('Bolus')) return builder.build().find();
..whereEqualTo(
'bolusProfile',
(ParseObject('BolusProfile')..objectId = bolusProfile.objectId!)
.toPointer())
..orderByAscending('startTime');
final ParseResponse apiResponse = await query.query();
if (apiResponse.success && apiResponse.results != null) {
return apiResponse.results!.map((e) => Bolus(e as ParseObject)).toList();
} else {
return [];
} }
} }
static Future<void> save({
required DateTime startTime,
required DateTime endTime,
required double units,
required double carbs,
int? mgPerDl,
double? mmolPerL,
required String bolusProfile,
}) async {
final bolus = ParseObject('Bolus')
..set('startTime', startTime.toUtc())
..set('endTime', endTime.toUtc())
..set('units', units * 100)
..set('carbs', carbs.round())
..set('bolusProfile',
(ParseObject('BolusProfile')..objectId = bolusProfile).toPointer());
bolus.set(
'mgPerDl',
mgPerDl != null
? mgPerDl.round()
: Utils.convertMmolPerLToMgPerDl(mmolPerL ?? 0));
bolus.set(
'mmolPerL',
mmolPerL != null
? mmolPerL * 100
: Utils.convertMgPerDlToMmolPerL(mgPerDl ?? 0) * 100);
await bolus.save();
}
static Future<void> update(
String objectId, {
DateTime? startTime,
DateTime? endTime,
double? units,
double? carbs,
int? mgPerDl,
double? mmolPerL,
}) async {
var bolus = ParseObject('Bolus')..objectId = objectId;
if (startTime != null) {
bolus.set('startTime', startTime.toUtc());
}
if (endTime != null) {
bolus.set('endTime', endTime.toUtc());
}
if (units != null) {
bolus.set('units', units * 100);
}
if (carbs != null) {
bolus.set('carbs', carbs);
}
if (mgPerDl != null || mmolPerL != null) {
bolus.set(
'mgPerDl',
mgPerDl != null
? mgPerDl.round()
: Utils.convertMmolPerLToMgPerDl(mmolPerL ?? 0));
bolus.set(
'mmolPerL',
mmolPerL != null
? mmolPerL * 100
: Utils.convertMgPerDlToMmolPerL(mgPerDl ?? 0) * 100);
}
await bolus.save();
}
Future<void> delete() async {
var bolus = ParseObject('Bolus')..objectId = objectId;
await bolus.delete();
}
//
// @override
// List<DataCell> asDataTableCells(List<Widget>? actions) {
// var cols = [
// DataCell(Text(DateTimeUtils.displayTime(startTime))),
// DataCell(Text(DateTimeUtils.displayTime(endTime))),
// DataCell(Text('${units.toString()} U')),
// DataCell(Text('${carbs.toString()} g')),
// ];
//
// if (glucoseMeasurement == GlucoseMeasurement.mgPerDl ||
// glucoseDisplayMode == GlucoseDisplayMode.both ||
// glucoseDisplayMode == GlucoseDisplayMode.bothForList) {
// cols.add(DataCell(Text('${mgPerDl.toString()} mg/dl')));
// }
//
// if (glucoseMeasurement == GlucoseMeasurement.mmolPerL ||
// glucoseDisplayMode == GlucoseDisplayMode.both ||
// glucoseDisplayMode == GlucoseDisplayMode.bothForList) {
// cols.add(DataCell(Text('${mmolPerL.toString()} mmol/l')));
// }
//
// cols.add(
// DataCell(
// Row(
// children: actions ?? [],
// ),
// ),
// );
//
// return cols;
// }
//
// static List<DataColumn> asDataTableColumns() {
// var cols = [
// const DataColumn(label: Expanded(child: Text('Start Time'))),
// const DataColumn(label: Expanded(child: Text('End Time'))),
// const DataColumn(label: Expanded(child: Text('Units'))),
// const DataColumn(label: Expanded(child: Text('per Carbs'))),
// ];
//
// if (glucoseMeasurement == GlucoseMeasurement.mgPerDl ||
// glucoseDisplayMode == GlucoseDisplayMode.both ||
// glucoseDisplayMode == GlucoseDisplayMode.bothForList) {
// cols.add(const DataColumn(label: Expanded(child: Text('per mg/dl'))));
// }
//
// if (glucoseMeasurement == GlucoseMeasurement.mmolPerL ||
// glucoseDisplayMode == GlucoseDisplayMode.both ||
// glucoseDisplayMode == GlucoseDisplayMode.bothForList) {
// cols.add(const DataColumn(label: Expanded(child: Text('per mmol/l'))));
// }
//
// cols.add(
// const DataColumn(label: Expanded(child: Text('Actions'))),
// );
//
// return cols;
// }
}

View File

@ -1,101 +1,37 @@
import 'package:parse_server_sdk_flutter/parse_server_sdk.dart'; import 'package:diameter/main.dart';
import 'package:diameter/models/bolus.dart'; import 'package:diameter/objectbox.g.dart';
@Entity()
class BolusProfile { class BolusProfile {
late String? objectId; static final Box<BolusProfile> box = objectBox.store.box<BolusProfile>();
late String name;
late bool active = false;
late Future<List<Bolus>> bolusRates;
late String? notes;
BolusProfile(ParseObject? object) { int id;
if (object != null) { String name;
objectId = object.get<String>('objectId'); bool active;
name = object.get<String>('name')!; String? notes;
active = object.get<bool>('active')!;
bolusRates = Bolus.fetchAllForBolusProfile(this); BolusProfile({
notes = object.get<String>('notes'); this.id = 0,
} this.name = '',
this.active = false,
this.notes,
});
static BolusProfile? get(int id) => box.get(id);
static List<BolusProfile> getAll() => box.getAll();
static void put(BolusProfile bolusProfile) => box.put(bolusProfile);
static void remove(int id) => box.remove(id);
static int activeCount() {
Query<BolusProfile> query =
box.query(BolusProfile_.active.equals(true)).build();
return query.find().length;
} }
static Future<List<BolusProfile>> fetchAll() async { static void setAllInactive() {
QueryBuilder<ParseObject> query = box.putMany(box.getAll().map((element) {
QueryBuilder<ParseObject>(ParseObject('BolusProfile')); element.active = false;
final ParseResponse apiResponse = await query.query(); return element;
}).toList());
if (apiResponse.success && apiResponse.results != null) {
return apiResponse.results!
.map((e) => BolusProfile(e as ParseObject))
.toList();
} else {
return [];
}
}
static Future<BolusProfile?> get(String objectId) async {
QueryBuilder<ParseObject> query =
QueryBuilder<ParseObject>(ParseObject('BolusProfile'))
..whereEqualTo('objectId', objectId);
final ParseResponse apiResponse = await query.query();
if (apiResponse.success && apiResponse.results != null) {
return BolusProfile(apiResponse.result.first);
}
}
static Future<int> getActiveCount() async {
QueryBuilder<ParseObject> query =
QueryBuilder<ParseObject>(ParseObject('BolusProfile'))
..whereEqualTo('active', true);
final ParseResponse apiResponse = await query.query();
if (apiResponse.success && apiResponse.results != null) {
return apiResponse.results!.length;
} else {
return 0;
}
}
static Future<void> setAllInactive({String? exception}) async {
QueryBuilder<ParseObject> query =
QueryBuilder<ParseObject>(ParseObject('BolusProfile'));
final ParseResponse apiResponse = await query.query();
if (apiResponse.success && apiResponse.results != null) {
for (var bolusProfile in apiResponse.results as List<ParseObject>) {
bolusProfile.set(
'active', bolusProfile.objectId == exception ? true : false);
await bolusProfile.save();
}
}
}
static Future<void> save(
{required String name, bool active = false, String? notes}) async {
final bolusProfile = ParseObject('BolusProfile')
..set('name', name)
..set('active', active)
..set('notes', notes);
await bolusProfile.save();
}
static Future<void> update(String objectId,
{String? name, bool? active, String? notes}) async {
var bolusProfile = ParseObject('BolusProfile')..objectId = objectId;
if (name != null) {
bolusProfile.set('name', name);
}
if (active != null) {
bolusProfile.set('active', active);
}
if (notes != null) {
bolusProfile.set('notes', notes);
}
await bolusProfile.save();
}
Future<void> delete() async {
var bolusProfile = ParseObject('BolusProfile')..objectId = objectId;
await bolusProfile.delete();
} }
} }

View File

@ -1,50 +1,55 @@
import 'package:diameter/models/log_meal.dart'; import 'package:diameter/main.dart';
import 'package:diameter/utils/utils.dart';
import 'package:flutter/material.dart';
import 'package:parse_server_sdk_flutter/parse_server_sdk.dart';
import 'package:diameter/models/log_event.dart'; import 'package:diameter/models/log_event.dart';
import 'package:diameter/models/log_meal.dart';
import 'package:diameter/objectbox.g.dart';
import 'package:objectbox/objectbox.dart';
@Entity()
class LogEntry { class LogEntry {
late String? objectId; static final Box<LogEntry> box = objectBox.store.box<LogEntry>();
late DateTime time;
late int? mgPerDl;
late double? mmolPerL;
late double? bolusGlucose;
late int? delayedBolusDuration;
// TODO: either rename this or all other fields using delayedBolusRate
late double? delayedBolusRatio;
late String? notes;
late Future<List<LogEvent>> events;
late Future<List<LogEvent>> endedEvents;
late Future<List<LogMeal>> meals;
LogEntry(ParseObject object) { int id;
objectId = object.get<String>('objectId');
time = object.get<DateTime>('time')!.toLocal();
mgPerDl = object.get<num>('mgPerDl') != null
? object.get<num>('mgPerDl')!.toInt()
: null;
mmolPerL = object.get<num>('mmolPerL') != null
? object.get<num>('mmolPerL')!.toDouble()
: null;
bolusGlucose = object.get<num>('bolusGlucose') != null
? object.get<num>('bolusGlucose')!.toDouble()
: null;
delayedBolusDuration = object.get<num>('delayedBolusDuration') != null
? object.get<num>('delayedBolusDuration')!.toInt()
: null;
delayedBolusRatio = object.get<num>('delayedBolusRatio') != null
? object.get<num>('delayedBolusRatio')!.toDouble()
: null;
events = LogEvent.fetchAllForLogEntry(this);
endedEvents = LogEvent.fetchAllEndedByEntry(this);
meals = LogMeal.fetchAllForLogEntry(this);
notes = object.get<String>('notes');
}
static Future<Map<DateTime, List<LogEntry>>> createDailyEntryMap() async { @Property(type: PropertyType.date)
DateTime time;
int? mgPerDl;
double? mmolPerL;
double? bolusGlucose;
int? delayedBolusDuration;
double? delayedBolusRate;
String? notes;
@Backlink('logEntry')
final events = ToMany<LogEvent>();
@Backlink('endLogEntry')
final endedEvents = ToMany<LogEvent>();
@Backlink('logEntry')
final meals = ToMany<LogMeal>();
LogEntry({
this.id = 0,
required this.time,
this.mgPerDl,
this.mmolPerL,
this.bolusGlucose,
this.delayedBolusDuration,
this.delayedBolusRate,
this.notes,
});
static LogEntry? get(int id) => box.get(id);
static List<LogEntry> getAll() => box.getAll();
static void put(LogEntry logEntry) => box.put(logEntry);
static void remove(int id) => box.remove(id);
static Map<DateTime, List<LogEntry>> getDailyEntryMap() {
Map<DateTime, List<LogEntry>> dateMap = <DateTime, List<LogEntry>>{}; Map<DateTime, List<LogEntry>> dateMap = <DateTime, List<LogEntry>>{};
List<LogEntry> entries = await fetchAll();
QueryBuilder<LogEntry> allByDate = box.query()..order(LogEntry_.time, flags: Order.descending);
List<LogEntry> entries = allByDate.build().find();
DateTime? date; DateTime? date;
for (LogEntry entry in entries) { for (LogEntry entry in entries) {
@ -54,133 +59,4 @@ class LogEntry {
return dateMap; return dateMap;
} }
static Future<List<LogEntry>> fetchAllForRange(DateTimeRange range) async {
QueryBuilder<ParseObject> query =
QueryBuilder<ParseObject>(ParseObject('LogEntry'))
..whereGreaterThanOrEqualsTo('time', range.start.toUtc())
..whereLessThanOrEqualTo('time', range.end.toUtc())
..orderByAscending('time');
final ParseResponse apiResponse = await query.query();
if (apiResponse.success && apiResponse.results != null) {
return apiResponse.results!
.map((e) => LogEntry(e as ParseObject))
.toList();
} else {
return [];
}
}
static Future<List<LogEntry>> fetchAll() async {
// TODO: consider adding pagination/lazy loading here
QueryBuilder<ParseObject> query =
QueryBuilder<ParseObject>(ParseObject('LogEntry'))
..orderByAscending('time');
;
final ParseResponse apiResponse = await query.query();
if (apiResponse.success && apiResponse.results != null) {
return apiResponse.results!
.map((e) => LogEntry(e as ParseObject))
.toList();
} else {
return [];
}
}
static Future<LogEntry?> get(String objectId) async {
QueryBuilder<ParseObject> query =
QueryBuilder<ParseObject>(ParseObject('LogEntry'))
..whereEqualTo('objectId', objectId);
final ParseResponse apiResponse = await query.query();
if (apiResponse.success && apiResponse.results != null) {
return LogEntry(apiResponse.result.first);
}
}
static Future<void> save({
required DateTime time,
int? mgPerDl,
double? mmolPerL,
double? bolusGlucose,
int? delayedBolusDuration,
double? delayedBolusRatio,
String? notes,
}) async {
final logEntry = ParseObject('LogEntry')
..set('time', time.toUtc())
..set('bolusGlucose', bolusGlucose)
..set('delayedBolusDuration', delayedBolusDuration)
..set('delayedBolusRatio', delayedBolusRatio)
..set('notes', notes);
if (!(mgPerDl == null && mmolPerL == null)) {
logEntry.set(
'mgPerDl',
mgPerDl != null
? mgPerDl.round()
: Utils.convertMmolPerLToMgPerDl(mmolPerL ?? 0));
logEntry.set(
'mmolPerL',
mmolPerL != null
? mmolPerL * 100
: Utils.convertMgPerDlToMmolPerL(mgPerDl ?? 0) * 100);
}
await logEntry.save();
}
static Future<void> update(
String objectId, {
DateTime? time,
int? mgPerDl,
double? mmolPerL,
double? bolusGlucose,
int? delayedBolusDuration,
double? delayedBolusRatio,
String? notes,
}) async {
final logEntry = ParseObject('LogEntry');
if (time != null) {
logEntry.set('time', time.toUtc());
}
if (bolusGlucose != null) {
logEntry.set('bolusGlucose', bolusGlucose);
}
if (delayedBolusDuration != null) {
logEntry.set('delayedBolusDuration', delayedBolusDuration);
}
if (delayedBolusRatio != null) {
logEntry.set('delayedBolusRatio', delayedBolusRatio);
}
if (notes != null) {
logEntry.set('notes', notes);
}
if (!(mgPerDl == null && mmolPerL == null)) {
logEntry.set(
'mgPerDl',
mgPerDl != null
? mgPerDl.round()
: Utils.convertMmolPerLToMgPerDl(mmolPerL ?? 0));
logEntry.set(
'mmolPerL',
mmolPerL != null
? mmolPerL * 100
: Utils.convertMgPerDlToMmolPerL(mgPerDl ?? 0) * 100);
}
await logEntry.save();
}
Future<void> delete() async {
var logEntry = ParseObject('LogEntry')..objectId = objectId;
await logEntry.delete();
}
} }

View File

@ -1,176 +1,43 @@
import 'package:diameter/components/data_table.dart'; import 'package:diameter/main.dart';
import 'package:diameter/models/log_entry.dart'; import 'package:diameter/models/log_entry.dart';
import 'package:diameter/models/log_event_type.dart'; import 'package:diameter/models/log_event_type.dart';
import 'package:diameter/utils/date_time_utils.dart'; import 'package:diameter/objectbox.g.dart';
import 'package:flutter/material.dart';
import 'package:parse_server_sdk_flutter/parse_server_sdk.dart';
class LogEvent extends DataTableContent { @Entity()
late String? objectId; class LogEvent {
late String logEntry; static final Box<LogEvent> box = objectBox.store.box<LogEvent>();
late String? endLogEntry;
late String eventType;
late DateTime time;
late DateTime? endTime;
late bool hasEndTime;
late String? notes;
LogEvent(ParseObject? object) { int id;
if (object != null) {
objectId = object.get<String>('objectId');
logEntry = object.get<ParseObject>('logEntry')!.get<String>('objectId')!;
endLogEntry =
object.get<ParseObject>('endLogEntry')?.get<String>('objectId');
eventType =
object.get<ParseObject>('eventType')!.get<String>('objectId')!;
time = object.get<DateTime>('time')!.toLocal();
endTime = object.get<DateTime>('endTime')?.toLocal();
hasEndTime = object.get<bool>('hasEndTime')!;
notes = object.get<String>('notes');
}
}
static Future<LogEvent?> get(String objectId) async { @Property(type: PropertyType.date)
QueryBuilder<ParseObject> query = DateTime time;
QueryBuilder<ParseObject>(ParseObject('LogEvent'))
..whereEqualTo('objectId', objectId);
final ParseResponse apiResponse = await query.query();
if (apiResponse.success && apiResponse.results != null) { @Property(type: PropertyType.date)
return LogEvent(apiResponse.result.first); DateTime? endTime;
}
}
static Future<List<LogEvent>> fetchAllActive() async { bool hasEndTime;
QueryBuilder<ParseObject> query = String? notes;
QueryBuilder<ParseObject>(ParseObject('LogEvent'))
..whereEqualTo('hasEndTime', true)
..whereEqualTo('endTime', null);
final ParseResponse apiResponse = await query.query();
if (apiResponse.success && apiResponse.results != null) { final logEntry = ToOne<LogEntry>();
return apiResponse.results! final endLogEntry = ToOne<LogEntry>();
.map((e) => LogEvent(e as ParseObject)) final eventType = ToOne<LogEventType>();
.toList();
} else {
return [];
}
}
static Future<List<LogEvent>> fetchAllForLogEntry(LogEntry logEntry) async { LogEvent({
QueryBuilder<ParseObject> query = QueryBuilder<ParseObject>( this.id = 0,
ParseObject('LogEvent')) required this.time,
..whereEqualTo('logEntry', this.endTime,
(ParseObject('LogEntry')..objectId = logEntry.objectId!).toPointer()); this.hasEndTime = false,
final ParseResponse apiResponse = await query.query(); this.notes,
});
if (apiResponse.success && apiResponse.results != null) { static LogEvent? get(int id) => box.get(id);
return apiResponse.results! static List<LogEvent> getAll() => box.getAll();
.map((e) => LogEvent(e as ParseObject)) static void put(LogEvent logEvent) => box.put(logEvent);
.toList(); static void remove(int id) => box.remove(id);
} else {
return [];
}
}
static Future<List<LogEvent>> fetchAllEndedByEntry(LogEntry logEntry) async { static List<LogEvent> getAllOngoing() {
QueryBuilder<ParseObject> query = QueryBuilder<ParseObject>( QueryBuilder<LogEvent> query =
ParseObject('LogEvent')) box.query(LogEvent_.hasEndTime.equals(true) & LogEvent_.endTime.isNull())..order(LogEvent_.time);
..whereEqualTo('endLogEntry', return query.build().find();
(ParseObject('LogEntry')..objectId = logEntry.objectId!).toPointer());
final ParseResponse apiResponse = await query.query();
if (apiResponse.success && apiResponse.results != null) {
return apiResponse.results!
.map((e) => LogEvent(e as ParseObject))
.toList();
} else {
return [];
} }
} }
static Future<void> save({
required String logEntry,
required String eventType,
required DateTime time,
required bool hasEndTime,
String? notes,
}) async {
final logEvent = ParseObject('LogEvent')
..set('logEntry',
(ParseObject('LogEntry')..objectId = logEntry).toPointer())
..set('eventType',
(ParseObject('LogEventType')..objectId = eventType).toPointer())
..set('time', time.toUtc())
..set('hasEndTime', hasEndTime)
..set('notes', notes);
await logEvent.save();
}
static Future<void> update(
String objectId, {
String? eventType,
String? endLogEntry,
DateTime? time,
DateTime? endTime,
bool? hasEndTime,
String? notes,
}) async {
var logEvent = ParseObject('LogEvent')..objectId = objectId;
if (eventType != null) {
logEvent.set('eventType',
(ParseObject('LogEventType')..objectId = eventType).toPointer());
}
if (endLogEntry != null) {
logEvent.set('endLogEntry',
(ParseObject('LogEntry')..objectId = endLogEntry).toPointer());
}
if (time != null) {
logEvent.set('time', time.toUtc());
}
if (endTime != null) {
logEvent.set('endTime', endTime.toUtc());
}
if (hasEndTime != null) {
logEvent.set('hasEndTime', hasEndTime);
}
if (notes != null) {
logEvent.set('notes', notes);
}
await logEvent.save();
}
Future<void> delete() async {
var logEvent = ParseObject('LogEvent')..objectId = objectId;
await logEvent.delete();
}
//
// @override
// List<DataCell> asDataTableCells(List<Widget> actions,
// {List<LogEventType>? types}) {
// return [
// DataCell(Text(
// types?.firstWhere((element) => element.objectId == eventType).value ??
// types?.length.toString() ??
// '')),
// DataCell(Text(DateTimeUtils.displayDateTime(time))),
// DataCell(Text(hasEndTime
// ? DateTimeUtils.displayDateTime(endTime, fallback: 'ongoing')
// : '-')),
// DataCell(
// Row(
// children: actions,
// ),
// ),
// ];
// }
//
// static List<DataColumn> asDataTableColumns() {
// return [
// const DataColumn(label: Expanded(child: Text('Event Type'))),
// const DataColumn(label: Expanded(child: Text('Start Time'))),
// const DataColumn(label: Expanded(child: Text('End Time'))),
// const DataColumn(label: Expanded(child: Text('Actions'))),
// ];
// }
}

View File

@ -1,86 +1,26 @@
import 'package:parse_server_sdk_flutter/parse_server_sdk.dart'; import 'package:diameter/main.dart';
import 'package:objectbox/objectbox.dart';
@Entity()
class LogEventType { class LogEventType {
late String? objectId; static final Box<LogEventType> box = objectBox.store.box<LogEventType>();
late String value;
late bool hasEndTime;
late int? defaultReminderDuration;
late String? notes;
LogEventType(ParseObject object) { int id;
objectId = object.get<String>('objectId'); String value;
value = object.get<String>('value')!; bool hasEndTime;
hasEndTime = object.get<bool>('hasEndTime')!; int? defaultReminderDuration;
defaultReminderDuration = object.get<num>('defaultReminderDuration') != null String? notes;
? object.get<num>('defaultReminderDuration')!.toInt()
: null;
notes = object.get<String>('notes');
}
static Future<List<LogEventType>> fetchAll() async { LogEventType({
QueryBuilder<ParseObject> query = this.id = 0,
QueryBuilder<ParseObject>(ParseObject('LogEventType')); this.value = '',
final ParseResponse apiResponse = await query.query(); this.hasEndTime = false,
this.defaultReminderDuration,
this.notes,
});
if (apiResponse.success && apiResponse.results != null) { static LogEventType? get(int id) => box.get(id);
return apiResponse.results! static List<LogEventType> getAll() => box.getAll();
.map((e) => LogEventType(e as ParseObject)) static void put(LogEventType logEventType) => box.put(logEventType);
.toList(); static void remove(int id) => box.remove(id);
} else {
return [];
}
}
static Future<LogEventType?> get(String objectId) async {
QueryBuilder<ParseObject> query =
QueryBuilder<ParseObject>(ParseObject('LogEventType'))
..whereEqualTo('objectId', objectId);
final ParseResponse apiResponse = await query.query();
if (apiResponse.success && apiResponse.results != null) {
return LogEventType(apiResponse.result.first);
}
}
static Future<void> save({
required String value,
required bool hasEndTime,
int? defaultReminderDuration,
String? notes,
}) async {
final logEventType = ParseObject('LogEventType')
..set('value', value)
..set('hasEndTime', hasEndTime)
..set('defaultReminderDuration', defaultReminderDuration)
..set('notes', notes);
await logEventType.save();
}
static Future<void> update(
String objectId, {
String? value,
bool? hasEndTime,
int? defaultReminderDuration,
String? notes,
}) async {
var logEventType = ParseObject('LogEventType')..objectId = objectId;
if (value != null) {
logEventType.set('value', value);
}
if (hasEndTime != null) {
logEventType.set('hasEndTime', hasEndTime);
}
if (defaultReminderDuration != null) {
logEventType.set('defaultReminderDuration', defaultReminderDuration);
}
if (notes != null) {
logEventType.set('notes', notes);
}
await logEventType.save();
}
Future<void> delete() async {
var logEventType = ParseObject('LogEventType')..objectId = objectId;
await logEventType.delete();
}
} }

View File

@ -1,228 +1,48 @@
import 'package:diameter/components/data_table.dart'; import 'package:diameter/main.dart';
import 'package:diameter/models/log_entry.dart'; import 'package:diameter/models/log_entry.dart';
import 'package:flutter/material.dart'; import 'package:diameter/models/meal.dart';
import 'package:parse_server_sdk_flutter/parse_server_sdk.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/accuracy.dart';
import 'package:objectbox/objectbox.dart';
class LogMeal extends DataTableContent { @Entity()
late String? objectId; class LogMeal {
late String logEntry; static final Box<LogMeal> box = objectBox.store.box<LogMeal>();
late String? meal;
late String value;
late String? source;
late String? category;
late String? portionType;
late double? carbsRatio;
late double? portionSize;
late double? carbsPerPortion;
late String? portionSizeAccuracy;
late String? carbsRatioAccuracy;
late double? bolus;
late int? delayedBolusDuration;
late double? delayedBolusRate;
late String? notes;
LogMeal(ParseObject object) { int id;
objectId = object.get<String>('objectId'); String value;
logEntry = object.get<ParseObject>('logEntry')!.get<String>('objectId')!; double? carbsRatio;
meal = object.get<ParseObject>('meal')?.get<String>('objectId'); double? portionSize;
value = object.get<String>('value')!; double? carbsPerPortion;
source = object.get<ParseObject>('source')?.get<String>('objectId'); double? bolus;
category = object.get<ParseObject>('category')?.get<String>('objectId'); int? delayedBolusDuration;
portionType = object.get<ParseObject>('portionType')?.get<String>('objectId'); double? delayedBolusRate;
carbsRatio = object.get<num>('carbsRatio')?.toDouble(); String? notes;
portionSize = object.get<num>('portionSize')?.toDouble();
carbsPerPortion = object.get<num>('carbsPerPortion')?.toDouble();
portionSizeAccuracy = object.get<ParseObject>('portionSizeAccuracy')?.get<String>('objectId');
carbsRatioAccuracy = object.get<ParseObject>('carbsRatioAccuracy')?.get<String>('objectId');
bolus = object.get<num>('bolus')?.toDouble();
delayedBolusDuration = object.get<num>('delayedBolusDuration')?.toInt();
delayedBolusRate = object.get<num>('delayedBolusRate')?.toDouble();
notes = object.get<String>('notes');
}
static Future<LogMeal?> get(String objectId) async { final logEntry = ToOne<LogEntry>();
QueryBuilder<ParseObject> query = final meal = ToOne<Meal>();
QueryBuilder<ParseObject>(ParseObject('LogMeal')) final mealSource = ToOne<MealSource>();
..whereEqualTo('objectId', objectId); final mealCategory = ToOne<MealCategory>();
final ParseResponse apiResponse = await query.query(); final mealPortionType = ToOne<MealPortionType>();
final portionSizeAccuracy = ToOne<Accuracy>();
final carbsRatioAccuracy = ToOne<Accuracy>();
if (apiResponse.success && apiResponse.results != null) { LogMeal({
return LogMeal(apiResponse.result.first); this.id = 0,
} this.value = '',
} this.carbsRatio,
this.portionSize,
this.carbsPerPortion,
this.bolus,
this.delayedBolusDuration,
this.delayedBolusRate,
this.notes,
});
static Future<List<LogMeal>> fetchAllForLogEntry(LogEntry logEntry) async { static LogMeal? get(int id) => box.get(id);
QueryBuilder<ParseObject> query = QueryBuilder<ParseObject>( static List<LogMeal> getAll() => box.getAll();
ParseObject('LogMeal')) static void put(LogMeal logMeal) => box.put(logMeal);
..whereEqualTo('logEntry', static void remove(int id) => box.remove(id);
(ParseObject('LogEntry')..objectId = logEntry.objectId!).toPointer());
final ParseResponse apiResponse = await query.query();
if (apiResponse.success && apiResponse.results != null) {
return apiResponse.results!
.map((e) => LogMeal(e as ParseObject))
.toList();
} else {
return [];
}
}
static Future<void> save({
required String value,
required String logEntry,
String? meal,
String? source,
String? category,
String? portionType,
double? carbsRatio,
double? portionSize,
double? carbsPerPortion,
String? portionSizeAccuracy,
String? carbsRatioAccuracy,
double? bolus,
int? delayedBolusDuration,
double? delayedBolusRate,
String? notes,
}) async {
final logMeal = ParseObject('LogMeal')
..set('value', value)
..set('logEntry',
(ParseObject('LogEntry')..objectId = logEntry).toPointer())
..set('carbsRatio', carbsRatio)
..set('portionSize', portionSize)
..set('carbsPerPortion', carbsPerPortion)
..set('bolus', bolus)
..set('delayedBolusDuration', delayedBolusDuration)
..set('delayedBolusRate', delayedBolusRate)
..set('notes', notes);
if (meal != null) {
logMeal.set('meal', (ParseObject('Meal')..objectId = meal).toPointer());
}
if (source != null) {
logMeal.set(
'source', (ParseObject('MealSource')..objectId = source).toPointer());
}
if (category != null) {
logMeal.set('category',
(ParseObject('MealCategory')..objectId = category).toPointer());
}
if (portionType != null) {
logMeal.set('portionType',
(ParseObject('MealPortionType')..objectId = portionType).toPointer());
}
if (portionSizeAccuracy != null) {
logMeal.set(
'portionSizeAccuracy',
(ParseObject('Accuracy')..objectId = portionSizeAccuracy)
.toPointer());
}
if (carbsRatioAccuracy != null) {
logMeal.set('carbsRatioAccuracy',
(ParseObject('Accuracy')..objectId = carbsRatioAccuracy).toPointer());
}
await logMeal.save();
}
static Future<void> update(
String objectId, {
String? value,
String? meal,
String? source,
String? category,
String? portionType,
double? carbsRatio,
double? portionSize,
double? carbsPerPortion,
String? portionSizeAccuracy,
String? carbsRatioAccuracy,
double? bolus,
int? delayedBolusDuration,
double? delayedBolusRate,
String? notes,
}) async {
var logMeal = ParseObject('LogMeal')..objectId = objectId;
if (value != null) {
logMeal.set('value', value);
}
if (meal != null) {
logMeal.set('meal', (ParseObject('Meal')..objectId = meal).toPointer());
}
if (source != null) {
logMeal.set(
'source', (ParseObject('MealSource')..objectId = source).toPointer());
}
if (category != null) {
logMeal.set('category',
(ParseObject('MealCategory')..objectId = category).toPointer());
}
if (portionType != null) {
logMeal.set('portionType',
(ParseObject('MealPortionType')..objectId = portionType).toPointer());
}
if (carbsRatio != null) {
logMeal.set('carbsRatio', carbsRatio);
}
if (portionSize != null) {
logMeal.set('portionSize', portionSize);
}
if (carbsPerPortion != null) {
logMeal.set('carbsPerPortion', carbsPerPortion);
}
if (portionSizeAccuracy != null) {
logMeal.set(
'portionSizeAccuracy',
(ParseObject('Accuracy')..objectId = portionSizeAccuracy)
.toPointer());
}
if (carbsRatioAccuracy != null) {
logMeal.set('carbsRatioAccuracy',
(ParseObject('Accuracy')..objectId = carbsRatioAccuracy).toPointer());
}
if (bolus != null) {
logMeal.set('bolus', bolus);
}
if (delayedBolusDuration != null) {
logMeal.set('delayedBolusDuration', delayedBolusDuration);
}
if (delayedBolusRate != null) {
logMeal.set('delayedBolusRate', delayedBolusRate);
}
if (notes != null) {
logMeal.set('notes', notes);
}
await logMeal.save();
}
Future<void> delete() async {
var logMeal = ParseObject('LogMeal')..objectId = objectId;
await logMeal.delete();
}
@override
List<DataCell> asDataTableCells(List<Widget>? actions) {
return [
DataCell(Text(value)),
DataCell(Text('${(carbsPerPortion ?? '').toString()} g')),
DataCell(Text('${(bolus ?? '').toString()} U')),
DataCell(Text(delayedBolusRate != null
? '${delayedBolusRate.toString()} U/${(delayedBolusDuration ?? '').toString()} min'
: '')),
DataCell(
Row(
children: actions ?? [],
),
),
];
}
static List<DataColumn> asDataTableColumns() {
return [
const DataColumn(label: Expanded(child: Text('Meal'))),
const DataColumn(label: Expanded(child: Text('Carbs'))),
const DataColumn(label: Expanded(child: Text('Bolus'))),
const DataColumn(label: Expanded(child: Text('Delayed Bolus'))),
const DataColumn(label: Expanded(child: Text('Actions'))),
];
}
} }

View File

@ -1,195 +1,45 @@
import 'package:parse_server_sdk_flutter/parse_server_sdk.dart'; import 'package:diameter/main.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:objectbox/objectbox.dart';
enum PortionCarbsParameter { carbsRatio, portionSize, carbsPerPortion } enum PortionCarbsParameter { carbsRatio, portionSize, carbsPerPortion }
@Entity()
class Meal { class Meal {
late String? objectId; static final Box<Meal> box = objectBox.store.box<Meal>();
late String value;
late String? source;
late String? category;
late String? portionType;
late double? carbsRatio;
late double? portionSize;
late double? carbsPerPortion;
late String? portionSizeAccuracy;
late String? carbsRatioAccuracy;
late int? delayedBolusDuration;
late double? delayedBolusRate;
late String? notes;
Meal(ParseObject object) { int id;
objectId = object.get<String>('objectId'); String value;
value = object.get<String>('value')!; double? carbsRatio;
source = object.get<ParseObject>('source') != null double? portionSize;
? object.get<ParseObject>('source')!.get<String>('objectId') double? carbsPerPortion;
: null; int? delayedBolusDuration;
category = object.get<ParseObject>('category') != null double? delayedBolusRate;
? object.get<ParseObject>('category')!.get<String>('objectId') String? notes;
: null;
portionType = object.get<ParseObject>('portionType') != null
? object.get<ParseObject>('portionType')!.get<String>('objectId')
: null;
carbsRatio = object.get<num>('carbsRatio') != null
? object.get<num>('carbsRatio')!.toDouble()
: null;
portionSize = object.get<num>('portionSize') != null
? object.get<num>('portionSize')!.toDouble()
: null;
carbsPerPortion = object.get<num>('carbsPerPortion') != null
? object.get<num>('carbsPerPortion')!.toDouble()
: null;
portionSizeAccuracy = object.get<ParseObject>('portionSizeAccuracy') != null
? object
.get<ParseObject>('portionSizeAccuracy')!
.get<String>('objectId')
: null;
carbsRatioAccuracy = object.get<ParseObject>('carbsRatioAccuracy') != null
? object.get<ParseObject>('carbsRatioAccuracy')!.get<String>('objectId')
: null;
delayedBolusDuration = object.get<num>('delayedBolusDuration') != null
? object.get<num>('delayedBolusDuration')!.toInt()
: null;
delayedBolusRate = object.get<num>('delayedBolusRate') != null
? object.get<num>('delayedBolusRate')!.toDouble()
: null;
notes = object.get<String>('notes');
}
static Future<List<Meal>> fetchAll() async {
QueryBuilder<ParseObject> query =
QueryBuilder<ParseObject>(ParseObject('Meal'));
final ParseResponse apiResponse = await query.query();
if (apiResponse.success && apiResponse.results != null) { final mealSource = ToOne<MealSource>();
return apiResponse.results!.map((e) => Meal(e as ParseObject)).toList(); final mealCategory = ToOne<MealCategory>();
} else { final mealPortionType = ToOne<MealPortionType>();
return []; final portionSizeAccuracy = ToOne<Accuracy>();
} final carbsRatioAccuracy = ToOne<Accuracy>();
}
static Future<Meal?> get(String objectId) async { Meal({
QueryBuilder<ParseObject> query = this.id = 0,
QueryBuilder<ParseObject>(ParseObject('Meal')) this.value = '',
..whereEqualTo('objectId', objectId); this.carbsRatio,
final ParseResponse apiResponse = await query.query(); this.portionSize,
this.carbsPerPortion,
this.delayedBolusDuration,
this.delayedBolusRate,
this.notes,
});
if (apiResponse.success && apiResponse.results != null) { static Meal? get(int id) => box.get(id);
return Meal(apiResponse.result.first); static List<Meal> getAll() => box.getAll();
} static void put(Meal meal) => box.put(meal);
} static void remove(int id) => box.remove(id);
static Future<void> save({
required String value,
String? source,
String? category,
String? portionType,
double? carbsRatio,
double? portionSize,
double? carbsPerPortion,
String? portionSizeAccuracy,
String? carbsRatioAccuracy,
int? delayedBolusDuration,
double? delayedBolusRate,
String? notes,
}) async {
final meal = ParseObject('Meal')
..set('value', value)
..set('carbsRatio', carbsRatio)
..set('portionSize', portionSize)
..set('carbsPerPortion', carbsPerPortion)
..set('delayedBolusDuration', delayedBolusDuration)
..set('delayedBolusRate', delayedBolusRate)
..set('notes', notes);
if (source != null) {
meal.set(
'source', (ParseObject('MealSource')..objectId = source).toPointer());
}
if (category != null) {
meal.set('category',
(ParseObject('MealCategory')..objectId = category).toPointer());
}
if (portionType != null) {
meal.set('portionType',
(ParseObject('MealPortionType')..objectId = portionType).toPointer());
}
if (portionSizeAccuracy != null) {
meal.set(
'portionSizeAccuracy',
(ParseObject('Accuracy')..objectId = portionSizeAccuracy)
.toPointer());
}
if (carbsRatioAccuracy != null) {
meal.set('carbsRatioAccuracy',
(ParseObject('Accuracy')..objectId = carbsRatioAccuracy).toPointer());
}
await meal.save();
}
static Future<void> update(
String objectId, {
String? value,
String? source,
String? category,
String? portionType,
double? carbsRatio,
double? portionSize,
double? carbsPerPortion,
String? portionSizeAccuracy,
String? carbsRatioAccuracy,
int? delayedBolusDuration,
double? delayedBolusRate,
String? notes,
}) async {
var meal = ParseObject('Meal')..objectId = objectId;
if (value != null) {
meal.set('value', value);
}
if (source != null) {
meal.set(
'source', (ParseObject('MealSource')..objectId = source).toPointer());
}
if (category != null) {
meal.set('category',
(ParseObject('MealCategory')..objectId = category).toPointer());
}
if (portionType != null) {
meal.set('portionType',
(ParseObject('MealPortionType')..objectId = portionType).toPointer());
}
if (carbsRatio != null) {
meal.set('carbsRatio', carbsRatio);
}
if (portionSize != null) {
meal.set('portionSize', portionSize);
}
if (carbsPerPortion != null) {
meal.set('carbsPerPortion', carbsPerPortion);
}
if (portionSizeAccuracy != null) {
meal.set(
'portionSizeAccuracy',
(ParseObject('Accuracy')..objectId = portionSizeAccuracy)
.toPointer());
}
if (carbsRatioAccuracy != null) {
meal.set('carbsRatioAccuracy',
(ParseObject('Accuracy')..objectId = carbsRatioAccuracy).toPointer());
}
if (delayedBolusDuration != null) {
meal.set('delayedBolusDuration', delayedBolusDuration);
}
if (delayedBolusRate != null) {
meal.set('delayedBolusRate', delayedBolusRate);
}
if (notes != null) {
meal.set('notes', notes);
}
await meal.save();
}
Future<void> delete() async {
var meal = ParseObject('Meal')..objectId = objectId;
await meal.delete();
}
} }

View File

@ -1,68 +1,22 @@
import 'package:parse_server_sdk_flutter/parse_server_sdk.dart'; import 'package:diameter/main.dart';
import 'package:objectbox/objectbox.dart';
@Entity()
class MealCategory { class MealCategory {
late String? objectId; static final Box<MealCategory> box = objectBox.store.box<MealCategory>();
late String value;
late String? notes;
MealCategory(ParseObject? object) { int id;
if (object != null) { String value;
objectId = object.get<String>('objectId'); String? notes;
value = object.get<String>('value')!;
notes = object.get<String>('notes');
}
}
static Future<List<MealCategory>> fetchAll() async { MealCategory({
QueryBuilder<ParseObject> query = this.id = 0,
QueryBuilder<ParseObject>(ParseObject('MealCategory')); this.value = '',
final ParseResponse apiResponse = await query.query(); this.notes,
});
if (apiResponse.success && apiResponse.results != null) { static MealCategory? get(int id) => box.get(id);
return apiResponse.results!.map((e) => MealCategory(e as ParseObject)).toList(); static List<MealCategory> getAll() => box.getAll();
} else { static void put(MealCategory mealCategory) => box.put(mealCategory);
return []; static void remove(int id) => box.remove(id);
}
}
static Future<MealCategory?> get(String objectId) async {
QueryBuilder<ParseObject> query =
QueryBuilder<ParseObject>(ParseObject('MealCategory'))
..whereEqualTo('objectId', objectId);
final ParseResponse apiResponse = await query.query();
if (apiResponse.success && apiResponse.results != null) {
return MealCategory(apiResponse.result.first);
}
}
static Future<void> save({
required String value,
String? notes,
}) async {
final mealCategory = ParseObject('MealCategory')
..set('value', value)
..set('notes', notes);
await mealCategory.save();
}
static Future<void> update(
String objectId, {
String? value,
String? notes,
}) async {
var mealCategory = ParseObject('MealCategory')..objectId = objectId;
if (value != null) {
mealCategory.set('value', value);
}
if (notes != null) {
mealCategory.set('notes', notes);
}
await mealCategory.save();
}
Future<void> delete() async {
var mealCategory = ParseObject('MealCategory')..objectId = objectId;
await mealCategory.delete();
}
} }

View File

@ -1,69 +1,22 @@
import 'package:parse_server_sdk_flutter/parse_server_sdk.dart'; import 'package:diameter/main.dart';
import 'package:objectbox/objectbox.dart';
@Entity()
class MealPortionType { class MealPortionType {
late String? objectId; static final Box<MealPortionType> box = objectBox.store.box<MealPortionType>();
late String value;
late String? notes;
MealPortionType(ParseObject? object) { int id;
if (object != null) { String value;
objectId = object.get<String>('objectId'); String? notes;
value = object.get<String>('value')!;
notes = object.get<String>('notes');
}
}
static Future<List<MealPortionType>> fetchAll() async { MealPortionType({
QueryBuilder<ParseObject> query = this.id = 0,
QueryBuilder<ParseObject>(ParseObject('MealPortionType')); this.value = '',
final ParseResponse apiResponse = await query.query(); this.notes,
});
if (apiResponse.success && apiResponse.results != null) { static MealPortionType? get(int id) => box.get(id);
// return apiResponse.results as List<ParseObject>; static List<MealPortionType> getAll() => box.getAll();
return apiResponse.results!.map((e) => MealPortionType(e as ParseObject)).toList(); static void put(MealPortionType mealPortionType) => box.put(mealPortionType);
} else { static void remove(int id) => box.remove(id);
return [];
}
}
static Future<MealPortionType?> get(String objectId) async {
QueryBuilder<ParseObject> query =
QueryBuilder<ParseObject>(ParseObject('MealPortionType'))
..whereEqualTo('objectId', objectId);
final ParseResponse apiResponse = await query.query();
if (apiResponse.success && apiResponse.results != null) {
return MealPortionType(apiResponse.result.first);
}
}
static Future<void> save({
required String value,
String? notes,
}) async {
final mealPortionType = ParseObject('MealPortionType')
..set('value', value)
..set('notes', notes);
await mealPortionType.save();
}
static Future<void> update(
String objectId, {
String? value,
String? notes,
}) async {
var mealPortionType = ParseObject('MealPortionType')..objectId = objectId;
if (value != null) {
mealPortionType.set('value', value);
}
if (notes != null) {
mealPortionType.set('notes', notes);
}
await mealPortionType.save();
}
Future<void> delete() async {
var mealPortionType = ParseObject('MealPortionType')..objectId = objectId;
await mealPortionType.delete();
}
} }

View File

@ -1,157 +1,30 @@
// import 'package:diameter/models/accuracy.dart'; import 'package:diameter/main.dart';
// import 'package:diameter/models/meal_category.dart'; import 'package:diameter/models/accuracy.dart';
// import 'package:diameter/models/meal_portion_type.dart'; import 'package:diameter/models/meal_category.dart';
import 'package:parse_server_sdk_flutter/parse_server_sdk.dart'; import 'package:diameter/models/meal_portion_type.dart';
import 'package:objectbox/objectbox.dart';
@Entity()
class MealSource { class MealSource {
late String? objectId; static final Box<MealSource> box = objectBox.store.box<MealSource>();
late String value;
late String? defaultCarbsRatioAccuracy;
late String? defaultPortionSizeAccuracy;
late String? defaultMealCategory;
late String? defaultMealPortionType;
late String? notes;
MealSource(ParseObject? object) { int id;
if (object != null) { String value;
objectId = object.get<String>('objectId'); String? notes;
value = object.get<String>('value')!;
defaultCarbsRatioAccuracy =
object.get<ParseObject>('defaultCarbsRatioAccuracy') != null
? object
.get<ParseObject>('defaultCarbsRatioAccuracy')!
.get<String>('objectId')
: null;
defaultPortionSizeAccuracy =
object.get<ParseObject>('defaultPortionSizeAccuracy') != null
? object
.get<ParseObject>('defaultPortionSizeAccuracy')!
.get<String>('objectId')
: null;
defaultMealCategory =
object.get<ParseObject>('defaultMealCategory') != null
? object
.get<ParseObject>('defaultMealCategory')!
.get<String>('objectId')
: null;
defaultMealPortionType =
object.get<ParseObject>('defaultMealPortionType') != null
? object
.get<ParseObject>('defaultMealPortionType')!
.get<String>('objectId')
: null;
notes = object.get<String>('notes');
}
}
static Future<List<MealSource>> fetchAll() async { final defaultMealCategory = ToOne<MealCategory>();
QueryBuilder<ParseObject> query = final defaultMealPortionType = ToOne<MealPortionType>();
QueryBuilder<ParseObject>(ParseObject('MealSource')); final defaultCarbsRatioAccuracy = ToOne<Accuracy>();
final ParseResponse apiResponse = await query.query(); final defaultPortionSizeAccuracy = ToOne<Accuracy>();
if (apiResponse.success && apiResponse.results != null) { MealSource({
return apiResponse.results! this.id = 0,
.map((e) => MealSource(e as ParseObject)) this.value = '',
.toList(); this.notes,
} else { });
return [];
}
}
static Future<MealSource?> get(String objectId) async { static MealSource? get(int id) => box.get(id);
QueryBuilder<ParseObject> query = static List<MealSource> getAll() => box.getAll();
QueryBuilder<ParseObject>(ParseObject('MealSource')) static void put(MealSource mealSource) => box.put(mealSource);
..whereEqualTo('objectId', objectId); static void remove(int id) => box.remove(id);
final ParseResponse apiResponse = await query.query();
if (apiResponse.success && apiResponse.results != null) {
return MealSource(apiResponse.result.first);
}
}
static Future<void> save({
required String value,
String? defaultCarbsRatioAccuracy,
String? defaultPortionSizeAccuracy,
String? defaultMealCategory,
String? defaultMealPortionType,
String? notes,
}) async {
final mealSource = ParseObject('MealSource')
..set('value', value)
..set('notes', notes);
if (defaultCarbsRatioAccuracy != null) {
mealSource.set(
'defaultCarbsRatioAccuracy',
(ParseObject('Accuracy')..objectId = defaultCarbsRatioAccuracy)
.toPointer());
}
if (defaultPortionSizeAccuracy != null) {
mealSource.set(
'defaultCarbsRatioAccuracy',
(ParseObject('Accuracy')..objectId = defaultPortionSizeAccuracy)
.toPointer());
}
if (defaultMealCategory != null) {
mealSource.set(
'defaultMealCategory',
(ParseObject('MealCategory')..objectId = defaultMealCategory)
.toPointer());
}
if (defaultMealPortionType != null) {
mealSource.set(
'defaultMealPortionType',
(ParseObject('MealPortionType')..objectId = defaultMealPortionType)
.toPointer());
}
await mealSource.save();
}
static Future<void> update(
String objectId, {
String? value,
String? defaultCarbsRatioAccuracy,
String? defaultPortionSizeAccuracy,
String? defaultMealCategory,
String? defaultMealPortionType,
String? notes,
}) async {
final mealSource = ParseObject('MealSource')..objectId = objectId;
if (value != null) {
mealSource.set('value', value);
}
if (defaultCarbsRatioAccuracy != null) {
mealSource.set(
'defaultCarbsRatioAccuracy',
(ParseObject('Accuracy')..objectId = defaultCarbsRatioAccuracy)
.toPointer());
}
if (defaultPortionSizeAccuracy != null) {
mealSource.set(
'defaultCarbsRatioAccuracy',
(ParseObject('Accuracy')..objectId = defaultPortionSizeAccuracy)
.toPointer());
}
if (defaultMealCategory != null) {
mealSource.set(
'defaultMealCategory',
(ParseObject('MealCategory')..objectId = defaultMealCategory)
.toPointer());
}
if (defaultMealPortionType != null) {
mealSource.set(
'defaultMealPortionType',
(ParseObject('MealPortionType')..objectId = defaultMealPortionType)
.toPointer());
}
if (notes != null) {
mealSource.set('notes', notes);
}
await mealSource.save();
}
Future<void> delete() async {
var mealSource = ParseObject('MealSource')..objectId = objectId;
await mealSource.delete();
}
} }

14
lib/object_box.dart Normal file
View File

@ -0,0 +1,14 @@
import 'package:diameter/objectbox.g.dart';
class ObjectBox {
late final Store store;
ObjectBox._create(this.store) {
// additiona setup logic
}
static Future<ObjectBox> create() async {
final store = await openStore();
return ObjectBox._create(store);
}
}

648
lib/objectbox-model.json Normal file
View File

@ -0,0 +1,648 @@
{
"_note1": "KEEP THIS FILE! Check it into a version control system (VCS) like git.",
"_note2": "ObjectBox manages crucial IDs for your object model. See docs for details.",
"_note3": "If you have VCS merge conflicts, you must resolve them according to ObjectBox docs.",
"entities": [
{
"id": "1:3095978685310268382",
"lastPropertyId": "6:5471636804765937328",
"name": "Accuracy",
"properties": [
{
"id": "1:3455702077061719523",
"name": "id",
"type": 6,
"flags": 1
},
{
"id": "2:1048198814030724077",
"name": "value",
"type": 9
},
{
"id": "3:9003780003858349085",
"name": "forCarbsRatio",
"type": 1
},
{
"id": "4:5421422436108145565",
"name": "forPortionSize",
"type": 1
},
{
"id": "5:7741631874181070179",
"name": "confidenceRating",
"type": 6
},
{
"id": "6:5471636804765937328",
"name": "notes",
"type": 9
}
],
"relations": []
},
{
"id": "2:1467758525778521891",
"lastPropertyId": "5:3908367275335317130",
"name": "Basal",
"properties": [
{
"id": "1:4281816825522738642",
"name": "id",
"type": 6,
"flags": 1
},
{
"id": "2:4009055523978371979",
"name": "startTime",
"type": 10
},
{
"id": "3:4023788962759622162",
"name": "endTime",
"type": 10
},
{
"id": "4:7477362011547874977",
"name": "units",
"type": 8
},
{
"id": "5:3908367275335317130",
"name": "basalProfileId",
"type": 11,
"flags": 520,
"indexId": "1:8279975749291974737",
"relationTarget": "BasalProfile"
}
],
"relations": []
},
{
"id": "3:3613736032926903785",
"lastPropertyId": "4:6719547342639071472",
"name": "BasalProfile",
"properties": [
{
"id": "1:353771983641472117",
"name": "id",
"type": 6,
"flags": 1
},
{
"id": "2:3551375678911240048",
"name": "name",
"type": 9
},
{
"id": "3:8867907906620144161",
"name": "active",
"type": 1
},
{
"id": "4:6719547342639071472",
"name": "notes",
"type": 9
}
],
"relations": []
},
{
"id": "4:3417770529060202389",
"lastPropertyId": "8:7679622918986671917",
"name": "Bolus",
"properties": [
{
"id": "1:8141647919190345775",
"name": "id",
"type": 6,
"flags": 1
},
{
"id": "2:5125398907947855021",
"name": "startTime",
"type": 10
},
{
"id": "3:4407971823097024949",
"name": "endTime",
"type": 10
},
{
"id": "4:2189083553538343203",
"name": "units",
"type": 8
},
{
"id": "5:1743412468359480761",
"name": "carbs",
"type": 8
},
{
"id": "6:6172996718025500229",
"name": "mgPerDl",
"type": 6
},
{
"id": "7:5407916209359443797",
"name": "mmolPerL",
"type": 8
},
{
"id": "8:7679622918986671917",
"name": "bolusProfileId",
"type": 11,
"flags": 520,
"indexId": "2:1936045997906240691",
"relationTarget": "BolusProfile"
}
],
"relations": []
},
{
"id": "5:8812452529027052317",
"lastPropertyId": "4:3030493484602726372",
"name": "BolusProfile",
"properties": [
{
"id": "1:4233863196673391978",
"name": "id",
"type": 6,
"flags": 1
},
{
"id": "2:1213341428301430611",
"name": "name",
"type": 9
},
{
"id": "3:8255411532896152868",
"name": "active",
"type": 1
},
{
"id": "4:3030493484602726372",
"name": "notes",
"type": 9
}
],
"relations": []
},
{
"id": "6:752131069307970560",
"lastPropertyId": "8:6492273995038150006",
"name": "LogEntry",
"properties": [
{
"id": "1:5528657304180237933",
"name": "id",
"type": 6,
"flags": 1
},
{
"id": "2:2227745196606148370",
"name": "time",
"type": 10
},
{
"id": "3:6679353626542225935",
"name": "mgPerDl",
"type": 6
},
{
"id": "4:7624273251826662730",
"name": "mmolPerL",
"type": 8
},
{
"id": "5:3678829169126156351",
"name": "bolusGlucose",
"type": 8
},
{
"id": "6:1568597071506264632",
"name": "delayedBolusDuration",
"type": 6
},
{
"id": "7:8795268969829293398",
"name": "delayedBolusRate",
"type": 8
},
{
"id": "8:6492273995038150006",
"name": "notes",
"type": 9
}
],
"relations": []
},
{
"id": "7:4303325892753185970",
"lastPropertyId": "8:2514297323717317184",
"name": "LogEvent",
"properties": [
{
"id": "1:6648501734758557663",
"name": "id",
"type": 6,
"flags": 1
},
{
"id": "2:4564145770032506132",
"name": "time",
"type": 10
},
{
"id": "3:1956029073259700909",
"name": "endTime",
"type": 10
},
{
"id": "4:289190785515853098",
"name": "hasEndTime",
"type": 1
},
{
"id": "5:3285255817130847007",
"name": "notes",
"type": 9
},
{
"id": "6:7838546213550447420",
"name": "logEntryId",
"type": 11,
"flags": 520,
"indexId": "3:3670661188280692002",
"relationTarget": "LogEntry"
},
{
"id": "7:8031421171668506924",
"name": "endLogEntryId",
"type": 11,
"flags": 520,
"indexId": "4:7379712902406481832",
"relationTarget": "LogEntry"
},
{
"id": "8:2514297323717317184",
"name": "eventTypeId",
"type": 11,
"flags": 520,
"indexId": "5:1417691902662024007",
"relationTarget": "LogEventType"
}
],
"relations": []
},
{
"id": "8:8362795406595606110",
"lastPropertyId": "5:7361377572496986196",
"name": "LogEventType",
"properties": [
{
"id": "1:1430413826199774000",
"name": "id",
"type": 6,
"flags": 1
},
{
"id": "2:2680646402943052466",
"name": "value",
"type": 9
},
{
"id": "3:8730441532702098240",
"name": "hasEndTime",
"type": 1
},
{
"id": "4:236107426012682102",
"name": "defaultReminderDuration",
"type": 6
},
{
"id": "5:7361377572496986196",
"name": "notes",
"type": 9
}
],
"relations": []
},
{
"id": "9:411177866700467286",
"lastPropertyId": "16:7121997990741934484",
"name": "LogMeal",
"properties": [
{
"id": "1:962999525294133158",
"name": "id",
"type": 6,
"flags": 1
},
{
"id": "2:4212591835755306346",
"name": "value",
"type": 9
},
{
"id": "3:2349971916492396452",
"name": "carbsRatio",
"type": 8
},
{
"id": "4:6619360836320223700",
"name": "portionSize",
"type": 8
},
{
"id": "5:2215708755581938580",
"name": "carbsPerPortion",
"type": 8
},
{
"id": "6:8074052538574863399",
"name": "bolus",
"type": 8
},
{
"id": "7:3247926313599127440",
"name": "delayedBolusDuration",
"type": 6
},
{
"id": "8:8789440370359282572",
"name": "delayedBolusRate",
"type": 8
},
{
"id": "9:1920579694098037947",
"name": "notes",
"type": 9
},
{
"id": "10:1795064951424581062",
"name": "logEntryId",
"type": 11,
"flags": 520,
"indexId": "6:2834631406671335954",
"relationTarget": "LogEntry"
},
{
"id": "11:1891119831250929825",
"name": "mealId",
"type": 11,
"flags": 520,
"indexId": "7:7173877404300881319",
"relationTarget": "Meal"
},
{
"id": "12:4842743668630588125",
"name": "mealSourceId",
"type": 11,
"flags": 520,
"indexId": "8:7798251372140622457",
"relationTarget": "MealSource"
},
{
"id": "13:2354087448211548018",
"name": "mealCategoryId",
"type": 11,
"flags": 520,
"indexId": "9:169838544472067864",
"relationTarget": "MealCategory"
},
{
"id": "14:6539714286499574550",
"name": "mealPortionTypeId",
"type": 11,
"flags": 520,
"indexId": "10:5017472742879643357",
"relationTarget": "MealPortionType"
},
{
"id": "15:6038336651358102122",
"name": "portionSizeAccuracyId",
"type": 11,
"flags": 520,
"indexId": "11:7222637893025102905",
"relationTarget": "Accuracy"
},
{
"id": "16:7121997990741934484",
"name": "carbsRatioAccuracyId",
"type": 11,
"flags": 520,
"indexId": "12:35287836658362611",
"relationTarget": "Accuracy"
}
],
"relations": []
},
{
"id": "10:382130101578692012",
"lastPropertyId": "13:4890778480468380841",
"name": "Meal",
"properties": [
{
"id": "1:612386612600420389",
"name": "id",
"type": 6,
"flags": 1
},
{
"id": "2:681065067668661250",
"name": "value",
"type": 9
},
{
"id": "3:2286464176896471045",
"name": "carbsRatio",
"type": 8
},
{
"id": "4:416562598768357192",
"name": "portionSize",
"type": 8
},
{
"id": "5:1888298906034647156",
"name": "carbsPerPortion",
"type": 8
},
{
"id": "6:3569732290918220814",
"name": "delayedBolusDuration",
"type": 6
},
{
"id": "7:2172890064639236018",
"name": "delayedBolusRate",
"type": 8
},
{
"id": "8:6111684052388229887",
"name": "notes",
"type": 9
},
{
"id": "9:5739692923906084863",
"name": "mealSourceId",
"type": 11,
"flags": 520,
"indexId": "13:5996617393160420536",
"relationTarget": "MealSource"
},
{
"id": "10:8193290820580187413",
"name": "mealCategoryId",
"type": 11,
"flags": 520,
"indexId": "14:2128681772675444526",
"relationTarget": "MealCategory"
},
{
"id": "11:3434302594951612479",
"name": "mealPortionTypeId",
"type": 11,
"flags": 520,
"indexId": "15:6211229960970898621",
"relationTarget": "MealPortionType"
},
{
"id": "12:7588894964037003621",
"name": "portionSizeAccuracyId",
"type": 11,
"flags": 520,
"indexId": "16:7050212577048329568",
"relationTarget": "Accuracy"
},
{
"id": "13:4890778480468380841",
"name": "carbsRatioAccuracyId",
"type": 11,
"flags": 520,
"indexId": "17:9108886538013386415",
"relationTarget": "Accuracy"
}
],
"relations": []
},
{
"id": "11:3158200688796904913",
"lastPropertyId": "3:3543757971350345683",
"name": "MealCategory",
"properties": [
{
"id": "1:3678943122076184840",
"name": "id",
"type": 6,
"flags": 1
},
{
"id": "2:1327341169479604917",
"name": "value",
"type": 9
},
{
"id": "3:3543757971350345683",
"name": "notes",
"type": 9
}
],
"relations": []
},
{
"id": "12:2111511899235985637",
"lastPropertyId": "3:1950852666001613408",
"name": "MealPortionType",
"properties": [
{
"id": "1:65428405312238271",
"name": "id",
"type": 6,
"flags": 1
},
{
"id": "2:5681230398840506311",
"name": "value",
"type": 9
},
{
"id": "3:1950852666001613408",
"name": "notes",
"type": 9
}
],
"relations": []
},
{
"id": "13:1283034494527412242",
"lastPropertyId": "7:5852853174931678667",
"name": "MealSource",
"properties": [
{
"id": "1:7205380295259922130",
"name": "id",
"type": 6,
"flags": 1
},
{
"id": "2:5768595544243621991",
"name": "value",
"type": 9
},
{
"id": "3:458760914712162612",
"name": "notes",
"type": 9
},
{
"id": "4:2034134758700899120",
"name": "defaultMealCategoryId",
"type": 11,
"flags": 520,
"indexId": "18:3223820950176501928",
"relationTarget": "MealCategory"
},
{
"id": "5:8956771998294100216",
"name": "defaultMealPortionTypeId",
"type": 11,
"flags": 520,
"indexId": "19:2932086504877178672",
"relationTarget": "MealPortionType"
},
{
"id": "6:7521960537811004317",
"name": "defaultCarbsRatioAccuracyId",
"type": 11,
"flags": 520,
"indexId": "20:2257356658656760706",
"relationTarget": "Accuracy"
},
{
"id": "7:5852853174931678667",
"name": "defaultPortionSizeAccuracyId",
"type": 11,
"flags": 520,
"indexId": "21:1931330716440762729",
"relationTarget": "Accuracy"
}
],
"relations": []
}
],
"lastEntityId": "13:1283034494527412242",
"lastIndexId": "21:1931330716440762729",
"lastRelationId": "0:0",
"lastSequenceId": "0:0",
"modelVersion": 5,
"modelVersionParserMinimum": 5,
"retiredEntityUids": [],
"retiredIndexUids": [],
"retiredPropertyUids": [],
"retiredRelationUids": [],
"version": 1
}

1694
lib/objectbox.g.dart Normal file

File diff suppressed because it is too large Load Diff

View File

@ -8,60 +8,64 @@ import 'package:diameter/models/accuracy.dart';
class AccuracyDetailScreen extends StatefulWidget { class AccuracyDetailScreen extends StatefulWidget {
static const String routeName = '/accuracy'; static const String routeName = '/accuracy';
final Accuracy? accuracy; final int id;
const AccuracyDetailScreen({Key? key, this.accuracy}) : super(key: key); const AccuracyDetailScreen({Key? key, this.id = 0}) : super(key: key);
@override @override
_AccuracyDetailScreenState createState() => _AccuracyDetailScreenState(); _AccuracyDetailScreenState createState() => _AccuracyDetailScreenState();
} }
class _AccuracyDetailScreenState extends State<AccuracyDetailScreen> { class _AccuracyDetailScreenState extends State<AccuracyDetailScreen> {
Accuracy? _accuracy;
bool _isNew = true;
bool _isSaving = false;
final GlobalKey<FormState> _accuracyForm = GlobalKey<FormState>(); final GlobalKey<FormState> _accuracyForm = GlobalKey<FormState>();
final _valueController = TextEditingController(text: ''); final _valueController = TextEditingController(text: '');
final _confidenceRatingController = TextEditingController(text: ''); final _confidenceRatingController = TextEditingController(text: '');
final _notesController = TextEditingController(text: ''); final _notesController = TextEditingController(text: '');
bool _forCarbsRatio = false; bool _forCarbsRatio = false;
bool _forPortionSize = false; bool _forPortionSize = false;
bool _isSaving = false;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
if (widget.accuracy != null) { reload();
_valueController.text = widget.accuracy!.value; if (_accuracy != null) {
_forCarbsRatio = widget.accuracy!.forCarbsRatio; _valueController.text = _accuracy!.value;
_forPortionSize = widget.accuracy!.forPortionSize; _forCarbsRatio = _accuracy!.forCarbsRatio;
_forPortionSize = _accuracy!.forPortionSize;
_confidenceRatingController.text = _confidenceRatingController.text =
(widget.accuracy!.confidenceRating ?? '').toString(); (_accuracy!.confidenceRating ?? '').toString();
_notesController.text = widget.accuracy!.notes ?? ''; _notesController.text = _accuracy!.notes ?? '';
} }
} }
void reload() {
if (widget.id != 0) {
setState(() {
_accuracy = Accuracy.get(widget.id);
});
}
_isNew = _accuracy == null;
}
void handleSaveAction() async { void handleSaveAction() async {
setState(() { setState(() {
_isSaving = true; _isSaving = true;
}); });
if (_accuracyForm.currentState!.validate()) { if (_accuracyForm.currentState!.validate()) {
bool isNew = widget.accuracy == null; Accuracy.box.put(Accuracy(
isNew id: widget.id,
? await Accuracy.save(
value: _valueController.text, value: _valueController.text,
forCarbsRatio: _forCarbsRatio, forCarbsRatio: _forCarbsRatio,
forPortionSize: _forPortionSize, forPortionSize: _forPortionSize,
confidenceRating: int.tryParse(_confidenceRatingController.text), confidenceRating: int.tryParse(_confidenceRatingController.text),
notes: _notesController.text, notes: _notesController.text,
) ));
: await Accuracy.update( Navigator.pop(context, '${_isNew ? 'New' : ''} Accuracy saved');
widget.accuracy!.objectId!,
value: _valueController.text,
forCarbsRatio: _forCarbsRatio,
forPortionSize: _forPortionSize,
confidenceRating: int.tryParse(_confidenceRatingController.text),
notes: _notesController.text,
);
Navigator.pop(context, '${isNew ? 'New' : ''} Accuracy saved');
} }
setState(() { setState(() {
_isSaving = false; _isSaving = false;
@ -69,25 +73,23 @@ class _AccuracyDetailScreenState extends State<AccuracyDetailScreen> {
} }
void handleCancelAction() { void handleCancelAction() {
bool isNew = widget.accuracy == null;
if (showConfirmationDialogOnCancel && if (showConfirmationDialogOnCancel &&
(isNew && (_isNew &&
(_forCarbsRatio || (_forCarbsRatio ||
_forPortionSize || _forPortionSize ||
_valueController.text != '' || _valueController.text != '' ||
int.tryParse(_confidenceRatingController.text) != null || int.tryParse(_confidenceRatingController.text) != null ||
_notesController.text != '')) || _notesController.text != '')) ||
(!isNew && (!_isNew &&
(_forCarbsRatio != widget.accuracy!.forCarbsRatio || (_forCarbsRatio != _accuracy!.forCarbsRatio ||
_forPortionSize != widget.accuracy!.forPortionSize || _forPortionSize != _accuracy!.forPortionSize ||
widget.accuracy!.value != _valueController.text || _accuracy!.value != _valueController.text ||
int.tryParse(_confidenceRatingController.text) != int.tryParse(_confidenceRatingController.text) !=
widget.accuracy!.confidenceRating || _accuracy!.confidenceRating ||
(widget.accuracy!.notes ?? '') != _notesController.text))) { (_accuracy!.notes ?? '') != _notesController.text))) {
Dialogs.showCancelConfirmationDialog( Dialogs.showCancelConfirmationDialog(
context: context, context: context,
isNew: isNew, isNew: _isNew,
onSave: handleSaveAction, onSave: handleSaveAction,
); );
} else { } else {
@ -97,10 +99,9 @@ class _AccuracyDetailScreenState extends State<AccuracyDetailScreen> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
bool isNew = widget.accuracy == null;
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text(isNew ? 'New Accuracy' : widget.accuracy!.value), title: Text(_isNew ? 'New Accuracy' : _accuracy!.value),
), ),
drawer: const Navigation(currentLocation: AccuracyDetailScreen.routeName), drawer: const Navigation(currentLocation: AccuracyDetailScreen.routeName),
body: SingleChildScrollView( body: SingleChildScrollView(

View File

@ -1,5 +1,4 @@
import 'package:diameter/components/dialogs.dart'; import 'package:diameter/components/dialogs.dart';
import 'package:diameter/components/progress_indicator.dart';
import 'package:diameter/config.dart'; import 'package:diameter/config.dart';
import 'package:diameter/navigation.dart'; import 'package:diameter/navigation.dart';
import 'package:diameter/screens/accuracy_detail.dart'; import 'package:diameter/screens/accuracy_detail.dart';
@ -15,11 +14,17 @@ class AccuracyListScreen extends StatefulWidget {
} }
class _AccuracyListScreenState extends State<AccuracyListScreen> { class _AccuracyListScreenState extends State<AccuracyListScreen> {
late Future<List<Accuracy>?> _accuracies; List<Accuracy> _accuracies = Accuracy.getAll();
@override
void initState() {
super.initState();
refresh();
}
void refresh({String? message}) { void refresh({String? message}) {
setState(() { setState(() {
_accuracies = Accuracy.fetchAll(); _accuracies = Accuracy.getAll();
}); });
setState(() { setState(() {
@ -36,7 +41,8 @@ class _AccuracyListScreenState extends State<AccuracyListScreen> {
} }
void onDelete(Accuracy accuracy) { void onDelete(Accuracy accuracy) {
accuracy.delete().then((_) => refresh(message: 'Accuracy deleted')); Accuracy.remove(accuracy.id);
refresh();
} }
void handleDeleteAction(Accuracy accuracy) async { void handleDeleteAction(Accuracy accuracy) async {
@ -52,24 +58,14 @@ class _AccuracyListScreenState extends State<AccuracyListScreen> {
} }
void handleToggleForPortionSizeAction(Accuracy accuracy) async { void handleToggleForPortionSizeAction(Accuracy accuracy) async {
await Accuracy.update( accuracy.forPortionSize = !accuracy.forPortionSize;
accuracy.objectId!, Accuracy.put(accuracy);
forPortionSize: !accuracy.forPortionSize,
);
refresh(); refresh();
} }
void handleToggleForCarbsRatioAction(Accuracy accuracy) async { void handleToggleForCarbsRatioAction(Accuracy accuracy) async {
await Accuracy.update( accuracy.forCarbsRatio = !accuracy.forCarbsRatio;
accuracy.objectId!, Accuracy.put(accuracy);
forCarbsRatio: !accuracy.forCarbsRatio,
);
refresh();
}
@override
void initState() {
super.initState();
refresh(); refresh();
} }
@ -87,39 +83,20 @@ class _AccuracyListScreenState extends State<AccuracyListScreen> {
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[ children: <Widget>[
Expanded( Expanded(
child: FutureBuilder<List<Accuracy>?>( child: _accuracies.isNotEmpty ? ListView.builder(
future: _accuracies,
builder: (context, snapshot) {
return ViewWithProgressIndicator(
snapshot: snapshot,
child: snapshot.data == null || snapshot.data!.isEmpty
? Row(
mainAxisAlignment: MainAxisAlignment.center,
children: const [
Padding(
padding: EdgeInsets.all(10.0),
child: Text('No Accuracies'),
)
],
)
: ListView.builder(
padding: const EdgeInsets.only(top: 10.0), padding: const EdgeInsets.only(top: 10.0),
itemCount: snapshot.data != null itemCount: _accuracies.length,
? snapshot.data!.length
: 0,
itemBuilder: (context, index) { itemBuilder: (context, index) {
final accuracy = snapshot.data![index]; final accuracy = _accuracies[index];
return ListTile( return ListTile(
onTap: () { onTap: () {
Navigator.push( Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => builder: (context) =>
AccuracyDetailScreen( AccuracyDetailScreen(id: accuracy.id),
accuracy: accuracy),
), ),
).then( ).then((message) => refresh(message: message));
(message) => refresh(message: message));
}, },
title: Text(accuracy.value), title: Text(accuracy.value),
leading: Row( leading: Row(
@ -137,36 +114,35 @@ class _AccuracyListScreenState extends State<AccuracyListScreen> {
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
IconButton( IconButton(
icon: Icon(Icons.square_foot, icon: Icon(
Icons.square_foot,
color: accuracy.forPortionSize color: accuracy.forPortionSize
? Colors.blue ? Theme.of(context).toggleableActiveColor
: Colors.grey.shade300), : Theme.of(context).highlightColor,
onPressed: () => ),
handleToggleForPortionSizeAction( onPressed: () => handleToggleForPortionSizeAction(accuracy),
accuracy)), ),
IconButton( IconButton(
icon: Icon(Icons.pie_chart, icon: Icon(
Icons.pie_chart,
color: accuracy.forCarbsRatio color: accuracy.forCarbsRatio
? Colors.blue ? Theme.of(context).toggleableActiveColor
: Colors.grey.shade300), : Theme.of(context).highlightColor,
onPressed: () => ),
handleToggleForCarbsRatioAction( onPressed: () => handleToggleForCarbsRatioAction(accuracy),
accuracy),
), ),
const SizedBox(width: 24), const SizedBox(width: 24),
IconButton( IconButton(
icon: const Icon( icon: const Icon(Icons.delete),
Icons.delete, onPressed: () => handleDeleteAction(accuracy),
color: Colors.blue,
),
onPressed: () =>
handleDeleteAction(accuracy),
) )
], ],
), ),
); );
})); }
}), ) : const Center(
child: Text('You have not created any Accuracies yet!'),
),
), ),
], ],
), ),
@ -176,7 +152,8 @@ class _AccuracyListScreenState extends State<AccuracyListScreen> {
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => const AccuracyDetailScreen(), builder: (context) => const AccuracyDetailScreen(),
)).then((message) => refresh(message: message)); ),
).then((message) => refresh(message: message));
}, },
child: const Icon(Icons.add), child: const Icon(Icons.add),
), ),

View File

@ -11,15 +11,15 @@ import 'package:diameter/models/basal_profile.dart';
class BasalDetailScreen extends StatefulWidget { class BasalDetailScreen extends StatefulWidget {
static const String routeName = '/basal'; static const String routeName = '/basal';
final BasalProfile basalProfile; final int basalProfileId;
final Basal? basal; final int id;
final TimeOfDay? suggestedStartTime; final TimeOfDay? suggestedStartTime;
final TimeOfDay? suggestedEndTime; final TimeOfDay? suggestedEndTime;
const BasalDetailScreen( const BasalDetailScreen(
{Key? key, {Key? key,
required this.basalProfile, this.basalProfileId = 0,
this.basal, this.id = 0,
this.suggestedStartTime, this.suggestedStartTime,
this.suggestedEndTime}) this.suggestedEndTime})
: super(key: key); : super(key: key);
@ -29,7 +29,12 @@ class BasalDetailScreen extends StatefulWidget {
} }
class _BasalDetailScreenState extends State<BasalDetailScreen> { class _BasalDetailScreenState extends State<BasalDetailScreen> {
Basal? _basal;
bool _isNew = true;
bool _isSaving = false;
final GlobalKey<FormState> _basalForm = GlobalKey<FormState>(); final GlobalKey<FormState> _basalForm = GlobalKey<FormState>();
TimeOfDay _startTime = const TimeOfDay(hour: 0, minute: 0); TimeOfDay _startTime = const TimeOfDay(hour: 0, minute: 0);
TimeOfDay _endTime = const TimeOfDay(hour: 0, minute: 0); TimeOfDay _endTime = const TimeOfDay(hour: 0, minute: 0);
@ -37,27 +42,37 @@ class _BasalDetailScreenState extends State<BasalDetailScreen> {
final _endTimeController = TextEditingController(text: ''); final _endTimeController = TextEditingController(text: '');
final _unitsController = TextEditingController(text: ''); final _unitsController = TextEditingController(text: '');
bool _isSaving = false;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
reload();
if (widget.suggestedStartTime != null) { if (widget.suggestedStartTime != null) {
_startTime = widget.suggestedStartTime!; _startTime = widget.suggestedStartTime!;
} }
if (widget.suggestedEndTime != null) { if (widget.suggestedEndTime != null) {
_endTime = widget.suggestedEndTime!; _endTime = widget.suggestedEndTime!;
} }
if (widget.basal != null) {
_startTime = TimeOfDay.fromDateTime(widget.basal!.startTime);
_endTime = TimeOfDay.fromDateTime(widget.basal!.endTime);
_unitsController.text = widget.basal!.units.toString(); if (_basal != null) {
_startTime = TimeOfDay.fromDateTime(_basal!.startTime);
_endTime = TimeOfDay.fromDateTime(_basal!.endTime);
_unitsController.text = _basal!.units.toString();
} }
updateStartTime(); updateStartTime();
updateEndTime(); updateEndTime();
} }
void reload() {
if (widget.id != 0) {
setState(() {
_basal = Basal.get(widget.id);
});
}
_isNew = _basal == null;
}
void updateStartTime() { void updateStartTime() {
_startTimeController.text = DateTimeUtils.displayTimeOfDay(_startTime); _startTimeController.text = DateTimeUtils.displayTimeOfDay(_startTime);
} }
@ -68,14 +83,13 @@ class _BasalDetailScreenState extends State<BasalDetailScreen> {
Future<String?> validateTimePeriod() async { Future<String?> validateTimePeriod() async {
String? error; String? error;
List<Basal> basalRates = List<Basal> basalRates = Basal.getAllForProfile(widget.basalProfileId);
await Basal.fetchAllForBasalProfile(widget.basalProfile);
// TODO use a query for the following checks instead?
// check for duplicates // check for duplicates
if (basalRates if (basalRates
.where((other) => .where((other) =>
(widget.basal == null || (widget.id != other.id) &&
widget.basal!.objectId != other.objectId) &&
_startTime.hour == other.startTime.hour && _startTime.hour == other.startTime.hour &&
_startTime.minute == other.startTime.minute) _startTime.minute == other.startTime.minute)
.isNotEmpty) { .isNotEmpty) {
@ -84,8 +98,7 @@ class _BasalDetailScreenState extends State<BasalDetailScreen> {
if (basalRates if (basalRates
.where((other) => .where((other) =>
(widget.basal == null || (widget.id != other.id) &&
widget.basal!.objectId != other.objectId) &&
DateTimeUtils.convertTimeOfDayToDateTime(_startTime) DateTimeUtils.convertTimeOfDayToDateTime(_startTime)
.isBefore(other.startTime) && .isBefore(other.startTime) &&
DateTimeUtils.convertTimeOfDayToDateTime(_endTime) DateTimeUtils.convertTimeOfDayToDateTime(_endTime)
@ -122,23 +135,15 @@ class _BasalDetailScreenState extends State<BasalDetailScreen> {
if (_basalForm.currentState!.validate()) { if (_basalForm.currentState!.validate()) {
await validateTimePeriod().then((value) async { await validateTimePeriod().then((value) async {
if (value != 'CANCEL') { if (value != 'CANCEL') {
bool isNew = widget.basal == null; Basal basal = Basal(
isNew id: widget.id,
? await Basal.save( startTime: DateTimeUtils.convertTimeOfDayToDateTime(_startTime),
startTime:
DateTimeUtils.convertTimeOfDayToDateTime(_startTime),
endTime: DateTimeUtils.convertTimeOfDayToDateTime(_endTime),
units: double.parse(_unitsController.text),
basalProfile: widget.basalProfile.objectId!,
)
: await Basal.update(
widget.basal!.objectId!,
startTime:
DateTimeUtils.convertTimeOfDayToDateTime(_startTime),
endTime: DateTimeUtils.convertTimeOfDayToDateTime(_endTime), endTime: DateTimeUtils.convertTimeOfDayToDateTime(_endTime),
units: double.parse(_unitsController.text), units: double.parse(_unitsController.text),
); );
Navigator.pop(context, '${isNew ? 'New' : ''} Basal Rate saved'); basal.basalProfile.targetId = widget.basalProfileId;
Basal.put(basal);
Navigator.pop(context, '${_isNew ? 'New' : ''} Basal Rate saved');
} }
}); });
} }
@ -148,24 +153,23 @@ class _BasalDetailScreenState extends State<BasalDetailScreen> {
} }
void handleCancelAction() { void handleCancelAction() {
bool isNew = widget.basal == null;
if (showConfirmationDialogOnCancel && if (showConfirmationDialogOnCancel &&
((isNew && ((_isNew &&
(_startTime.hour != (widget.suggestedStartTime?.hour ?? 0) || (_startTime.hour != (widget.suggestedStartTime?.hour ?? 0) ||
_endTime.hour != (widget.suggestedEndTime?.hour ?? 0) || _endTime.hour != (widget.suggestedEndTime?.hour ?? 0) ||
_startTime.minute != _startTime.minute !=
(widget.suggestedStartTime?.minute ?? 0) || (widget.suggestedStartTime?.minute ?? 0) ||
_endTime.minute != (widget.suggestedEndTime?.minute ?? 0) || _endTime.minute != (widget.suggestedEndTime?.minute ?? 0) ||
double.tryParse(_unitsController.text) != null)) || double.tryParse(_unitsController.text) != null)) ||
(!isNew && (!_isNew &&
(TimeOfDay.fromDateTime(widget.basal!.startTime) != (TimeOfDay.fromDateTime(_basal!.startTime) !=
_startTime || _startTime ||
TimeOfDay.fromDateTime(widget.basal!.endTime) != _endTime || TimeOfDay.fromDateTime(_basal!.endTime) != _endTime ||
(double.tryParse(_unitsController.text) ?? 0) != (double.tryParse(_unitsController.text) ?? 0) !=
widget.basal!.units)))) { _basal!.units)))) {
Dialogs.showCancelConfirmationDialog( Dialogs.showCancelConfirmationDialog(
context: context, context: context,
isNew: isNew, isNew: _isNew,
onSave: handleSaveAction, onSave: handleSaveAction,
); );
} else { } else {
@ -175,12 +179,10 @@ class _BasalDetailScreenState extends State<BasalDetailScreen> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
bool isNew = widget.basal == null;
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text( title: Text(
'${isNew ? 'New' : 'Edit'} Basal Rate for ${widget.basalProfile.name}'), '${_isNew ? 'New' : 'Edit'} Basal Rate for ${BasalProfile.get(widget.basalProfileId)?.name}'),
), ),
drawer: const Navigation(currentLocation: BasalDetailScreen.routeName), drawer: const Navigation(currentLocation: BasalDetailScreen.routeName),
body: Column( body: Column(

View File

@ -2,28 +2,30 @@ import 'package:diameter/components/dialogs.dart';
import 'package:diameter/config.dart'; import 'package:diameter/config.dart';
import 'package:diameter/utils/date_time_utils.dart'; import 'package:diameter/utils/date_time_utils.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:diameter/components/progress_indicator.dart';
import 'package:diameter/models/basal.dart'; import 'package:diameter/models/basal.dart';
import 'package:diameter/models/basal_profile.dart'; import 'package:diameter/models/basal_profile.dart';
import 'package:diameter/screens/basal/basal_detail.dart'; import 'package:diameter/screens/basal/basal_detail.dart';
class BasalListScreen extends StatefulWidget { class BasalListScreen extends StatefulWidget {
final BasalProfile? basalProfile; final BasalProfile basalProfile;
final List<Basal> basalRates;
final Function() reload;
const BasalListScreen({Key? key, this.basalProfile}) : super(key: key); const BasalListScreen(
{Key? key,
required this.basalProfile,
this.basalRates = const [],
required this.reload})
: super(key: key);
@override @override
_BasalListScreenState createState() => _BasalListScreenState(); _BasalListScreenState createState() => _BasalListScreenState();
} }
class _BasalListScreenState extends State<BasalListScreen> { class _BasalListScreenState extends State<BasalListScreen> {
void refresh({String? message}) { void reload({String? message}) {
setState(() { widget.reload();
if (widget.basalProfile != null) {
widget.basalProfile!.basalRates =
Basal.fetchAllForBasalProfile(widget.basalProfile!);
}
});
setState(() { setState(() {
if (message != null) { if (message != null) {
var snackBar = SnackBar( var snackBar = SnackBar(
@ -42,15 +44,16 @@ class _BasalListScreenState extends State<BasalListScreen> {
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => BasalDetailScreen( builder: (context) => BasalDetailScreen(
basalProfile: widget.basalProfile!, basalProfileId: widget.basalProfile.id,
basal: basal, id: basal.id,
), ),
), ),
).then((message) => refresh(message: message)); ).then((message) => reload(message: message));
} }
void onDelete(Basal basal) { void onDelete(Basal basal) {
basal.delete().then((_) => refresh(message: 'Basal Rate deleted')); Basal.remove(basal.id);
reload(message: 'Basal Rate deleted');
} }
void handleDeleteAction(Basal basal) async { void handleDeleteAction(Basal basal) async {
@ -65,9 +68,11 @@ class _BasalListScreenState extends State<BasalListScreen> {
} }
} }
String? validateTimePeriod(List<Basal> basalRates, int index) { String? validateTimePeriod(int index) {
List<Basal> basalRates = widget.basalRates;
Basal basal = basalRates[index]; Basal basal = basalRates[index];
// TODO: use queries for all this
// check for gaps // check for gaps
if (index == 0 && if (index == 0 &&
(basal.startTime.hour != 0 || basal.startTime.minute != 0)) { (basal.startTime.hour != 0 || basal.startTime.minute != 0)) {
@ -102,38 +107,19 @@ class _BasalListScreenState extends State<BasalListScreen> {
} }
} }
@override
void initState() {
super.initState();
refresh();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return SingleChildScrollView( return SingleChildScrollView(
child: Column( child: Column(
children: [ children: [
FutureBuilder<List<Basal>>( widget.basalRates.isNotEmpty ? ListView.builder(
future: widget.basalProfile!.basalRates,
builder: (context, snapshot) {
return ViewWithProgressIndicator(
snapshot: snapshot,
child: snapshot.data == null || snapshot.data!.isEmpty
? const Padding(
padding: EdgeInsets.all(10.0),
child: Text('No Basal Rates for this Profile'),
)
: ListView.builder(
shrinkWrap: true, shrinkWrap: true,
itemCount: itemCount: widget.basalRates.length,
snapshot.data != null ? snapshot.data!.length : 0,
itemBuilder: (context, index) { itemBuilder: (context, index) {
final basal = snapshot.data![index]; final basal = widget.basalRates[index];
final error = final error = validateTimePeriod(index);
validateTimePeriod(snapshot.data!, index);
return ListTile( return ListTile(
tileColor: tileColor: error != null ? Colors.red.shade100 : null,
error != null ? Colors.red.shade100 : null,
onTap: () { onTap: () {
handleEditAction(basal); handleEditAction(basal);
}, },
@ -148,8 +134,7 @@ class _BasalListScreenState extends State<BasalListScreen> {
], ],
), ),
subtitle: error != null subtitle: error != null
? Text(error, ? Text(error, style: const TextStyle(color: Colors.red))
style: const TextStyle(color: Colors.red))
: Container(), : Container(),
trailing: Row( trailing: Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
@ -165,9 +150,8 @@ class _BasalListScreenState extends State<BasalListScreen> {
), ),
); );
}, },
), ) : const Center(
); child: Text('You have not created any Basal Rates yet!'),
},
), ),
], ],
), ),

View File

@ -12,11 +12,10 @@ import 'package:diameter/screens/basal/basal_list.dart';
class BasalProfileDetailScreen extends StatefulWidget { class BasalProfileDetailScreen extends StatefulWidget {
static const String routeName = '/basal-profile'; static const String routeName = '/basal-profile';
final BasalProfile? basalProfile; final int id;
final bool active; final bool active;
const BasalProfileDetailScreen( const BasalProfileDetailScreen({Key? key, this.active = false, this.id = 0})
{Key? key, this.active = false, this.basalProfile})
: super(key: key); : super(key: key);
@override @override
@ -25,6 +24,11 @@ class BasalProfileDetailScreen extends StatefulWidget {
} }
class _BasalProfileDetailScreenState extends State<BasalProfileDetailScreen> { class _BasalProfileDetailScreenState extends State<BasalProfileDetailScreen> {
BasalProfile? _basalProfile;
List<Basal> _basalRates = [];
bool _isNew = true;
bool _isSaving = false;
final GlobalKey<FormState> _basalProfileForm = GlobalKey<FormState>(); final GlobalKey<FormState> _basalProfileForm = GlobalKey<FormState>();
late FloatingActionButton addBasalButton; late FloatingActionButton addBasalButton;
@ -40,28 +44,30 @@ class _BasalProfileDetailScreenState extends State<BasalProfileDetailScreen> {
final _notesController = TextEditingController(text: ''); final _notesController = TextEditingController(text: '');
bool _active = false; bool _active = false;
bool _isSaving = false;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
if (widget.basalProfile != null) {
_nameController.text = widget.basalProfile!.name; reload();
_active = widget.basalProfile!.active;
_notesController.text = widget.basalProfile!.notes ?? ''; if (_basalProfile != null) {
_nameController.text = _basalProfile!.name;
_active = _basalProfile!.active;
_notesController.text = _basalProfile!.notes ?? '';
} }
if (widget.active) { if (widget.active) {
_active = true; _active = true;
} }
addBasalButton = FloatingActionButton( addBasalButton = FloatingActionButton(
onPressed: handleAddNew, onPressed: handleAddNewBasal,
child: const Icon(Icons.add), child: const Icon(Icons.add),
); );
refreshButton = IconButton( refreshButton = IconButton(
icon: const Icon(Icons.refresh), icon: const Icon(Icons.refresh),
onPressed: refresh, onPressed: reload,
); );
closeButton = IconButton( closeButton = IconButton(
@ -79,13 +85,15 @@ class _BasalProfileDetailScreenState extends State<BasalProfileDetailScreen> {
bottomNav = detailBottomRow; bottomNav = detailBottomRow;
} }
void refresh({String? message}) { void reload({String? message}) {
if (widget.id != 0) {
setState(() { setState(() {
if (widget.basalProfile != null) { _basalProfile = BasalProfile.get(widget.id);
widget.basalProfile!.basalRates = _basalRates = Basal.getAllForProfile(widget.id);
Basal.fetchAllForBasalProfile(widget.basalProfile!);
}
}); });
_isNew = _basalProfile == null;
}
setState(() { setState(() {
if (message != null) { if (message != null) {
var snackBar = SnackBar( var snackBar = SnackBar(
@ -100,12 +108,11 @@ class _BasalProfileDetailScreenState extends State<BasalProfileDetailScreen> {
} }
Future<void> checkActiveProfiles() async { Future<void> checkActiveProfiles() async {
int _activeCount = await BasalProfile.getActiveCount(); int _activeCount = BasalProfile.activeCount();
bool isNew = widget.basalProfile == null;
if (_active && if (_active &&
(_activeCount > 1 || (_activeCount > 1 ||
_activeCount == 1 && (isNew || !widget.basalProfile!.active))) { _activeCount == 1 && (_isNew || !_basalProfile!.active))) {
await showDialog( await showDialog(
context: context, context: context,
builder: (BuildContext context) { builder: (BuildContext context) {
@ -134,12 +141,12 @@ class _BasalProfileDetailScreenState extends State<BasalProfileDetailScreen> {
_active = false; _active = false;
}); });
} else if (value == 2) { } else if (value == 2) {
await BasalProfile.setAllInactive(); BasalProfile.setAllInactive();
} }
}); });
} else if (!_active && } else if (!_active &&
((isNew && _activeCount == 0) || ((_isNew && _activeCount == 0) ||
(_activeCount == 1 && widget.basalProfile!.active))) { (_activeCount == 1 && _basalProfile!.active))) {
await showDialog( await showDialog(
context: context, context: context,
builder: (BuildContext context) { builder: (BuildContext context) {
@ -167,24 +174,22 @@ class _BasalProfileDetailScreenState extends State<BasalProfileDetailScreen> {
} }
} }
void handleAddNew() async { void handleAddNewBasal() async {
List<Basal> basalRates =
await Basal.fetchAllForBasalProfile(widget.basalProfile!);
TimeOfDay? suggestedStartTime; TimeOfDay? suggestedStartTime;
TimeOfDay? suggestedEndTime; TimeOfDay? suggestedEndTime;
basalRates.asMap().forEach((index, basal) { _basalRates.asMap().forEach((index, basal) {
if (suggestedStartTime == null && suggestedEndTime == null) { if (suggestedStartTime == null && suggestedEndTime == null) {
if (index == 0 && if (index == 0 &&
(basal.startTime.hour != 0 || basal.startTime.minute != 0)) { (basal.startTime.hour != 0 || basal.startTime.minute != 0)) {
suggestedStartTime = const TimeOfDay(hour: 0, minute: 0); suggestedStartTime = const TimeOfDay(hour: 0, minute: 0);
suggestedEndTime = TimeOfDay.fromDateTime(basal.startTime); suggestedEndTime = TimeOfDay.fromDateTime(basal.startTime);
} else if ((index == basalRates.length - 1) && } else if ((index == _basalRates.length - 1) &&
(basal.endTime.hour != 0 || basal.endTime.minute != 0)) { (basal.endTime.hour != 0 || basal.endTime.minute != 0)) {
suggestedStartTime = TimeOfDay.fromDateTime(basal.endTime); suggestedStartTime = TimeOfDay.fromDateTime(basal.endTime);
suggestedEndTime = const TimeOfDay(hour: 0, minute: 0); suggestedEndTime = const TimeOfDay(hour: 0, minute: 0);
} else if (index != 0) { } else if (index != 0) {
var lastEndTime = basalRates[index - 1].endTime; var lastEndTime = _basalRates[index - 1].endTime;
if (basal.startTime.isAfter(lastEndTime)) { if (basal.startTime.isAfter(lastEndTime)) {
suggestedStartTime = TimeOfDay.fromDateTime(lastEndTime); suggestedStartTime = TimeOfDay.fromDateTime(lastEndTime);
suggestedEndTime = TimeOfDay.fromDateTime(basal.startTime); suggestedEndTime = TimeOfDay.fromDateTime(basal.startTime);
@ -198,13 +203,13 @@ class _BasalProfileDetailScreenState extends State<BasalProfileDetailScreen> {
MaterialPageRoute( MaterialPageRoute(
builder: (context) { builder: (context) {
return BasalDetailScreen( return BasalDetailScreen(
basalProfile: widget.basalProfile!, basalProfileId: widget.id,
suggestedStartTime: suggestedStartTime, suggestedStartTime: suggestedStartTime,
suggestedEndTime: suggestedEndTime, suggestedEndTime: suggestedEndTime,
); );
}, },
), ),
).then((message) => refresh(message: message)); ).then((message) => reload(message: message));
} }
void handleSaveAction() async { void handleSaveAction() async {
@ -213,19 +218,13 @@ class _BasalProfileDetailScreenState extends State<BasalProfileDetailScreen> {
}); });
if (_basalProfileForm.currentState!.validate()) { if (_basalProfileForm.currentState!.validate()) {
await checkActiveProfiles(); await checkActiveProfiles();
bool isNew = widget.basalProfile == null; BasalProfile.put(BasalProfile(
isNew id: widget.id,
? await BasalProfile.save(
name: _nameController.text,
active: _active,
notes: _notesController.text)
: await BasalProfile.update(
widget.basalProfile!.objectId!,
name: _nameController.text, name: _nameController.text,
active: _active, active: _active,
notes: _notesController.text, notes: _notesController.text,
); ));
Navigator.pop(context, '${isNew ? 'New' : ''} Basal Profile saved'); Navigator.pop(context, '${_isNew ? 'New' : ''} Basal Profile saved');
} }
setState(() { setState(() {
_isSaving = false; _isSaving = false;
@ -233,20 +232,18 @@ class _BasalProfileDetailScreenState extends State<BasalProfileDetailScreen> {
} }
void handleCancelAction() { void handleCancelAction() {
bool isNew = widget.basalProfile == null;
if (showConfirmationDialogOnCancel && if (showConfirmationDialogOnCancel &&
(isNew && (_isNew &&
(_active != widget.active || (_active != widget.active ||
_nameController.text != '' || _nameController.text != '' ||
_notesController.text != '')) || _notesController.text != '')) ||
(!isNew && (!_isNew &&
(widget.basalProfile!.active != _active || (_basalProfile!.active != _active ||
widget.basalProfile!.name != _nameController.text || _basalProfile!.name != _nameController.text ||
(widget.basalProfile!.notes ?? '') != _notesController.text))) { (_basalProfile!.notes ?? '') != _notesController.text))) {
Dialogs.showCancelConfirmationDialog( Dialogs.showCancelConfirmationDialog(
context: context, context: context,
isNew: isNew, isNew: _isNew,
onSave: handleSaveAction, onSave: handleSaveAction,
); );
} else { } else {
@ -255,7 +252,7 @@ class _BasalProfileDetailScreenState extends State<BasalProfileDetailScreen> {
} }
void renderTabButtons(index) { void renderTabButtons(index) {
if (widget.basalProfile != null) { if (_basalProfile != null) {
setState(() { setState(() {
switch (index) { switch (index) {
case 1: case 1:
@ -274,9 +271,8 @@ class _BasalProfileDetailScreenState extends State<BasalProfileDetailScreen> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
bool isNew = widget.basalProfile == null;
return DefaultTabController( return DefaultTabController(
length: isNew ? 1 : 2, length: _isNew ? 1 : 2,
child: Builder(builder: (BuildContext context) { child: Builder(builder: (BuildContext context) {
final TabController tabController = DefaultTabController.of(context)!; final TabController tabController = DefaultTabController.of(context)!;
tabController.addListener(() { tabController.addListener(() {
@ -327,15 +323,14 @@ class _BasalProfileDetailScreenState extends State<BasalProfileDetailScreen> {
), ),
]; ];
if (!isNew) { if (!_isNew) {
tabs.add(BasalListScreen(basalProfile: widget.basalProfile)); tabs.add(BasalListScreen(basalProfile: _basalProfile!, basalRates: _basalRates, reload: reload));
} }
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: title: Text(_isNew ? 'New Basal Profile' : _basalProfile!.name),
Text(isNew ? 'New Basal Profile' : widget.basalProfile!.name), bottom: _isNew
bottom: isNew
? PreferredSize(child: Container(), preferredSize: Size.zero) ? PreferredSize(child: Container(), preferredSize: Size.zero)
: const TabBar( : const TabBar(
tabs: [ tabs: [
@ -350,8 +345,7 @@ class _BasalProfileDetailScreenState extends State<BasalProfileDetailScreen> {
body: TabBarView(children: tabs), body: TabBarView(children: tabs),
bottomNavigationBar: bottomNav, bottomNavigationBar: bottomNav,
floatingActionButton: actionButton, floatingActionButton: actionButton,
floatingActionButtonLocation: floatingActionButtonLocation: FloatingActionButtonLocation.endFloat,
FloatingActionButtonLocation.endFloat,
); );
}), }),
); );

View File

@ -2,7 +2,7 @@ import 'package:diameter/components/dialogs.dart';
import 'package:diameter/config.dart'; import 'package:diameter/config.dart';
import 'package:diameter/navigation.dart'; import 'package:diameter/navigation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:diameter/components/progress_indicator.dart'; // import 'package:diameter/components/progress_indicator.dart';
import 'package:diameter/models/basal_profile.dart'; import 'package:diameter/models/basal_profile.dart';
import 'package:diameter/screens/basal/basal_profile_detail.dart'; import 'package:diameter/screens/basal/basal_profile_detail.dart';
@ -15,18 +15,17 @@ class BasalProfileListScreen extends StatefulWidget {
} }
class _BasalProfileListScreenState extends State<BasalProfileListScreen> { class _BasalProfileListScreenState extends State<BasalProfileListScreen> {
late Future<List<BasalProfile>?> _basalProfiles; late List<BasalProfile> _basalProfiles;
Widget banner = Container(); Widget banner = Container();
bool pickActiveProfileMode = false; bool pickActiveProfileMode = false;
void refresh({String? message}) { void refresh({String? message}) {
setState(() { setState(() {
pickActiveProfileMode = false; pickActiveProfileMode = false;
_basalProfiles = BasalProfile.fetchAll(); _basalProfiles = BasalProfile.getAll();
}); });
_basalProfiles.then((list) => updateBanner( // _basalProfiles.then((list) =>
list?.where((element) => element.active).length ?? 0, updateBanner();
list?.isNotEmpty ?? false));
setState(() { setState(() {
if (message != null) { if (message != null) {
var snackBar = SnackBar( var snackBar = SnackBar(
@ -40,7 +39,8 @@ class _BasalProfileListScreenState extends State<BasalProfileListScreen> {
}); });
} }
void updateBanner(int activeProfileCount, bool isNotEmpty) { void updateBanner() {
int activeProfileCount = BasalProfile.activeCount();
setState(() { setState(() {
banner = activeProfileCount != 1 banner = activeProfileCount != 1
? MaterialBanner( ? MaterialBanner(
@ -51,7 +51,7 @@ class _BasalProfileListScreenState extends State<BasalProfileListScreen> {
forceActionsBelow: true, forceActionsBelow: true,
actions: activeProfileCount == 0 actions: activeProfileCount == 0
? [ ? [
isNotEmpty _basalProfiles.isNotEmpty
? TextButton( ? TextButton(
child: const Text('ACTIVATE A PROFILE'), child: const Text('ACTIVATE A PROFILE'),
onPressed: handlePickActiveProfileAction, onPressed: handlePickActiveProfileAction,
@ -74,9 +74,8 @@ class _BasalProfileListScreenState extends State<BasalProfileListScreen> {
} }
void onDelete(BasalProfile basalProfile) { void onDelete(BasalProfile basalProfile) {
basalProfile BasalProfile.remove(basalProfile.id);
.delete() refresh(message: 'Basal Profile deleted');
.then((_) => refresh(message: 'Basal Profile deleted'));
} }
void handleDeleteAction(BasalProfile basalProfile) async { void handleDeleteAction(BasalProfile basalProfile) async {
@ -92,10 +91,11 @@ class _BasalProfileListScreenState extends State<BasalProfileListScreen> {
} }
void onPickActive(BasalProfile basalProfile) { void onPickActive(BasalProfile basalProfile) {
BasalProfile.setAllInactive(exception: basalProfile.objectId!).then((_) => BasalProfile.setAllInactive;
refresh( basalProfile.active = true;
message: BasalProfile.put(basalProfile);
'${basalProfile.name} has been set as your active Profile')); // (exception: basalProfile.objectId!).then((_) =>
refresh(message: '${basalProfile.name} has been set as your active Profile');
} }
void handlePickActiveProfileAction() { void handlePickActiveProfileAction() {
@ -120,7 +120,7 @@ class _BasalProfileListScreenState extends State<BasalProfileListScreen> {
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => BasalProfileDetailScreen( builder: (context) => BasalProfileDetailScreen(
basalProfile: basalProfile, active: active), id: basalProfile?.id ?? 0, active: active),
), ),
).then((message) => refresh(message: message)); ).then((message) => refresh(message: message));
} }
@ -155,25 +155,11 @@ class _BasalProfileListScreenState extends State<BasalProfileListScreen> {
children: [ children: [
banner, banner,
Expanded( Expanded(
child: FutureBuilder<List<BasalProfile>?>( child: _basalProfiles.isNotEmpty ? ListView.builder(
future: _basalProfiles, itemCount: _basalProfiles.length,
builder: (context, snapshot) {
return ViewWithProgressIndicator(
snapshot: snapshot,
child: snapshot.data == null || snapshot.data!.isEmpty
? Row(
mainAxisAlignment: MainAxisAlignment.center,
children: const [
Padding(
padding: EdgeInsets.all(10.0),
child: Text('No Basal Profiles'),
),
])
: ListView.builder(
itemCount:
snapshot.data != null ? snapshot.data!.length : 0,
itemBuilder: (context, index) { itemBuilder: (context, index) {
final basalProfile = snapshot.data![index]; final basalProfile = _basalProfiles[index];
return ListTile( return ListTile(
tileColor: basalProfile.active tileColor: basalProfile.active
? Colors.green.shade100 ? Colors.green.shade100
@ -202,9 +188,8 @@ class _BasalProfileListScreenState extends State<BasalProfileListScreen> {
), ),
); );
}, },
), ) : const Center(
); child: Text('You have not created any Basal Profiles yet!'),
},
), ),
), ),
], ],

View File

@ -13,15 +13,15 @@ import 'package:diameter/models/bolus_profile.dart';
class BolusDetailScreen extends StatefulWidget { class BolusDetailScreen extends StatefulWidget {
static const String routeName = '/bolus'; static const String routeName = '/bolus';
final BolusProfile bolusProfile; final int bolusProfileId;
final Bolus? bolus; final int id;
final TimeOfDay? suggestedStartTime; final TimeOfDay? suggestedStartTime;
final TimeOfDay? suggestedEndTime; final TimeOfDay? suggestedEndTime;
const BolusDetailScreen( const BolusDetailScreen(
{Key? key, {Key? key,
required this.bolusProfile, this.bolusProfileId = 0,
this.bolus, this.id = 0,
this.suggestedStartTime, this.suggestedStartTime,
this.suggestedEndTime}) this.suggestedEndTime})
: super(key: key); : super(key: key);
@ -31,6 +31,10 @@ class BolusDetailScreen extends StatefulWidget {
} }
class _BolusDetailScreenState extends State<BolusDetailScreen> { class _BolusDetailScreenState extends State<BolusDetailScreen> {
Bolus? _bolus;
bool _isNew = true;
bool _isSaving = false;
final GlobalKey<FormState> _bolusForm = GlobalKey<FormState>(); final GlobalKey<FormState> _bolusForm = GlobalKey<FormState>();
TimeOfDay _startTime = const TimeOfDay(hour: 0, minute: 0); TimeOfDay _startTime = const TimeOfDay(hour: 0, minute: 0);
@ -43,30 +47,41 @@ class _BolusDetailScreenState extends State<BolusDetailScreen> {
final _mgPerDlController = TextEditingController(text: ''); final _mgPerDlController = TextEditingController(text: '');
final _mmolPerLController = TextEditingController(text: ''); final _mmolPerLController = TextEditingController(text: '');
bool _isSaving = false;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
reload();
if (widget.suggestedStartTime != null) { if (widget.suggestedStartTime != null) {
_startTime = widget.suggestedStartTime!; _startTime = widget.suggestedStartTime!;
} }
if (widget.suggestedEndTime != null) { if (widget.suggestedEndTime != null) {
_endTime = widget.suggestedEndTime!; _endTime = widget.suggestedEndTime!;
} }
if (widget.bolus != null) {
_startTime = TimeOfDay.fromDateTime(widget.bolus!.startTime);
_endTime = TimeOfDay.fromDateTime(widget.bolus!.endTime);
_unitsController.text = widget.bolus!.units.toString(); if (_bolus != null) {
_carbsController.text = widget.bolus!.carbs.toString(); _startTime = TimeOfDay.fromDateTime(_bolus!.startTime);
_mgPerDlController.text = widget.bolus!.mgPerDl.toString(); _endTime = TimeOfDay.fromDateTime(_bolus!.endTime);
_mmolPerLController.text = widget.bolus!.mmolPerL.toString();
_unitsController.text = _bolus!.units.toString();
_carbsController.text = _bolus!.carbs.toString();
_mgPerDlController.text = _bolus!.mgPerDl.toString();
_mmolPerLController.text = _bolus!.mmolPerL.toString();
} }
updateStartTime(); updateStartTime();
updateEndTime(); updateEndTime();
} }
void reload() {
if (widget.id != 0) {
setState(() {
_bolus = Bolus.get(widget.id);
});
}
_isNew = _bolus == null;
}
void updateStartTime() { void updateStartTime() {
_startTimeController.text = DateTimeUtils.displayTimeOfDay(_startTime); _startTimeController.text = DateTimeUtils.displayTimeOfDay(_startTime);
} }
@ -77,14 +92,14 @@ class _BolusDetailScreenState extends State<BolusDetailScreen> {
Future<String?> validateTimePeriod() async { Future<String?> validateTimePeriod() async {
String? error; String? error;
List<Bolus> bolusRates = List<Bolus> bolusRates = Bolus.getAllForProfile(widget.bolusProfileId);
await Bolus.fetchAllForBolusProfile(widget.bolusProfile); // BolusProfile.get(widget.bolusProfileId)?.bolusRates ?? [];
// TODO use a query for the following checks instead?
// check for duplicates // check for duplicates
if (bolusRates if (bolusRates
.where((other) => .where((other) =>
(widget.bolus == null || widget.id != other.id &&
widget.bolus!.objectId != other.objectId) &&
_startTime.hour == other.startTime.hour && _startTime.hour == other.startTime.hour &&
_startTime.minute == other.startTime.minute) _startTime.minute == other.startTime.minute)
.isNotEmpty) { .isNotEmpty) {
@ -93,8 +108,7 @@ class _BolusDetailScreenState extends State<BolusDetailScreen> {
if (bolusRates if (bolusRates
.where((other) => .where((other) =>
(widget.bolus == null || (widget.id != other.id) &&
widget.bolus!.objectId != other.objectId) &&
DateTimeUtils.convertTimeOfDayToDateTime(_startTime) DateTimeUtils.convertTimeOfDayToDateTime(_startTime)
.isBefore(other.startTime) && .isBefore(other.startTime) &&
DateTimeUtils.convertTimeOfDayToDateTime(_endTime) DateTimeUtils.convertTimeOfDayToDateTime(_endTime)
@ -128,44 +142,34 @@ class _BolusDetailScreenState extends State<BolusDetailScreen> {
setState(() { setState(() {
_isSaving = true; _isSaving = true;
}); });
if (_bolusForm.currentState!.validate()) { if (_bolusForm.currentState!.validate()) {
await validateTimePeriod().then((value) async { await validateTimePeriod().then((value) async {
if (value != 'CANCEL') { if (value != 'CANCEL') {
bool isNew = widget.bolus == null; Bolus bolus = Bolus(
isNew id: widget.id,
? await Bolus.save( startTime: DateTimeUtils.convertTimeOfDayToDateTime(_startTime),
startTime:
DateTimeUtils.convertTimeOfDayToDateTime(_startTime),
endTime: DateTimeUtils.convertTimeOfDayToDateTime(_endTime), endTime: DateTimeUtils.convertTimeOfDayToDateTime(_endTime),
units: double.parse(_unitsController.text), units: double.tryParse(_unitsController.text) ?? 0,
bolusProfile: widget.bolusProfile.objectId!, carbs: double.tryParse(_carbsController.text) ?? 0,
carbs: double.parse(_carbsController.text),
mgPerDl: int.tryParse(_mgPerDlController.text),
mmolPerL: double.tryParse(_mmolPerLController.text),
)
: await Bolus.update(
widget.bolus!.objectId!,
startTime:
DateTimeUtils.convertTimeOfDayToDateTime(_startTime),
endTime: DateTimeUtils.convertTimeOfDayToDateTime(_endTime),
units: double.tryParse(_unitsController.text),
carbs: double.tryParse(_carbsController.text),
mgPerDl: int.tryParse(_mgPerDlController.text), mgPerDl: int.tryParse(_mgPerDlController.text),
mmolPerL: double.parse(_mmolPerLController.text), mmolPerL: double.parse(_mmolPerLController.text),
); );
Navigator.pop(context, '${isNew ? 'New' : ''} Bolus Rate saved'); bolus.bolusProfile.targetId = widget.bolusProfileId;
Bolus.put(bolus);
Navigator.pop(context, '${_isNew ? 'New' : ''} Bolus Rate saved');
} }
}); });
} }
setState(() { setState(() {
_isSaving = false; _isSaving = false;
}); });
} }
void handleCancelAction() { void handleCancelAction() {
bool isNew = widget.bolus == null;
if (showConfirmationDialogOnCancel && if (showConfirmationDialogOnCancel &&
((isNew && ((_isNew &&
(_startTime.hour != (widget.suggestedStartTime?.hour ?? 0) || (_startTime.hour != (widget.suggestedStartTime?.hour ?? 0) ||
_endTime.hour != (widget.suggestedEndTime?.hour ?? 0) || _endTime.hour != (widget.suggestedEndTime?.hour ?? 0) ||
_startTime.minute != _startTime.minute !=
@ -175,21 +179,20 @@ class _BolusDetailScreenState extends State<BolusDetailScreen> {
(double.tryParse(_carbsController.text) ?? 0) != 0.0 || (double.tryParse(_carbsController.text) ?? 0) != 0.0 ||
(int.tryParse(_mgPerDlController.text) ?? 0) != 0 || (int.tryParse(_mgPerDlController.text) ?? 0) != 0 ||
(double.tryParse(_mmolPerLController.text) ?? 0) != 0.0)) || (double.tryParse(_mmolPerLController.text) ?? 0) != 0.0)) ||
(!isNew && (!_isNew &&
(TimeOfDay.fromDateTime(widget.bolus!.startTime) != (TimeOfDay.fromDateTime(_bolus!.startTime) != _startTime ||
_startTime || TimeOfDay.fromDateTime(_bolus!.endTime) != _endTime ||
TimeOfDay.fromDateTime(widget.bolus!.endTime) != _endTime ||
(double.tryParse(_unitsController.text) ?? 0) != (double.tryParse(_unitsController.text) ?? 0) !=
widget.bolus!.units || _bolus!.units ||
(double.tryParse(_carbsController.text) ?? 0) != (double.tryParse(_carbsController.text) ?? 0) !=
widget.bolus!.carbs || _bolus!.carbs ||
(double.tryParse(_mgPerDlController.text) ?? 0) != (double.tryParse(_mgPerDlController.text) ?? 0) !=
widget.bolus!.mgPerDl || _bolus!.mgPerDl ||
(double.tryParse(_mmolPerLController.text) ?? 0) != (double.tryParse(_mmolPerLController.text) ?? 0) !=
widget.bolus!.mmolPerL)))) { _bolus!.mmolPerL)))) {
Dialogs.showCancelConfirmationDialog( Dialogs.showCancelConfirmationDialog(
context: context, context: context,
isNew: isNew, isNew: _isNew,
onSave: handleSaveAction, onSave: handleSaveAction,
); );
} else { } else {
@ -226,12 +229,10 @@ class _BolusDetailScreenState extends State<BolusDetailScreen> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
bool isNew = widget.bolus == null;
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text( title: Text(
'${isNew ? 'New' : 'Edit'} Bolus Rate for ${widget.bolusProfile.name}'), '${_isNew ? 'New' : 'Edit'} Bolus Rate for ${BolusProfile.get(widget.bolusProfileId)?.name}'),
), ),
drawer: const Navigation(currentLocation: BolusDetailScreen.routeName), drawer: const Navigation(currentLocation: BolusDetailScreen.routeName),
body: SingleChildScrollView( body: SingleChildScrollView(

View File

@ -3,28 +3,27 @@ import 'package:diameter/config.dart';
import 'package:diameter/settings.dart'; import 'package:diameter/settings.dart';
import 'package:diameter/utils/date_time_utils.dart'; import 'package:diameter/utils/date_time_utils.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:diameter/components/progress_indicator.dart';
import 'package:diameter/models/bolus.dart'; import 'package:diameter/models/bolus.dart';
import 'package:diameter/models/bolus_profile.dart'; import 'package:diameter/models/bolus_profile.dart';
import 'package:diameter/screens/bolus/bolus_detail.dart'; import 'package:diameter/screens/bolus/bolus_detail.dart';
class BolusListScreen extends StatefulWidget { class BolusListScreen extends StatefulWidget {
final BolusProfile? bolusProfile; final BolusProfile bolusProfile;
final List<Bolus> bolusRates;
final Function() reload;
const BolusListScreen({Key? key, this.bolusProfile}) : super(key: key); const BolusListScreen(
{Key? key, required this.bolusProfile, this.bolusRates = const [], required this.reload})
: super(key: key);
@override @override
_BolusListScreenState createState() => _BolusListScreenState(); _BolusListScreenState createState() => _BolusListScreenState();
} }
class _BolusListScreenState extends State<BolusListScreen> { class _BolusListScreenState extends State<BolusListScreen> {
void refresh({String? message}) { void reload({String? message}) {
setState(() { widget.reload();
if (widget.bolusProfile != null) {
widget.bolusProfile!.bolusRates =
Bolus.fetchAllForBolusProfile(widget.bolusProfile!);
}
});
setState(() { setState(() {
if (message != null) { if (message != null) {
var snackBar = SnackBar( var snackBar = SnackBar(
@ -43,15 +42,16 @@ class _BolusListScreenState extends State<BolusListScreen> {
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => BolusDetailScreen( builder: (context) => BolusDetailScreen(
bolusProfile: widget.bolusProfile!, bolusProfileId: widget.bolusProfile.id,
bolus: bolus, id: bolus.id,
), ),
), ),
).then((message) => refresh(message: message)); ).then((message) => reload(message: message));
} }
void onDelete(Bolus bolus) { void onDelete(Bolus bolus) {
bolus.delete().then((_) => refresh(message: 'Bolus Rate deleted')); Bolus.remove(bolus.id);
reload(message: 'Bolus Rate deleted');
} }
void handleDeleteAction(Bolus bolus) async { void handleDeleteAction(Bolus bolus) async {
@ -66,9 +66,11 @@ class _BolusListScreenState extends State<BolusListScreen> {
} }
} }
String? validateTimePeriod(List<Bolus> bolusRates, int index) { String? validateTimePeriod(int index) {
List<Bolus> bolusRates = widget.bolusRates;
Bolus bolus = bolusRates[index]; Bolus bolus = bolusRates[index];
// TODO: use queries for all this
// check for gaps // check for gaps
if (index == 0 && if (index == 0 &&
(bolus.startTime.toLocal().hour != 0 || bolus.startTime.minute != 0)) { (bolus.startTime.toLocal().hour != 0 || bolus.startTime.minute != 0)) {
@ -103,40 +105,21 @@ class _BolusListScreenState extends State<BolusListScreen> {
} }
} }
@override
void initState() {
super.initState();
refresh();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return SingleChildScrollView( return SingleChildScrollView(
padding: const EdgeInsets.only(top: 10.0), padding: const EdgeInsets.only(top: 10.0),
child: Column( child: Column(
children: [ children: [
FutureBuilder<List<Bolus>>( widget.bolusRates.isNotEmpty ? ListView.builder(
future: widget.bolusProfile!.bolusRates,
builder: (context, snapshot) {
return ViewWithProgressIndicator(
snapshot: snapshot,
child: snapshot.data == null || snapshot.data!.isEmpty
? const Padding(
padding: EdgeInsets.all(10.0),
child: Text('No Basal Rates for this Profile'),
)
: ListView.builder(
shrinkWrap: true, shrinkWrap: true,
itemCount: itemCount: widget.bolusRates.length,
snapshot.data != null ? snapshot.data!.length : 0,
itemBuilder: (context, index) { itemBuilder: (context, index) {
final bolus = snapshot.data![index]; final bolus = widget.bolusRates[index];
final error = final error = validateTimePeriod(index);
validateTimePeriod(snapshot.data!, index);
return ListTile( return ListTile(
isThreeLine: true, isThreeLine: true,
tileColor: tileColor: error != null ? Colors.red.shade100 : null,
error != null ? Colors.red.shade100 : null,
onTap: () { onTap: () {
handleEditAction(bolus); handleEditAction(bolus);
}, },
@ -150,8 +133,7 @@ class _BolusListScreenState extends State<BolusListScreen> {
'${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}${nutritionMeasurement == NutritionMeasurement.grams ? ' g' : ' oz'} carbs/${glucoseMeasurement == GlucoseMeasurement.mgPerDl ? bolus.mgPerDl : bolus.mmolPerL} ${glucoseMeasurement == GlucoseMeasurement.mgPerDl ? 'mg/dl' : 'mmol/l'}'),
error != null error != null
? Text(error, ? Text(error,
style: const TextStyle( style: const TextStyle(color: Colors.red))
color: Colors.red))
: const Text('') : const Text('')
]), ]),
trailing: Row( trailing: Row(
@ -168,9 +150,8 @@ class _BolusListScreenState extends State<BolusListScreen> {
), ),
); );
}, },
), ) : const Center(
); child: Text('You have not created any Bolus Rates yet!'),
},
), ),
], ],
), ),

View File

@ -11,12 +11,10 @@ import 'package:diameter/screens/bolus/bolus_list.dart';
class BolusProfileDetailScreen extends StatefulWidget { class BolusProfileDetailScreen extends StatefulWidget {
static const String routeName = '/bolus-profile'; static const String routeName = '/bolus-profile';
final int id;
final BolusProfile? bolusProfile;
final bool active; final bool active;
const BolusProfileDetailScreen( const BolusProfileDetailScreen({Key? key, this.active = false, this.id = 0})
{Key? key, this.active = false, this.bolusProfile})
: super(key: key); : super(key: key);
@override @override
@ -25,6 +23,11 @@ class BolusProfileDetailScreen extends StatefulWidget {
} }
class _BolusProfileDetailScreenState extends State<BolusProfileDetailScreen> { class _BolusProfileDetailScreenState extends State<BolusProfileDetailScreen> {
BolusProfile? _bolusProfile;
List<Bolus> _bolusRates = [];
bool _isNew = true;
bool _isSaving = false;
final GlobalKey<FormState> _bolusProfileForm = GlobalKey<FormState>(); final GlobalKey<FormState> _bolusProfileForm = GlobalKey<FormState>();
late FloatingActionButton addBolusButton; late FloatingActionButton addBolusButton;
@ -40,28 +43,29 @@ class _BolusProfileDetailScreenState extends State<BolusProfileDetailScreen> {
final _notesController = TextEditingController(text: ''); final _notesController = TextEditingController(text: '');
bool _active = false; bool _active = false;
bool _isSaving = false;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
if (widget.bolusProfile != null) {
_nameController.text = widget.bolusProfile!.name; reload();
_active = widget.bolusProfile!.active;
_notesController.text = widget.bolusProfile!.notes ?? ''; if (_bolusProfile != null) {
_nameController.text = _bolusProfile!.name;
_active = _bolusProfile!.active;
_notesController.text = _bolusProfile!.notes ?? '';
} }
if (widget.active) { if (widget.active) {
_active = true; _active = true;
} }
addBolusButton = FloatingActionButton( addBolusButton = FloatingActionButton(
onPressed: handleAddNew, onPressed: handleAddNewBolus,
child: const Icon(Icons.add), child: const Icon(Icons.add),
); );
refreshButton = IconButton( refreshButton = IconButton(
icon: const Icon(Icons.refresh), icon: const Icon(Icons.refresh),
onPressed: refresh, onPressed: reload,
); );
closeButton = IconButton( closeButton = IconButton(
@ -80,13 +84,15 @@ class _BolusProfileDetailScreenState extends State<BolusProfileDetailScreen> {
bottomNav = detailBottomRow; bottomNav = detailBottomRow;
} }
void refresh({String? message}) { void reload({String? message}) {
if (widget.id != 0) {
setState(() { setState(() {
if (widget.bolusProfile != null) { _bolusProfile = BolusProfile.get(widget.id);
widget.bolusProfile!.bolusRates = _bolusRates = Bolus.getAllForProfile(widget.id);
Bolus.fetchAllForBolusProfile(widget.bolusProfile!);
}
}); });
_isNew = _bolusProfile == null;
}
setState(() { setState(() {
if (message != null) { if (message != null) {
var snackBar = SnackBar( var snackBar = SnackBar(
@ -101,12 +107,11 @@ class _BolusProfileDetailScreenState extends State<BolusProfileDetailScreen> {
} }
Future<void> checkActiveProfiles() async { Future<void> checkActiveProfiles() async {
int _activeCount = await BolusProfile.getActiveCount(); int _activeCount = BolusProfile.activeCount();
bool isNew = widget.bolusProfile == null;
if (_active && if (_active &&
(_activeCount > 1 || (_activeCount > 1 ||
_activeCount == 1 && (isNew || !widget.bolusProfile!.active))) { _activeCount == 1 && (_isNew || !_bolusProfile!.active))) {
await showDialog( await showDialog(
context: context, context: context,
builder: (BuildContext context) { builder: (BuildContext context) {
@ -120,8 +125,7 @@ class _BolusProfileDetailScreenState extends State<BolusProfileDetailScreen> {
), ),
TextButton( TextButton(
onPressed: () => Navigator.pop(context, 1), onPressed: () => Navigator.pop(context, 1),
child: child: const Text('DEACTIVATE THIS PROFILE'),
Text('DEACTIVATE ${_nameController.text.toUpperCase()}'),
), ),
ElevatedButton( ElevatedButton(
onPressed: () => Navigator.pop(context, 2), onPressed: () => Navigator.pop(context, 2),
@ -135,12 +139,12 @@ class _BolusProfileDetailScreenState extends State<BolusProfileDetailScreen> {
_active = false; _active = false;
}); });
} else if (value == 2) { } else if (value == 2) {
await BolusProfile.setAllInactive(); BolusProfile.setAllInactive();
} }
}); });
} else if (!_active && } else if (!_active &&
((isNew && _activeCount == 0) || ((_isNew && _activeCount == 0) ||
(_activeCount == 1 && widget.bolusProfile!.active))) { (_activeCount == 1 && _bolusProfile!.active))) {
await showDialog( await showDialog(
context: context, context: context,
builder: (BuildContext context) { builder: (BuildContext context) {
@ -168,24 +172,22 @@ class _BolusProfileDetailScreenState extends State<BolusProfileDetailScreen> {
} }
} }
void handleAddNew() async { void handleAddNewBolus() async {
List<Bolus> bolusRates =
await Bolus.fetchAllForBolusProfile(widget.bolusProfile!);
TimeOfDay? suggestedStartTime; TimeOfDay? suggestedStartTime;
TimeOfDay? suggestedEndTime; TimeOfDay? suggestedEndTime;
bolusRates.asMap().forEach((index, bolus) { _bolusRates.asMap().forEach((index, bolus) {
if (suggestedStartTime == null && suggestedEndTime == null) { if (suggestedStartTime == null && suggestedEndTime == null) {
if (index == 0 && if (index == 0 &&
(bolus.startTime.hour != 0 || bolus.startTime.minute != 0)) { (bolus.startTime.hour != 0 || bolus.startTime.minute != 0)) {
suggestedStartTime = const TimeOfDay(hour: 0, minute: 0); suggestedStartTime = const TimeOfDay(hour: 0, minute: 0);
suggestedEndTime = TimeOfDay.fromDateTime(bolus.startTime); suggestedEndTime = TimeOfDay.fromDateTime(bolus.startTime);
} else if ((index == bolusRates.length - 1) && } else if ((index == _bolusRates.length - 1) &&
(bolus.endTime.hour != 0 || bolus.endTime.minute != 0)) { (bolus.endTime.hour != 0 || bolus.endTime.minute != 0)) {
suggestedStartTime = TimeOfDay.fromDateTime(bolus.endTime); suggestedStartTime = TimeOfDay.fromDateTime(bolus.endTime);
suggestedEndTime = const TimeOfDay(hour: 0, minute: 0); suggestedEndTime = const TimeOfDay(hour: 0, minute: 0);
} else if (index != 0) { } else if (index != 0) {
var lastEndTime = bolusRates[index - 1].endTime; var lastEndTime = _bolusRates[index - 1].endTime;
if (bolus.startTime.isAfter(lastEndTime)) { if (bolus.startTime.isAfter(lastEndTime)) {
suggestedStartTime = TimeOfDay.fromDateTime(lastEndTime); suggestedStartTime = TimeOfDay.fromDateTime(lastEndTime);
suggestedEndTime = TimeOfDay.fromDateTime(bolus.startTime); suggestedEndTime = TimeOfDay.fromDateTime(bolus.startTime);
@ -199,55 +201,51 @@ class _BolusProfileDetailScreenState extends State<BolusProfileDetailScreen> {
MaterialPageRoute( MaterialPageRoute(
builder: (context) { builder: (context) {
return BolusDetailScreen( return BolusDetailScreen(
bolusProfile: widget.bolusProfile!, bolusProfileId: widget.id,
suggestedStartTime: suggestedStartTime, suggestedStartTime: suggestedStartTime,
suggestedEndTime: suggestedEndTime, suggestedEndTime: suggestedEndTime,
); );
}, },
), ),
).then((message) => refresh(message: message)); ).then((message) => reload(message: message));
} }
void handleSaveAction() async { void handleSaveAction() async {
setState(() { setState(() {
_isSaving = true; _isSaving = true;
}); });
if (_bolusProfileForm.currentState!.validate()) { if (_bolusProfileForm.currentState!.validate()) {
await checkActiveProfiles(); await checkActiveProfiles();
bool isNew = widget.bolusProfile == null; BolusProfile.put(
isNew BolusProfile(
? await BolusProfile.save( id: widget.id,
name: _nameController.text,
active: _active,
notes: _notesController.text)
: await BolusProfile.update(
widget.bolusProfile!.objectId!,
name: _nameController.text, name: _nameController.text,
active: _active, active: _active,
notes: _notesController.text, notes: _notesController.text,
),
); );
Navigator.pop(context, '${isNew ? 'New' : ''} Bolus Profile saved'); Navigator.pop(context, '${_isNew ? 'New' : ''} Bolus Profile saved');
} }
setState(() { setState(() {
_isSaving = false; _isSaving = false;
}); });
} }
void handleCancelAction() { void handleCancelAction() {
bool isNew = widget.bolusProfile == null;
if (showConfirmationDialogOnCancel && if (showConfirmationDialogOnCancel &&
(isNew && (_isNew &&
(_active != widget.active || (_active != widget.active ||
_nameController.text != '' || _nameController.text != '' ||
_notesController.text != '')) || _notesController.text != '')) ||
(!isNew && (!_isNew &&
(widget.bolusProfile!.active != _active || (_bolusProfile!.active != _active ||
widget.bolusProfile!.name != _nameController.text || _bolusProfile!.name != _nameController.text ||
(widget.bolusProfile!.notes ?? '') != _notesController.text))) { (_bolusProfile!.notes ?? '') != _notesController.text))) {
Dialogs.showCancelConfirmationDialog( Dialogs.showCancelConfirmationDialog(
context: context, context: context,
isNew: isNew, isNew: _isNew,
onSave: handleSaveAction, onSave: handleSaveAction,
); );
} else { } else {
@ -256,7 +254,7 @@ class _BolusProfileDetailScreenState extends State<BolusProfileDetailScreen> {
} }
void renderTabButtons(index) { void renderTabButtons(index) {
if (widget.bolusProfile != null) { if (_bolusProfile != null) {
setState(() { setState(() {
switch (index) { switch (index) {
case 1: case 1:
@ -275,9 +273,8 @@ class _BolusProfileDetailScreenState extends State<BolusProfileDetailScreen> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
bool isNew = widget.bolusProfile == null;
return DefaultTabController( return DefaultTabController(
length: isNew ? 1 : 2, length: _isNew ? 1 : 2,
child: Builder(builder: (BuildContext context) { child: Builder(builder: (BuildContext context) {
final TabController tabController = DefaultTabController.of(context)!; final TabController tabController = DefaultTabController.of(context)!;
tabController.addListener(() { tabController.addListener(() {
@ -328,15 +325,15 @@ class _BolusProfileDetailScreenState extends State<BolusProfileDetailScreen> {
), ),
]; ];
if (!isNew) { if (!_isNew) {
tabs.add(BolusListScreen(bolusProfile: widget.bolusProfile)); tabs.add(
BolusListScreen(bolusProfile: _bolusProfile!, bolusRates: _bolusRates, reload: reload));
} }
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: title: Text(_isNew ? 'New Bolus Profile' : _bolusProfile!.name),
Text(isNew ? 'New Bolus Profile' : widget.bolusProfile!.name), bottom: _isNew
bottom: isNew
? PreferredSize(child: Container(), preferredSize: Size.zero) ? PreferredSize(child: Container(), preferredSize: Size.zero)
: const TabBar( : const TabBar(
tabs: [ tabs: [
@ -353,8 +350,7 @@ class _BolusProfileDetailScreenState extends State<BolusProfileDetailScreen> {
), ),
bottomNavigationBar: bottomNav, bottomNavigationBar: bottomNav,
floatingActionButton: actionButton, floatingActionButton: actionButton,
floatingActionButtonLocation: floatingActionButtonLocation: FloatingActionButtonLocation.endFloat,
FloatingActionButtonLocation.endFloat,
); );
}), }),
); );

View File

@ -2,7 +2,6 @@ import 'package:diameter/components/dialogs.dart';
import 'package:diameter/config.dart'; import 'package:diameter/config.dart';
import 'package:diameter/navigation.dart'; import 'package:diameter/navigation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:diameter/components/progress_indicator.dart';
import 'package:diameter/models/bolus_profile.dart'; import 'package:diameter/models/bolus_profile.dart';
import 'package:diameter/screens/bolus/bolus_profile_detail.dart'; import 'package:diameter/screens/bolus/bolus_profile_detail.dart';
@ -15,18 +14,18 @@ class BolusProfileListScreen extends StatefulWidget {
} }
class _BolusProfileListScreenState extends State<BolusProfileListScreen> { class _BolusProfileListScreenState extends State<BolusProfileListScreen> {
late Future<List<BolusProfile>?> _bolusProfiles; List<BolusProfile> _bolusProfiles = [];
Widget banner = Container(); Widget banner = Container();
bool pickActiveProfileMode = false; bool pickActiveProfileMode = false;
void refresh({String? message}) { void reload({String? message}) {
setState(() { setState(() {
pickActiveProfileMode = false; pickActiveProfileMode = false;
_bolusProfiles = BolusProfile.fetchAll(); _bolusProfiles = BolusProfile.getAll();
}); });
_bolusProfiles.then((list) => updateBanner(
list?.where((element) => element.active).length ?? 0, updateBanner();
list?.isNotEmpty ?? false));
setState(() { setState(() {
if (message != null) { if (message != null) {
var snackBar = SnackBar( var snackBar = SnackBar(
@ -40,7 +39,9 @@ class _BolusProfileListScreenState extends State<BolusProfileListScreen> {
}); });
} }
void updateBanner(int activeProfileCount, bool isNotEmpty) { void updateBanner() {
int activeProfileCount = BolusProfile.activeCount();
setState(() { setState(() {
banner = activeProfileCount != 1 banner = activeProfileCount != 1
? MaterialBanner( ? MaterialBanner(
@ -51,7 +52,7 @@ class _BolusProfileListScreenState extends State<BolusProfileListScreen> {
forceActionsBelow: true, forceActionsBelow: true,
actions: activeProfileCount == 0 actions: activeProfileCount == 0
? [ ? [
isNotEmpty _bolusProfiles.isNotEmpty
? TextButton( ? TextButton(
child: const Text('ACTIVATE A PROFILE'), child: const Text('ACTIVATE A PROFILE'),
onPressed: handlePickActiveProfileAction, onPressed: handlePickActiveProfileAction,
@ -74,9 +75,8 @@ class _BolusProfileListScreenState extends State<BolusProfileListScreen> {
} }
void onDelete(BolusProfile bolusProfile) { void onDelete(BolusProfile bolusProfile) {
bolusProfile BolusProfile.remove(bolusProfile.id);
.delete() reload(message: 'Bolus Profile deleted');
.then((_) => refresh(message: 'Bolus Profile deleted'));
} }
void handleDeleteAction(BolusProfile bolusProfile) async { void handleDeleteAction(BolusProfile bolusProfile) async {
@ -92,10 +92,11 @@ class _BolusProfileListScreenState extends State<BolusProfileListScreen> {
} }
void onPickActive(BolusProfile bolusProfile) { void onPickActive(BolusProfile bolusProfile) {
BolusProfile.setAllInactive(exception: bolusProfile.objectId!).then((_) => BolusProfile.setAllInactive;
refresh( bolusProfile.active = true;
message: BolusProfile.put(bolusProfile);
'${bolusProfile.name} has been set as your active Profile')); reload(
message: '${bolusProfile.name} has been set as your active Profile');
} }
void handlePickActiveProfileAction() { void handlePickActiveProfileAction() {
@ -120,9 +121,9 @@ class _BolusProfileListScreenState extends State<BolusProfileListScreen> {
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => BolusProfileDetailScreen( builder: (context) => BolusProfileDetailScreen(
bolusProfile: bolusProfile, active: active), id: bolusProfile?.id ?? 0, active: active),
), ),
).then((message) => refresh(message: message)); ).then((message) => reload(message: message));
} }
void onNew(bool active) { void onNew(bool active) {
@ -136,7 +137,7 @@ class _BolusProfileListScreenState extends State<BolusProfileListScreen> {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
refresh(); reload();
} }
@override @override
@ -145,7 +146,7 @@ class _BolusProfileListScreenState extends State<BolusProfileListScreen> {
appBar: AppBar( appBar: AppBar(
title: const Text('Bolus Profiles'), title: const Text('Bolus Profiles'),
actions: <Widget>[ actions: <Widget>[
IconButton(onPressed: refresh, icon: const Icon(Icons.refresh)) IconButton(onPressed: reload, icon: const Icon(Icons.refresh))
], ],
), ),
drawer: drawer:
@ -155,30 +156,16 @@ class _BolusProfileListScreenState extends State<BolusProfileListScreen> {
children: [ children: [
banner, banner,
Expanded( Expanded(
child: FutureBuilder<List<BolusProfile>?>( child: _bolusProfiles.isNotEmpty ? ListView.builder(
future: _bolusProfiles, itemCount: _bolusProfiles.length,
builder: (context, snapshot) {
return ViewWithProgressIndicator(
snapshot: snapshot,
child: snapshot.data == null || snapshot.data!.isEmpty
? Row(
mainAxisAlignment: MainAxisAlignment.center,
children: const [
Padding(
padding: EdgeInsets.all(10.0),
child: Text('No Bolus Profiles'),
),
])
: ListView.builder(
itemCount:
snapshot.data != null ? snapshot.data!.length : 0,
itemBuilder: (context, index) { itemBuilder: (context, index) {
final bolusProfile = snapshot.data![index]; final bolusProfile = _bolusProfiles[index];
return ListTile( return ListTile(
tileColor: bolusProfile.active tileColor: bolusProfile.active
? Colors.green.shade100 ? Colors.green.shade100
: null, : null,
onTap: () { onTap: () {
// TODO: make pick active profile visually distinct
pickActiveProfileMode pickActiveProfileMode
? onPickActive(bolusProfile) ? onPickActive(bolusProfile)
: onEdit(bolusProfile); : onEdit(bolusProfile);
@ -202,9 +189,8 @@ class _BolusProfileListScreenState extends State<BolusProfileListScreen> {
), ),
); );
}, },
), ) : const Center(
); child: Text('You have not created any Bolus Profiles yet!'),
},
), ),
), ),
], ],

View File

@ -2,10 +2,11 @@ import 'package:diameter/components/dialogs.dart';
import 'package:diameter/config.dart'; import 'package:diameter/config.dart';
import 'package:diameter/models/log_entry.dart'; import 'package:diameter/models/log_entry.dart';
import 'package:diameter/models/log_event.dart'; import 'package:diameter/models/log_event.dart';
import 'package:diameter/models/log_event_type.dart'; // import 'package:diameter/models/log_event_type.dart';
import 'package:diameter/screens/log/log_event_detail.dart'; import 'package:diameter/screens/log/log_event_detail.dart';
import 'package:diameter/utils/date_time_utils.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:diameter/components/progress_indicator.dart'; // import 'package:diameter/components/progress_indicator.dart';
class ActiveLogEventListScreen extends StatefulWidget { class ActiveLogEventListScreen extends StatefulWidget {
static const String routeName = '/active-log-events'; static const String routeName = '/active-log-events';
@ -23,16 +24,11 @@ class ActiveLogEventListScreen extends StatefulWidget {
} }
class _ActiveLogEventListScreenState extends State<ActiveLogEventListScreen> { class _ActiveLogEventListScreenState extends State<ActiveLogEventListScreen> {
late Future<List<LogEvent>> _activeLogEvents; List<LogEvent> _activeLogEvents = [];
late Future<List<LogEventType>> _logEventTypes;
void refresh({String? message}) { void refresh({String? message}) {
setState(() { setState(() {
_logEventTypes = LogEventType.fetchAll(); _activeLogEvents = LogEvent.getAllOngoing();
});
setState(() {
_activeLogEvents = LogEvent.fetchAllForLogEntry(widget.endLogEntry!);
}); });
setState(() { setState(() {
@ -49,12 +45,10 @@ class _ActiveLogEventListScreenState extends State<ActiveLogEventListScreen> {
} }
void onStop(LogEvent event) async { void onStop(LogEvent event) async {
// TODO: create new entry if no existing entry is given event.endTime = DateTime.now();
await LogEvent.update( event.endLogEntry.target =
event.objectId!, widget.endLogEntry ?? LogEntry(time: DateTime.now());
endTime: DateTime.now(), LogEvent.put(event);
endLogEntry: widget.endLogEntry!.objectId!,
);
refresh(); refresh();
if (widget.onSetEndTime != null) { if (widget.onSetEndTime != null) {
widget.onSetEndTime!(); widget.onSetEndTime!();
@ -74,7 +68,8 @@ class _ActiveLogEventListScreenState extends State<ActiveLogEventListScreen> {
} }
void onDelete(LogEvent event) { void onDelete(LogEvent event) {
event.delete().then((_) => refresh(message: 'Event deleted')); LogEvent.remove(event.id);
refresh(message: 'Event deleted');
} }
void handleDeleteAction(LogEvent event) async { void handleDeleteAction(LogEvent event) async {
@ -97,10 +92,7 @@ class _ActiveLogEventListScreenState extends State<ActiveLogEventListScreen> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
bool isNew = widget.endLogEntry == null; return SingleChildScrollView(
return isNew
? Container()
: Container(
padding: const EdgeInsets.only(top: 10.0), padding: const EdgeInsets.only(top: 10.0),
child: Column( child: Column(
children: [ children: [
@ -123,61 +115,49 @@ class _ActiveLogEventListScreenState extends State<ActiveLogEventListScreen> {
).then((message) => refresh(message: message)); ).then((message) => refresh(message: message));
}, },
), ),
IconButton( IconButton(icon: const Icon(Icons.refresh), onPressed: refresh),
icon: const Icon(Icons.refresh), onPressed: refresh),
], ],
), ),
FutureBuilder<List<LogEvent>>( _activeLogEvents.isNotEmpty ?
future: _activeLogEvents, ListView.builder(
builder: (context, snapshot) { shrinkWrap: true,
return ViewWithProgressIndicator( itemCount: _activeLogEvents.length,
snapshot: snapshot, itemBuilder: (context, index) {
child: snapshot.data == null || snapshot.data!.isEmpty final event = _activeLogEvents[index];
? Container() return ListTile(
: ListBody( title: Row(
mainAxisSize: MainAxisSize.max,
children: [ children: [
// TODO: fix problems that this futurebuilder in futurebuilder creates Expanded(
FutureBuilder<List<LogEventType>>( child: Text(event.eventType.target?.value ?? ''),
future: _logEventTypes, ),
builder: (context, types) { ],
// return DataTable( ),
// columnSpacing: 10.0, subtitle: Text(
// showCheckboxColumn: false, '${DateTimeUtils.displayDateTime(event.time)}${event.hasEndTime ? ' - ${DateTimeUtils.displayDateTime(event.endTime)}' : ''}'),
// rows: snapshot.data != null trailing: Row(
// ? snapshot.data!.map((event) { mainAxisSize: MainAxisSize.min,
// return DataRow( children: [
// cells: event.asDataTableCells( IconButton(
// [ icon: const Icon(
// IconButton( Icons.delete,
// icon: const Icon( color: Colors.blue,
// Icons.stop), ),
// iconSize: 16.0, onPressed: () => handleStopAction(event),
// onPressed: () => ),
// handleStopAction( IconButton(
// event), icon: const Icon(
// ), Icons.delete,
// IconButton( color: Colors.blue,
// icon: const Icon( ),
// Icons.delete), onPressed: () => handleDeleteAction(event),
// iconSize: 16.0, ),
// onPressed: () =>
// handleDeleteAction(
// event),
// ),
// ],
// types: types.data,
// ),
// );
// }).toList()
// : [],
// columns: LogEvent.asDataTableColumns(),
// );
return Container();
})
], ],
), ),
); );
}, },
) : const Center(
child: Text('There are no currently ongoing events!'),
), ),
], ],
), ),

View File

@ -1,5 +1,4 @@
import 'package:diameter/components/dialogs.dart'; import 'package:diameter/components/dialogs.dart';
import 'package:diameter/components/progress_indicator.dart';
import 'package:diameter/config.dart'; import 'package:diameter/config.dart';
import 'package:diameter/models/log_entry.dart'; import 'package:diameter/models/log_entry.dart';
import 'package:diameter/navigation.dart'; import 'package:diameter/navigation.dart';
@ -16,11 +15,11 @@ class LogScreen extends StatefulWidget {
} }
class _LogScreenState extends State<LogScreen> { class _LogScreenState extends State<LogScreen> {
late Future<Map<DateTime, List<LogEntry>>?> _logEntryDailyMap; late Map<DateTime, List<LogEntry>> _logEntryDailyMap;
void refresh({String? message}) { void refresh({String? message}) {
setState(() { setState(() {
_logEntryDailyMap = LogEntry.createDailyEntryMap(); _logEntryDailyMap = LogEntry.getDailyEntryMap();
}); });
setState(() { setState(() {
if (message != null) { if (message != null) {
@ -36,7 +35,8 @@ class _LogScreenState extends State<LogScreen> {
} }
void onDelete(LogEntry logEntry) { void onDelete(LogEntry logEntry) {
logEntry.delete().then((_) => refresh(message: 'Log Entry deleted')); LogEntry.remove(logEntry.id);
refresh(message: 'Log Entry deleted');
} }
void handleDeleteAction(LogEntry logEntry) async { void handleDeleteAction(LogEntry logEntry) async {
@ -60,42 +60,29 @@ class _LogScreenState extends State<LogScreen> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar(title: const Text('Log Entries'), actions: <Widget>[ appBar: AppBar(
IconButton(onPressed: refresh, icon: const Icon(Icons.refresh)) title: const Text('Log Entries'),
]), actions: <Widget>[
IconButton(
onPressed: refresh,
icon: const Icon(Icons.refresh)
),
],
),
drawer: const Navigation(currentLocation: LogScreen.routeName), drawer: const Navigation(currentLocation: LogScreen.routeName),
body: Column( body: Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[ children: <Widget>[
Expanded( Expanded(
child: FutureBuilder<Map<DateTime, List<LogEntry>>?>( child: SingleChildScrollView(
future: _logEntryDailyMap, child: _logEntryDailyMap.isNotEmpty ? ListView.builder(
builder: (context, snapshot) {
return ViewWithProgressIndicator(
snapshot: snapshot,
child: snapshot.data == null || snapshot.data!.isEmpty
? Row(
mainAxisAlignment: MainAxisAlignment.center,
children: const [
Padding(
padding: EdgeInsets.all(10.0),
child: Text('No Log Entries'),
),
],
)
: SingleChildScrollView(
child: ListView.builder(
shrinkWrap: true, shrinkWrap: true,
padding: const EdgeInsets.all(10.0), padding: const EdgeInsets.all(10.0),
itemCount: snapshot.data != null itemCount: _logEntryDailyMap.length,
? snapshot.data!.length
: 0,
itemBuilder: (context, dateIndex) { itemBuilder: (context, dateIndex) {
List<DateTime> dateList = List<DateTime> dateList = _logEntryDailyMap.keys.toList();
snapshot.data!.keys.toList();
dateList.sort((a, b) => a.compareTo(b) * -1);
final date = dateList[dateIndex]; final date = dateList[dateIndex];
final entryList = snapshot.data![date]; final entryList = _logEntryDailyMap[date];
return ListBody( return ListBody(
children: [ children: [
Text(DateTimeUtils.displayDate(date)), Text(DateTimeUtils.displayDate(date)),
@ -112,7 +99,7 @@ class _LogScreenState extends State<LogScreen> {
MaterialPageRoute( MaterialPageRoute(
builder: (context) => builder: (context) =>
LogEntryScreen( LogEntryScreen(
entry: logEntry), id: logEntry.id),
), ),
).then((message) => refresh( ).then((message) => refresh(
message: message)); message: message));
@ -142,15 +129,15 @@ class _LogScreenState extends State<LogScreen> {
], ],
), ),
); );
}) }
: Container(), ) : Container(),
], ],
); );
}, },
) : const Center(
child: Text('You have not created any Log Entries yet!'),
), ),
), ),
);
}),
), ),
], ],
), ),

View File

@ -2,8 +2,6 @@ import 'package:diameter/components/detail.dart';
import 'package:diameter/components/dialogs.dart'; import 'package:diameter/components/dialogs.dart';
import 'package:diameter/config.dart'; import 'package:diameter/config.dart';
import 'package:diameter/models/log_entry.dart'; import 'package:diameter/models/log_entry.dart';
import 'package:diameter/models/log_event.dart';
import 'package:diameter/models/log_meal.dart';
import 'package:diameter/navigation.dart'; import 'package:diameter/navigation.dart';
import 'package:diameter/screens/log/log_entry_form.dart'; import 'package:diameter/screens/log/log_entry_form.dart';
import 'package:diameter/screens/log/log_event_detail.dart'; import 'package:diameter/screens/log/log_event_detail.dart';
@ -14,14 +12,19 @@ import 'package:flutter/material.dart';
class LogEntryScreen extends StatefulWidget { class LogEntryScreen extends StatefulWidget {
static const String routeName = '/log-entry'; static const String routeName = '/log-entry';
final LogEntry? entry; final int id;
const LogEntryScreen({Key? key, this.entry}) : super(key: key);
const LogEntryScreen({Key? key, this.id = 0}) : super(key: key);
@override @override
_LogEntryScreenState createState() => _LogEntryScreenState(); _LogEntryScreenState createState() => _LogEntryScreenState();
} }
class _LogEntryScreenState extends State<LogEntryScreen> { class _LogEntryScreenState extends State<LogEntryScreen> {
LogEntry? _logEntry;
bool _isNew = true;
bool _isSaving = false;
final GlobalKey<FormState> logEntryForm = GlobalKey<FormState>(); final GlobalKey<FormState> logEntryForm = GlobalKey<FormState>();
late FloatingActionButton addMealButton; late FloatingActionButton addMealButton;
@ -34,8 +37,6 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
List<Widget> appBarActions = []; List<Widget> appBarActions = [];
DetailBottomRow? bottomNav; DetailBottomRow? bottomNav;
bool _isSaving = false;
final formDataControllers = <String, TextEditingController>{ final formDataControllers = <String, TextEditingController>{
'time': TextEditingController(text: ''), 'time': TextEditingController(text: ''),
'mgPerDl': TextEditingController(text: ''), 'mgPerDl': TextEditingController(text: ''),
@ -46,151 +47,42 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
'notes': TextEditingController(text: ''), 'notes': TextEditingController(text: ''),
}; };
void refreshLists({String? message}) {
if (widget.entry != null) {
setState(() {
widget.entry!.meals = LogMeal.fetchAllForLogEntry(widget.entry!);
widget.entry!.events = LogEvent.fetchAllForLogEntry(widget.entry!);
widget.entry!.endedEvents =
LogEvent.fetchAllEndedByEntry(widget.entry!);
});
}
}
void handleSaveAction() async {
setState(() {
_isSaving = true;
});
if (logEntryForm.currentState!.validate()) {
bool isNew = widget.entry == null;
isNew
? await LogEntry.save(
time: DateTime.parse(formDataControllers['time']!.text),
mgPerDl: int.tryParse(formDataControllers['mgPerDl']!.text),
mmolPerL: double.tryParse(formDataControllers['mmolPerL']!.text),
bolusGlucose:
double.tryParse(formDataControllers['bolusGlucose']!.text),
delayedBolusDuration: int.tryParse(
formDataControllers['delayedBolusDuration']!.text),
delayedBolusRatio: double.tryParse(
formDataControllers['delayedBolusRate']!.text),
notes: formDataControllers['notes']!.text,
)
: await LogEntry.update(
widget.entry!.objectId!,
time: DateTime.parse(formDataControllers['time']!.text),
mgPerDl: int.tryParse(formDataControllers['mgPerDl']!.text),
mmolPerL: double.tryParse(formDataControllers['mmolPerL']!.text),
bolusGlucose: double.tryParse(
formDataControllers['delayedBolusRate']!.text),
delayedBolusDuration: int.tryParse(
formDataControllers['delayedBolusDuration']!.text),
delayedBolusRatio: double.tryParse(
formDataControllers['delayedBolusRate']!.text),
notes: formDataControllers['notes']!.text,
);
Navigator.pushReplacementNamed(context, '/log',
arguments: '${isNew ? 'New' : ''} Log Entry Saved');
}
setState(() {
_isSaving = false;
});
}
void handleCancelAction() {
bool isNew = widget.entry == null;
if (showConfirmationDialogOnCancel &&
((isNew &&
(int.tryParse(formDataControllers['mgPerDl']?.text ?? '') !=
null ||
double.tryParse(formDataControllers['mmolPerL']?.text ?? '') !=
null ||
double.tryParse(formDataControllers['bolusGlucose']?.text ?? '') !=
null ||
int.tryParse(formDataControllers['delayedBolusDuration']?.text ?? '') !=
null ||
double.tryParse(formDataControllers['delayedBolusRate']?.text ?? '') !=
null ||
formDataControllers['notes']?.text != '')) ||
(!isNew &&
(int.tryParse(formDataControllers['mgPerDl']?.text ?? '') !=
widget.entry!.mgPerDl ||
double.tryParse(formDataControllers['mmolPerL']?.text ?? '') !=
widget.entry!.mmolPerL ||
double.tryParse(formDataControllers['bolusGlucose']?.text ?? '') !=
widget.entry!.bolusGlucose ||
int.tryParse(
formDataControllers['delayedBolusDuration']?.text ??
'') !=
widget.entry!.delayedBolusDuration ||
double.tryParse(formDataControllers['delayedBolusRate']?.text ?? '') !=
widget.entry!.delayedBolusRatio ||
formDataControllers['notes']?.text !=
(widget.entry!.notes ?? ''))))) {
Dialogs.showCancelConfirmationDialog(
context: context,
isNew: isNew,
onSave: handleSaveAction,
onDiscard: (context) => Navigator.pushReplacementNamed(context, '/log'),
);
} else {
Navigator.pushReplacementNamed(context, '/log',
arguments: '${isNew ? 'New' : ''} Log Entry Saved');
}
}
@override @override
void initState() { void initState() {
super.initState(); super.initState();
if (widget.entry != null) { reload();
formDataControllers['time']!.text = widget.entry!.time.toString();
if (_logEntry != null) {
formDataControllers['time']!.text = _logEntry!.time.toString();
formDataControllers['mgPerDl']!.text = formDataControllers['mgPerDl']!.text =
(widget.entry!.mgPerDl ?? '').toString(); (_logEntry!.mgPerDl ?? '').toString();
formDataControllers['mmolPerL']!.text = formDataControllers['mmolPerL']!.text =
(widget.entry!.mmolPerL ?? '').toString(); (_logEntry!.mmolPerL ?? '').toString();
formDataControllers['bolusGlucose']!.text = formDataControllers['bolusGlucose']!.text =
(widget.entry!.bolusGlucose ?? '').toString(); (_logEntry!.bolusGlucose ?? '').toString();
formDataControllers['delayedBolusRate']!.text = formDataControllers['delayedBolusRate']!.text =
(widget.entry!.delayedBolusRatio ?? '').toString(); (_logEntry!.delayedBolusRate ?? '').toString();
formDataControllers['delayedBolusDuration']!.text = formDataControllers['delayedBolusDuration']!.text =
(widget.entry!.delayedBolusDuration ?? '').toString(); (_logEntry!.delayedBolusDuration ?? '').toString();
formDataControllers['notes']!.text = widget.entry!.notes ?? ''; formDataControllers['notes']!.text = _logEntry!.notes ?? '';
} else { } else {
formDataControllers['time']!.text = DateTime.now().toString(); formDataControllers['time']!.text = DateTime.now().toString();
} }
addMealButton = FloatingActionButton( addMealButton = FloatingActionButton(
onPressed: () { onPressed: handleAddNewMeal,
Navigator.push(
context,
MaterialPageRoute(
builder: (context) {
return LogMealDetailScreen(logEntry: widget.entry!);
},
),
).then((message) => refreshLists(message: message));
},
child: const Icon(Icons.add), child: const Icon(Icons.add),
); );
addEventButton = FloatingActionButton( addEventButton = FloatingActionButton(
onPressed: () { onPressed: handleAddNewEvent,
Navigator.push(
context,
MaterialPageRoute(
builder: (context) {
return LogEventDetailScreen(logEntry: widget.entry!);
},
),
).then((message) => refreshLists(message: message));
},
child: const Icon(Icons.add), child: const Icon(Icons.add),
); );
refreshButton = IconButton( refreshButton = IconButton(
icon: const Icon(Icons.refresh), icon: const Icon(Icons.refresh),
onPressed: refreshLists, onPressed: reload,
); );
closeButton = IconButton( closeButton = IconButton(
@ -208,8 +100,118 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
bottomNav = detailBottomRow; bottomNav = detailBottomRow;
} }
void reload({String? message}) {
if (widget.id != 0) {
setState(() {
_logEntry = LogEntry.get(widget.id);
});
_isNew = _logEntry == null;
}
setState(() {
if (message != null) {
var snackBar = SnackBar(
content: Text(message),
duration: const Duration(seconds: 2),
);
ScaffoldMessenger.of(context)
..removeCurrentSnackBar()
..showSnackBar(snackBar);
}
});
}
void handleAddNewMeal() async {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) {
return LogMealDetailScreen(logEntry: _logEntry!);
},
),
).then((message) => reload(message: message));
}
void handleAddNewEvent() async {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) {
return LogEventDetailScreen(logEntry: _logEntry!);
},
),
).then((message) => reload(message: message));
}
void handleSaveAction() async {
setState(() {
_isSaving = true;
});
if (logEntryForm.currentState!.validate()) {
LogEntry.put(LogEntry(
id: widget.id,
time: DateTime.parse(formDataControllers['time']!.text),
mgPerDl: int.tryParse(formDataControllers['mgPerDl']!.text),
mmolPerL: double.tryParse(formDataControllers['mmolPerL']!.text),
bolusGlucose:
double.tryParse(formDataControllers['delayedBolusRate']!.text),
delayedBolusDuration:
int.tryParse(formDataControllers['delayedBolusDuration']!.text),
delayedBolusRate:
double.tryParse(formDataControllers['delayedBolusRate']!.text),
notes: formDataControllers['notes']!.text,
));
Navigator.pushReplacementNamed(context, '/log',
arguments: '${_isNew ? 'New' : ''} Log Entry Saved');
}
setState(() {
_isSaving = false;
});
}
void handleCancelAction() {
if (showConfirmationDialogOnCancel &&
((_isNew &&
(int.tryParse(formDataControllers['mgPerDl']?.text ?? '') !=
null ||
double.tryParse(formDataControllers['mmolPerL']?.text ?? '') !=
null ||
double.tryParse(formDataControllers['bolusGlucose']?.text ?? '') !=
null ||
int.tryParse(formDataControllers['delayedBolusDuration']?.text ?? '') !=
null ||
double.tryParse(formDataControllers['delayedBolusRate']?.text ?? '') !=
null ||
formDataControllers['notes']?.text != '')) ||
(!_isNew &&
(int.tryParse(formDataControllers['mgPerDl']?.text ?? '') !=
_logEntry!.mgPerDl ||
double.tryParse(formDataControllers['mmolPerL']?.text ?? '') !=
_logEntry!.mmolPerL ||
double.tryParse(formDataControllers['bolusGlucose']?.text ?? '') !=
_logEntry!.bolusGlucose ||
int.tryParse(
formDataControllers['delayedBolusDuration']?.text ??
'') !=
_logEntry!.delayedBolusDuration ||
double.tryParse(formDataControllers['delayedBolusRate']?.text ?? '') !=
_logEntry!.delayedBolusRate ||
formDataControllers['notes']?.text !=
(_logEntry!.notes ?? ''))))) {
Dialogs.showCancelConfirmationDialog(
context: context,
isNew: _isNew,
onSave: handleSaveAction,
onDiscard: (context) => Navigator.pushReplacementNamed(context, '/log'),
);
} else {
Navigator.pushReplacementNamed(context, '/log',
arguments: '${_isNew ? 'New' : ''} Log Entry Saved');
}
}
void renderTabButtons(index) { void renderTabButtons(index) {
if (widget.entry != null) { if (_logEntry != null) {
setState(() { setState(() {
switch (index) { switch (index) {
case 1: case 1:
@ -233,10 +235,8 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
bool isNew = widget.entry == null;
return DefaultTabController( return DefaultTabController(
length: isNew ? 1 : 3, length: _isNew ? 1 : 3,
child: Builder(builder: (BuildContext context) { child: Builder(builder: (BuildContext context) {
final TabController tabController = DefaultTabController.of(context)!; final TabController tabController = DefaultTabController.of(context)!;
tabController.addListener(() { tabController.addListener(() {
@ -247,15 +247,15 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
formState: logEntryForm, controllers: formDataControllers), formState: logEntryForm, controllers: formDataControllers),
]; ];
if (!isNew) { if (!_isNew) {
tabs.add(LogMealListScreen(logEntry: widget.entry)); tabs.add(LogMealListScreen(logEntry: _logEntry!, reload: reload));
tabs.add(LogEventListScreen(logEntry: widget.entry)); tabs.add(LogEventListScreen(logEntry: _logEntry!, reload: reload));
} }
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text(isNew ? 'New Log Entry' : 'Edit Log Entry'), title: Text(_isNew ? 'New Log Entry' : 'Edit Log Entry'),
bottom: isNew bottom: _isNew
? PreferredSize(child: Container(), preferredSize: Size.zero) ? PreferredSize(child: Container(), preferredSize: Size.zero)
: const TabBar( : const TabBar(
tabs: [ tabs: [
@ -273,7 +273,7 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
bottomNavigationBar: bottomNav, bottomNavigationBar: bottomNav,
floatingActionButton: actionButton, floatingActionButton: actionButton,
floatingActionButtonLocation: floatingActionButtonLocation:
FloatingActionButtonLocation.centerDocked, FloatingActionButtonLocation.endFloat,
); );
}), }),
); );

View File

@ -48,7 +48,7 @@ class _LogEntryFormState extends State<LogEntryForm> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final _timeController = widget.controllers['time']; // final _timeController = widget.controllers['time'];
final _mgPerDlController = widget.controllers['mgPerDl']; final _mgPerDlController = widget.controllers['mgPerDl'];
final _mmolPerLController = widget.controllers['mmolPerL']; final _mmolPerLController = widget.controllers['mmolPerL'];
final _bolusGlucoseController = widget.controllers['bolusGlucose']; final _bolusGlucoseController = widget.controllers['bolusGlucose'];

View File

@ -25,10 +25,10 @@ class _LogEventDetailScreenState extends State<LogEventDetailScreen> {
final GlobalKey<FormState> _logEventForm = GlobalKey<FormState>(); final GlobalKey<FormState> _logEventForm = GlobalKey<FormState>();
final _notesController = TextEditingController(text: ''); final _notesController = TextEditingController(text: '');
String? _eventType; LogEventType? _eventType;
bool _hasEndTime = false; bool _hasEndTime = false;
late Future<List<LogEventType>> _logEventTypes; List<LogEventType> _logEventTypes = [];
bool _isSaving = false; bool _isSaving = false;
@ -38,11 +38,11 @@ class _LogEventDetailScreenState extends State<LogEventDetailScreen> {
if (widget.logEvent != null) { if (widget.logEvent != null) {
_notesController.text = widget.logEvent!.notes ?? ''; _notesController.text = widget.logEvent!.notes ?? '';
_eventType = widget.logEvent!.eventType; _eventType = widget.logEvent!.eventType.target;
_hasEndTime = widget.logEvent!.hasEndTime; _hasEndTime = widget.logEvent!.hasEndTime;
} }
_logEventTypes = LogEventType.fetchAll(); _logEventTypes = LogEventType.getAll();
} }
void handleSaveAction() async { void handleSaveAction() async {
@ -51,21 +51,29 @@ class _LogEventDetailScreenState extends State<LogEventDetailScreen> {
}); });
if (_logEventForm.currentState!.validate()) { if (_logEventForm.currentState!.validate()) {
bool isNew = widget.logEvent == null; bool isNew = widget.logEvent == null;
isNew // isNew
? await LogEvent.save( // ? await LogEvent.save(
logEntry: widget.logEntry!.objectId!, // logEntry: widget.logEntry!.objectId!,
eventType: _eventType!, // eventType: _eventType!,
time: widget.logEntry!.time, // time: widget.logEntry!.time,
hasEndTime: _hasEndTime, // hasEndTime: _hasEndTime,
notes: _notesController.text, // notes: _notesController.text,
) // )
: await LogEvent.update( // : await LogEvent.update(
widget.logEvent!.objectId!, // widget.logEvent!.objectId!,
eventType: _eventType!, // eventType: _eventType!,
// time: widget.logEntry!.time,
// hasEndTime: _hasEndTime,
// notes: _notesController.text,
// );
LogEvent event = LogEvent(
id: widget.logEvent?.id ?? 0,
time: widget.logEntry!.time, time: widget.logEntry!.time,
hasEndTime: _hasEndTime, hasEndTime: _hasEndTime,
notes: _notesController.text, notes: _notesController.text,
); );
event.eventType.target = _eventType;
LogEvent.put(event);
Navigator.pop(context, '${isNew ? 'New' : ''} Event Saved'); Navigator.pop(context, '${isNew ? 'New' : ''} Event Saved');
} }
setState(() { setState(() {
@ -82,7 +90,7 @@ class _LogEventDetailScreenState extends State<LogEventDetailScreen> {
_hasEndTime)) || _hasEndTime)) ||
(!isNew && (!isNew &&
(_notesController.text != (widget.logEvent!.notes ?? '') || (_notesController.text != (widget.logEvent!.notes ?? '') ||
_eventType != widget.logEvent!.eventType || _eventType != widget.logEvent!.eventType.target ||
_hasEndTime != widget.logEvent!.hasEndTime)))) { _hasEndTime != widget.logEvent!.hasEndTime)))) {
Dialogs.showCancelConfirmationDialog( Dialogs.showCancelConfirmationDialog(
context: context, context: context,
@ -109,11 +117,10 @@ class _LogEventDetailScreenState extends State<LogEventDetailScreen> {
StyledForm( StyledForm(
formState: _logEventForm, formState: _logEventForm,
fields: [ fields: [
StyledFutureDropdownButton<LogEventType>( StyledDropdownButton<LogEventType>(
selectedItem: _eventType, selectedItem: _eventType,
label: 'Event Type', label: 'Event Type',
items: _logEventTypes, items: _logEventTypes,
getItemValue: (item) => item.objectId,
renderItem: (item) => Text(item.value), renderItem: (item) => Text(item.value),
onChanged: (value) { onChanged: (value) {
setState(() { setState(() {
@ -121,6 +128,18 @@ class _LogEventDetailScreenState extends State<LogEventDetailScreen> {
}); });
}, },
), ),
// StyledFutureDropdownButton<LogEventType>(
// selectedItem: _eventType,
// label: 'Event Type',
// items: _logEventTypes,
// getItemValue: (item) => item.objectId,
// renderItem: (item) => Text(item.value),
// onChanged: (value) {
// setState(() {
// _eventType = value;
// });
// },
// ),
StyledBooleanFormField( StyledBooleanFormField(
value: _hasEndTime, value: _hasEndTime,
onChanged: (value) { onChanged: (value) {

View File

@ -2,35 +2,25 @@ import 'package:diameter/components/dialogs.dart';
import 'package:diameter/config.dart'; import 'package:diameter/config.dart';
import 'package:diameter/models/log_entry.dart'; import 'package:diameter/models/log_entry.dart';
import 'package:diameter/models/log_event.dart'; import 'package:diameter/models/log_event.dart';
import 'package:diameter/models/log_event_type.dart';
import 'package:diameter/screens/log/active_log_event_list.dart';
import 'package:diameter/screens/log/log_event_detail.dart'; import 'package:diameter/screens/log/log_event_detail.dart';
import 'package:diameter/utils/date_time_utils.dart'; import 'package:diameter/utils/date_time_utils.dart';
import 'package:flex_color_scheme/flex_color_scheme.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:diameter/components/progress_indicator.dart';
class LogEventListScreen extends StatefulWidget { class LogEventListScreen extends StatefulWidget {
final LogEntry? logEntry; final LogEntry logEntry;
final Function() reload;
const LogEventListScreen({Key? key, this.logEntry}) : super(key: key); const LogEventListScreen({Key? key, required this.logEntry, required this.reload})
: super(key: key);
@override @override
_LogEventListScreenState createState() => _LogEventListScreenState(); _LogEventListScreenState createState() => _LogEventListScreenState();
} }
class _LogEventListScreenState extends State<LogEventListScreen> { class _LogEventListScreenState extends State<LogEventListScreen> {
late Future<List<LogEventType>> _logEventTypes; void reload({String? message}) {
widget.reload();
void refresh({String? message}) {
if (widget.logEntry != null) {
setState(() {
widget.logEntry!.events =
LogEvent.fetchAllForLogEntry(widget.logEntry!);
widget.logEntry!.endedEvents =
LogEvent.fetchAllEndedByEntry(widget.logEntry!);
});
}
setState(() { setState(() {
if (message != null) { if (message != null) {
var snackBar = SnackBar( var snackBar = SnackBar(
@ -42,7 +32,6 @@ class _LogEventListScreenState extends State<LogEventListScreen> {
..showSnackBar(snackBar); ..showSnackBar(snackBar);
} }
}); });
_logEventTypes = LogEventType.fetchAll();
} }
void handleEditAction(LogEvent event) { void handleEditAction(LogEvent event) {
@ -50,15 +39,16 @@ class _LogEventListScreenState extends State<LogEventListScreen> {
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => LogEventDetailScreen( builder: (context) => LogEventDetailScreen(
endLogEntry: widget.logEntry!, endLogEntry: widget.logEntry,
logEvent: event, logEvent: event,
), ),
), ),
).then((message) => refresh(message: message)); ).then((message) => reload(message: message));
} }
void onDelete(LogEvent logEvent) { void onDelete(LogEvent logEvent) {
logEvent.delete().then((_) => refresh(message: 'Event deleted')); LogEvent.remove(logEvent.id);
reload(message: 'Event deleted');
} }
void handleDeleteAction(LogEvent logEvent) async { void handleDeleteAction(LogEvent logEvent) async {
@ -73,40 +63,19 @@ class _LogEventListScreenState extends State<LogEventListScreen> {
} }
} }
@override
void initState() {
super.initState();
refresh();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return SingleChildScrollView( return Column(
padding: const EdgeInsets.only(top: 10.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
// TODO: add button for active events // TODO: add button for active events
FutureBuilder<List<LogEvent>>( Expanded(
future: widget.logEntry!.events, child: (widget.logEntry.events.isNotEmpty || widget.logEntry.endedEvents.isNotEmpty)
builder: (context, snapshot) { ? ListView.builder(
return ViewWithProgressIndicator(
snapshot: snapshot,
child: snapshot.data == null || snapshot.data!.isEmpty
? const Padding(
padding: EdgeInsets.all(10.0),
child: Text('No Events for this Log Entry'),
)
: FutureBuilder<List<LogEventType>>(
future: _logEventTypes,
builder: (context, types) {
return ListView.builder(
shrinkWrap: true, shrinkWrap: true,
itemCount: snapshot.data != null itemCount: widget.logEntry.events.length + widget.logEntry.endedEvents.length,
? snapshot.data!.length
: 0,
itemBuilder: (context, index) { itemBuilder: (context, index) {
final event = snapshot.data![index]; final event = (widget.logEntry.events + widget.logEntry.endedEvents)[index];
return ListTile( return ListTile(
onTap: () { onTap: () {
handleEditAction(event); handleEditAction(event);
@ -115,12 +84,7 @@ class _LogEventListScreenState extends State<LogEventListScreen> {
mainAxisSize: MainAxisSize.max, mainAxisSize: MainAxisSize.max,
children: [ children: [
Expanded( Expanded(
child: Text(types.data child: Text(event.eventType.target?.value ?? '')),
?.firstWhere((element) =>
element.objectId ==
event.eventType)
.value ??
'')),
], ],
), ),
subtitle: Text( subtitle: Text(
@ -133,75 +97,17 @@ class _LogEventListScreenState extends State<LogEventListScreen> {
Icons.delete, Icons.delete,
color: Colors.blue, color: Colors.blue,
), ),
onPressed: () => onPressed: () => handleDeleteAction(event),
handleDeleteAction(event),
), ),
], ],
), ),
); );
}, })
); : const Center(
}), child: Text('You have not added any Events to this Log Entry yet!'),
);
},
), ),
FutureBuilder<List<LogEvent>>(
future: widget.logEntry!.endedEvents,
builder: (context, snapshot) {
return ViewWithProgressIndicator(
snapshot: snapshot,
child: snapshot.data == null || snapshot.data!.isEmpty
? Container()
: FutureBuilder<List<LogEventType>>(
future: _logEventTypes,
builder: (context, types) {
return ListView.builder(
shrinkWrap: true,
itemCount: snapshot.data != null
? snapshot.data!.length
: 0,
itemBuilder: (context, index) {
final event = snapshot.data![index];
return ListTile(
onTap: () {
handleEditAction(event);
},
title: Row(
mainAxisSize: MainAxisSize.max,
children: [
Expanded(
child: Text(types.data
?.firstWhere((element) =>
element.objectId ==
event.eventType)
.value ??
'')),
],
),
subtitle: Text(
'${DateTimeUtils.displayDateTime(event.time)}${event.hasEndTime ? ' - ${DateTimeUtils.displayDateTime(event.endTime)}' : ''}'),
trailing: Row(
mainAxisSize: MainAxisSize.min,
children: [
IconButton(
icon: const Icon(
Icons.delete,
color: Colors.blue,
),
onPressed: () =>
handleDeleteAction(event),
), ),
], ],
),
);
},
);
}),
);
},
),
],
),
); );
} }
} }

View File

@ -45,22 +45,30 @@ class _LogEventTypeDetailScreenState extends State<LogEventTypeDetailScreen> {
}); });
if (_logEventTypeForm.currentState!.validate()) { if (_logEventTypeForm.currentState!.validate()) {
bool isNew = widget.logEventType == null; bool isNew = widget.logEventType == null;
isNew // isNew
? await LogEventType.save( // ? await LogEventType.save(
// value: _valueController.text,
// notes: _notesController.text,
// defaultReminderDuration:
// int.tryParse(_defaultReminderDurationController.text),
// hasEndTime: _hasEndTime,
// )
// : await LogEventType.update(
// widget.logEventType!.objectId!,
// value: _valueController.text,
// notes: _notesController.text,
// defaultReminderDuration:
// int.tryParse(_defaultReminderDurationController.text),
// hasEndTime: _hasEndTime,
// );
LogEventType.put(LogEventType(
id: widget.logEventType?.id ?? 0,
value: _valueController.text, value: _valueController.text,
notes: _notesController.text, notes: _notesController.text,
defaultReminderDuration: defaultReminderDuration:
int.tryParse(_defaultReminderDurationController.text), int.tryParse(_defaultReminderDurationController.text),
hasEndTime: _hasEndTime, hasEndTime: _hasEndTime,
) ));
: await LogEventType.update(
widget.logEventType!.objectId!,
value: _valueController.text,
notes: _notesController.text,
defaultReminderDuration:
int.tryParse(_defaultReminderDurationController.text),
hasEndTime: _hasEndTime,
);
Navigator.pop(context, '${isNew ? 'New' : ''} Log Event Type Saved'); Navigator.pop(context, '${isNew ? 'New' : ''} Log Event Type Saved');
} }
setState(() { setState(() {

View File

@ -1,4 +1,4 @@
import 'package:diameter/components/progress_indicator.dart'; // import 'package:diameter/components/progress_indicator.dart';
import 'package:diameter/models/log_event_type.dart'; import 'package:diameter/models/log_event_type.dart';
import 'package:diameter/navigation.dart'; import 'package:diameter/navigation.dart';
import 'package:diameter/screens/log/log_event_type_detail.dart'; import 'package:diameter/screens/log/log_event_type_detail.dart';
@ -13,11 +13,11 @@ class LogEventTypeListScreen extends StatefulWidget {
} }
class _LogEventTypeListScreenState extends State<LogEventTypeListScreen> { class _LogEventTypeListScreenState extends State<LogEventTypeListScreen> {
late Future<List<LogEventType>?> _logEventTypes; List<LogEventType> _logEventTypes = [];
void refresh({String? message}) { void refresh({String? message}) {
setState(() { setState(() {
_logEventTypes = LogEventType.fetchAll(); _logEventTypes = LogEventType.getAll();
}); });
setState(() { setState(() {
if (message != null) { if (message != null) {
@ -50,27 +50,12 @@ class _LogEventTypeListScreenState extends State<LogEventTypeListScreen> {
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[ children: <Widget>[
Expanded( Expanded(
child: FutureBuilder<List<LogEventType>?>( child: _logEventTypes.isNotEmpty ? ListView.builder(
future: _logEventTypes,
builder: (context, snapshot) {
return ViewWithProgressIndicator(
snapshot: snapshot,
child: snapshot.data == null || snapshot.data!.isEmpty
? Row(
mainAxisAlignment: MainAxisAlignment.center,
children: const [
Padding(
padding: EdgeInsets.all(10.0),
child: Text('No Log Event Types'),
),
],
)
: ListView.builder(
padding: const EdgeInsets.all(10.0), padding: const EdgeInsets.all(10.0),
itemCount: itemCount: _logEventTypes.length,
snapshot.data != null ? snapshot.data!.length : 0,
itemBuilder: (context, index) { itemBuilder: (context, index) {
final logEventType = snapshot.data![index]; // final logEventType = snapshot.data![index];
final logEventType = _logEventTypes[index];
return ListTile( return ListTile(
onTap: () { onTap: () {
Navigator.push( Navigator.push(
@ -89,10 +74,11 @@ class _LogEventTypeListScreenState extends State<LogEventTypeListScreen> {
children: [ children: [
IconButton( IconButton(
onPressed: () async { onPressed: () async {
await logEventType.delete().then((_) { LogEventType.remove(logEventType.id);
// await logEventType.delete().then((_) {
refresh( refresh(
message: 'Log Event Type deleted'); message: 'Log Event Type deleted');
}); // });
}, },
icon: const Icon(Icons.delete, icon: const Icon(Icons.delete,
color: Colors.blue), color: Colors.blue),
@ -101,9 +87,8 @@ class _LogEventTypeListScreenState extends State<LogEventTypeListScreen> {
), ),
); );
}, },
), ) : const Center(
); child: Text('You have not created any Log Event Types yet!'),
},
), ),
), ),
], ],

View File

@ -27,6 +27,7 @@ class LogMealDetailScreen extends StatefulWidget {
class _LogMealDetailScreenState extends State<LogMealDetailScreen> { class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
final GlobalKey<FormState> _logMealForm = GlobalKey<FormState>(); final GlobalKey<FormState> _logMealForm = GlobalKey<FormState>();
final _valueController = TextEditingController(text: ''); final _valueController = TextEditingController(text: '');
final _carbsRatioController = TextEditingController(text: ''); final _carbsRatioController = TextEditingController(text: '');
final _portionSizeController = TextEditingController(text: ''); final _portionSizeController = TextEditingController(text: '');
@ -35,19 +36,19 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
final _delayedBolusRateController = TextEditingController(text: ''); final _delayedBolusRateController = TextEditingController(text: '');
final _delayedBolusDurationController = TextEditingController(text: ''); final _delayedBolusDurationController = TextEditingController(text: '');
final _notesController = TextEditingController(text: ''); final _notesController = TextEditingController(text: '');
String? _meal; Meal? _meal;
String? _source; MealSource? _mealSource;
String? _category; MealCategory? _mealCategory;
String? _portionType; MealPortionType? _mealPortionType;
String? _portionSizeAccuracy; Accuracy? _portionSizeAccuracy;
String? _carbsRatioAccuracy; Accuracy? _carbsRatioAccuracy;
late Future<List<Meal>> _meals; List<Meal> _meals = [];
late Future<List<MealCategory>> _mealCategories; List<MealCategory> _mealCategories = [];
late Future<List<MealPortionType>> _mealPortionTypes; List<MealPortionType> _mealPortionTypes = [];
late Future<List<MealSource>> _mealSources; List<MealSource> _mealSources = [];
late Future<List<Accuracy>> _portionSizeAccuracies; List<Accuracy> _portionSizeAccuracies = [];
late Future<List<Accuracy>> _carbsRatioAccuracies; List<Accuracy> _carbsRatioAccuracies = [];
bool _isSaving = false; bool _isSaving = false;
@ -55,6 +56,13 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
void initState() { void initState() {
super.initState(); super.initState();
_portionSizeAccuracies = Accuracy.getAllForPortionSize();
_carbsRatioAccuracies = Accuracy.getAllForCarbsRatio();
_meals = Meal.getAll();
_mealCategories = MealCategory.getAll();
_mealPortionTypes = MealPortionType.getAll();
_mealSources = MealSource.getAll();
if (widget.logMeal != null) { if (widget.logMeal != null) {
_valueController.text = widget.logMeal!.value; _valueController.text = widget.logMeal!.value;
_carbsRatioController.text = _carbsRatioController.text =
@ -70,28 +78,23 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
(widget.logMeal!.delayedBolusDuration ?? '').toString(); (widget.logMeal!.delayedBolusDuration ?? '').toString();
_notesController.text = widget.logMeal!.notes ?? ''; _notesController.text = widget.logMeal!.notes ?? '';
_meal = widget.logMeal!.meal; // _meal = widget.logMeal!.meal;
_source = widget.logMeal!.source; // _source = widget.logMeal!.source;
_category = widget.logMeal!.category; // _category = widget.logMeal!.category;
_portionType = widget.logMeal!.portionType; // _portionType = widget.logMeal!.portionType;
_portionSizeAccuracy = widget.logMeal!.portionSizeAccuracy; // _portionSizeAccuracy = _portionSizeAccuracies.firstWhere((element) =>
_carbsRatioAccuracy = widget.logMeal!.carbsRatioAccuracy; // element.id ==
// int.tryParse(widget.logMeal!.portionSizeAccuracy ?? ''));
// _carbsRatioAccuracy = _carbsRatioAccuracies.firstWhere((element) =>
// element.id == int.tryParse(widget.logMeal!.carbsRatioAccuracy ?? ''));
// _portionSizeAccuracy = widget.meal!.portionSizeAccuracy;
// _carbsRatioAccuracy = widget.meal!.carbsRatioAccuracy;
}
} }
_meals = Meal.fetchAll(); Future<void> onSelectMeal(Meal meal) async {
_mealCategories = MealCategory.fetchAll();
_mealPortionTypes = MealPortionType.fetchAll();
_mealSources = MealSource.fetchAll();
_portionSizeAccuracies = Accuracy.fetchAllForPortionSize();
_carbsRatioAccuracies = Accuracy.fetchAllForCarbsRatio();
}
Future<void> onSelectMeal(String? objectId) async {
if (objectId != null) {
Meal? meal = await Meal.get(objectId);
if (meal != null) {
setState(() { setState(() {
_meal = objectId; _meal = meal;
_valueController.text = meal.value; _valueController.text = meal.value;
if (meal.carbsRatio != null) { if (meal.carbsRatio != null) {
_carbsRatioController.text = meal.carbsRatio.toString(); _carbsRatioController.text = meal.carbsRatio.toString();
@ -109,25 +112,23 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
_delayedBolusDurationController.text = _delayedBolusDurationController.text =
meal.delayedBolusDuration.toString(); meal.delayedBolusDuration.toString();
} }
if (meal.source != null) { if (meal.mealSource.hasValue) {
_source = meal.source; _mealSource = meal.mealSource.target;
} }
if (meal.category != null) { if (meal.mealCategory.hasValue) {
_category = meal.category; _mealCategory = meal.mealCategory.target;
} }
if (meal.portionType != null) { if (meal.mealPortionType.hasValue) {
_portionType = meal.portionType; _mealPortionType = meal.mealPortionType.target;
} }
if (meal.portionSizeAccuracy != null) { if (meal.portionSizeAccuracy.hasValue) {
_portionSizeAccuracy = meal.portionSizeAccuracy; _portionSizeAccuracy = meal.portionSizeAccuracy.target;
} }
if (meal.carbsRatioAccuracy != null) { if (meal.carbsRatioAccuracy.hasValue) {
_carbsRatioAccuracy = meal.carbsRatioAccuracy; _carbsRatioAccuracy = meal.carbsRatioAccuracy.target;
} }
}); });
} }
}
}
void handleSaveAction() async { void handleSaveAction() async {
setState(() { setState(() {
@ -135,45 +136,69 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
}); });
if (_logMealForm.currentState!.validate()) { if (_logMealForm.currentState!.validate()) {
bool isNew = widget.logMeal == null; bool isNew = widget.logMeal == null;
isNew // isNew
? await LogMeal.save( // ? await LogMeal.save(
logEntry: widget.logEntry.objectId!, // logEntry: widget.logEntry.objectId!,
meal: _meal, // meal: _meal,
// value: _valueController.text,
// source: _mealSource,
// category: _category,
// portionType: _portionType,
// carbsRatio: double.tryParse(_carbsRatioController.text),
// portionSize: double.tryParse(_portionSizeController.text),
// carbsPerPortion: double.tryParse(_carbsPerPortionController.text),
// // portionSizeAccuracy: _portionSizeAccuracy,
// // carbsRatioAccuracy: _carbsRatioAccuracy,
// portionSizeAccuracy: _portionSizeAccuracy?.id.toString(),
// carbsRatioAccuracy: _carbsRatioAccuracy?.id.toString(),
// bolus: double.tryParse(_bolusController.text),
// delayedBolusDuration:
// int.tryParse(_delayedBolusDurationController.text),
// delayedBolusRate:
// double.tryParse(_delayedBolusRateController.text),
// notes: _notesController.text,
// )
// : await LogMeal.update(
// widget.logMeal!.objectId!,
// meal: _meal,
// value: _valueController.text,
// source: _mealSource,
// category: _category,
// portionType: _portionType,
// carbsRatio: double.tryParse(_carbsRatioController.text),
// portionSize: double.tryParse(_portionSizeController.text),
// carbsPerPortion: double.tryParse(_carbsPerPortionController.text),
// // portionSizeAccuracy: _portionSizeAccuracy,
// // carbsRatioAccuracy: _carbsRatioAccuracy,
// portionSizeAccuracy: _portionSizeAccuracy?.id.toString(),
// carbsRatioAccuracy: _carbsRatioAccuracy?.id.toString(),
// bolus: double.tryParse(_bolusController.text),
// delayedBolusDuration:
// int.tryParse(_delayedBolusDurationController.text),
// delayedBolusRate:
// double.tryParse(_delayedBolusRateController.text),
// notes: _notesController.text,
// );
LogMeal logMeal = LogMeal(
id: widget.logMeal?.id ?? 0,
value: _valueController.text, value: _valueController.text,
source: _source,
category: _category,
portionType: _portionType,
carbsRatio: double.tryParse(_carbsRatioController.text), carbsRatio: double.tryParse(_carbsRatioController.text),
portionSize: double.tryParse(_portionSizeController.text), portionSize: double.tryParse(_portionSizeController.text),
carbsPerPortion: double.tryParse(_carbsPerPortionController.text), carbsPerPortion: double.tryParse(_carbsPerPortionController.text),
portionSizeAccuracy: _portionSizeAccuracy,
carbsRatioAccuracy: _carbsRatioAccuracy,
bolus: double.tryParse(_bolusController.text), bolus: double.tryParse(_bolusController.text),
delayedBolusDuration: delayedBolusDuration:
int.tryParse(_delayedBolusDurationController.text), int.tryParse(_delayedBolusDurationController.text),
delayedBolusRate: delayedBolusRate: double.tryParse(_delayedBolusRateController.text),
double.tryParse(_delayedBolusRateController.text),
notes: _notesController.text,
)
: await LogMeal.update(
widget.logMeal!.objectId!,
meal: _meal,
value: _valueController.text,
source: _source,
category: _category,
portionType: _portionType,
carbsRatio: double.tryParse(_carbsRatioController.text),
portionSize: double.tryParse(_portionSizeController.text),
carbsPerPortion: double.tryParse(_carbsPerPortionController.text),
portionSizeAccuracy: _portionSizeAccuracy,
carbsRatioAccuracy: _carbsRatioAccuracy,
bolus: double.tryParse(_bolusController.text),
delayedBolusDuration:
int.tryParse(_delayedBolusDurationController.text),
delayedBolusRate:
double.tryParse(_delayedBolusRateController.text),
notes: _notesController.text, notes: _notesController.text,
); );
logMeal.meal.target = _meal;
logMeal.mealSource.target = _mealSource;
logMeal.mealCategory.target = _mealCategory;
logMeal.mealPortionType.target = _mealPortionType;
logMeal.portionSizeAccuracy.target = _portionSizeAccuracy;
logMeal.carbsRatioAccuracy.target = _carbsRatioAccuracy;
LogMeal.put(logMeal);
Navigator.pop(context, '${isNew ? 'New' : ''} Meal Saved'); Navigator.pop(context, '${isNew ? 'New' : ''} Meal Saved');
} }
setState(() { setState(() {
@ -187,9 +212,9 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
((isNew && ((isNew &&
(_valueController.text != '' || (_valueController.text != '' ||
_meal != null || _meal != null ||
_source != null || _mealSource != null ||
_category != null || _mealCategory != null ||
_portionType != null || _mealPortionType != null ||
double.tryParse(_carbsRatioController.text) != null || double.tryParse(_carbsRatioController.text) != null ||
double.tryParse(_portionSizeController.text) != null || double.tryParse(_portionSizeController.text) != null ||
double.tryParse(_carbsPerPortionController.text) != null || double.tryParse(_carbsPerPortionController.text) != null ||
@ -202,20 +227,25 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
_notesController.text != '')) || _notesController.text != '')) ||
(!isNew && (!isNew &&
(_valueController.text != widget.logMeal!.value || (_valueController.text != widget.logMeal!.value ||
_meal != widget.logMeal!.meal || _meal != widget.logMeal!.meal.target ||
_source != widget.logMeal!.source || _mealSource != widget.logMeal!.mealSource.target ||
_category != widget.logMeal!.category || _mealCategory != widget.logMeal!.mealCategory.target ||
_portionType != widget.logMeal!.portionType || _mealPortionType != widget.logMeal!.mealPortionType.target ||
double.tryParse(_carbsRatioController.text) != double.tryParse(_carbsRatioController.text) !=
widget.logMeal!.carbsRatio || widget.logMeal!.carbsRatio ||
double.tryParse(_portionSizeController.text) != double.tryParse(_portionSizeController.text) !=
widget.logMeal!.portionSize || widget.logMeal!.portionSize ||
double.tryParse(_carbsPerPortionController.text) != double.tryParse(_carbsPerPortionController.text) !=
widget.logMeal!.carbsPerPortion || widget.logMeal!.carbsPerPortion ||
_carbsRatioAccuracy != widget.logMeal!.carbsRatioAccuracy || // _carbsRatioAccuracy != widget.logMeal!.carbsRatioAccuracy ||
// _portionSizeAccuracy !=
// widget.logMeal!.portionSizeAccuracy ||
_carbsRatioAccuracy !=
widget.logMeal!.carbsRatioAccuracy.target ||
_portionSizeAccuracy != _portionSizeAccuracy !=
widget.logMeal!.portionSizeAccuracy || widget.logMeal!.portionSizeAccuracy.target ||
double.tryParse(_bolusController.text) != widget.logMeal!.bolus || double.tryParse(_bolusController.text) !=
widget.logMeal!.bolus ||
int.tryParse(_delayedBolusDurationController.text) != int.tryParse(_delayedBolusDurationController.text) !=
widget.logMeal!.delayedBolusDuration || widget.logMeal!.delayedBolusDuration ||
double.tryParse(_delayedBolusRateController.text) != double.tryParse(_delayedBolusRateController.text) !=
@ -300,49 +330,51 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
return null; return null;
}, },
), ),
StyledFutureDropdownButton<Meal>( StyledDropdownButton<Meal>(
selectedItem: _meal, selectedItem: _meal,
label: 'Meal', label: 'Meal',
items: _meals, items: _meals,
getItemValue: (item) => item.objectId, // getItemValue: (item) => item.objectId,
renderItem: (item) => Text(item.value), renderItem: (item) => Text(item.value),
onChanged: (value) { onChanged: (value) {
if (value != null) {
onSelectMeal(value); onSelectMeal(value);
}
}, },
), ),
StyledFutureDropdownButton<MealSource>( StyledDropdownButton<MealSource>(
selectedItem: _source, selectedItem: _mealSource,
label: 'Meal Source', label: 'Meal Source',
items: _mealSources, items: _mealSources,
getItemValue: (item) => item.objectId, // getItemValue: (item) => item.objectId,
renderItem: (item) => Text(item.value), renderItem: (item) => Text(item.value),
onChanged: (value) { onChanged: (value) {
setState(() { setState(() {
_source = value; _mealSource = value;
}); });
}, },
), ),
StyledFutureDropdownButton<MealCategory>( StyledDropdownButton<MealCategory>(
selectedItem: _category, selectedItem: _mealCategory,
label: 'Meal Category', label: 'Meal Category',
items: _mealCategories, items: _mealCategories,
getItemValue: (item) => item.objectId, // getItemValue: (item) => item.objectId,
renderItem: (item) => Text(item.value), renderItem: (item) => Text(item.value),
onChanged: (value) { onChanged: (value) {
setState(() { setState(() {
_category = value; _mealCategory = value;
}); });
}, },
), ),
StyledFutureDropdownButton<MealPortionType>( StyledDropdownButton<MealPortionType>(
selectedItem: _portionType, selectedItem: _mealPortionType,
label: 'Meal Portion Type', label: 'Meal Portion Type',
items: _mealPortionTypes, items: _mealPortionTypes,
getItemValue: (item) => item.objectId, // getItemValue: (item) => item.objectId,
renderItem: (item) => Text(item.value), renderItem: (item) => Text(item.value),
onChanged: (value) { onChanged: (value) {
setState(() { setState(() {
_portionType = value; _mealPortionType = value;
}); });
}, },
), ),
@ -402,11 +434,11 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
), ),
], ],
), ),
StyledFutureDropdownButton<Accuracy>( StyledDropdownButton<Accuracy>(
selectedItem: _portionSizeAccuracy, selectedItem: _portionSizeAccuracy,
label: 'Portion Size Accuracy', label: 'Portion Size Accuracy',
items: _portionSizeAccuracies, items: _portionSizeAccuracies,
getItemValue: (item) => item.objectId, // getItemValue: (item) => item.objectId,
renderItem: (item) => Text(item.value), renderItem: (item) => Text(item.value),
onChanged: (value) { onChanged: (value) {
setState(() { setState(() {
@ -414,6 +446,18 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
}); });
}, },
), ),
// StyledFutureDropdownButton<Accuracy>(
// selectedItem: _portionSizeAccuracy,
// label: 'Portion Size Accuracy',
// items: _portionSizeAccuracies,
// getItemValue: (item) => item.objectId,
// renderItem: (item) => Text(item.value),
// onChanged: (value) {
// setState(() {
// _portionSizeAccuracy = value;
// });
// },
// ),
Row( Row(
children: [ children: [
Expanded( Expanded(
@ -444,11 +488,11 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
), ),
], ],
), ),
StyledFutureDropdownButton<Accuracy>( StyledDropdownButton<Accuracy>(
selectedItem: _carbsRatioAccuracy, selectedItem: _carbsRatioAccuracy,
label: 'Carbs Ratio Accuracy', label: 'Carbs Ratio Accuracy',
items: _carbsRatioAccuracies, items: _carbsRatioAccuracies,
getItemValue: (item) => item.objectId, // getItemValue: (item) => item.objectId,
renderItem: (item) => Text(item.value), renderItem: (item) => Text(item.value),
onChanged: (value) { onChanged: (value) {
setState(() { setState(() {
@ -456,6 +500,18 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
}); });
}, },
), ),
// StyledFutureDropdownButton<Accuracy>(
// selectedItem: _carbsRatioAccuracy,
// label: 'Carbs Ratio Accuracy',
// items: _carbsRatioAccuracies,
// getItemValue: (item) => item.objectId,
// renderItem: (item) => Text(item.value),
// onChanged: (value) {
// setState(() {
// _carbsRatioAccuracy = value;
// });
// },
// ),
TextFormField( TextFormField(
decoration: const InputDecoration( decoration: const InputDecoration(
labelText: 'Bolus Units', labelText: 'Bolus Units',

View File

@ -4,24 +4,22 @@ import 'package:diameter/models/log_entry.dart';
import 'package:diameter/models/log_meal.dart'; import 'package:diameter/models/log_meal.dart';
import 'package:diameter/screens/log/log_meal_detail.dart'; import 'package:diameter/screens/log/log_meal_detail.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:diameter/components/progress_indicator.dart';
class LogMealListScreen extends StatefulWidget { class LogMealListScreen extends StatefulWidget {
final LogEntry? logEntry; final LogEntry logEntry;
final Function() reload;
const LogMealListScreen({Key? key, this.logEntry}) : super(key: key); const LogMealListScreen({Key? key, required this.logEntry, required this.reload})
: super(key: key);
@override @override
_LogMealListScreenState createState() => _LogMealListScreenState(); _LogMealListScreenState createState() => _LogMealListScreenState();
} }
class _LogMealListScreenState extends State<LogMealListScreen> { class _LogMealListScreenState extends State<LogMealListScreen> {
void refresh({String? message}) { void reload({String? message}) {
if (widget.logEntry != null) { widget.reload();
setState(() {
widget.logEntry!.meals = LogMeal.fetchAllForLogEntry(widget.logEntry!);
});
}
setState(() { setState(() {
if (message != null) { if (message != null) {
var snackBar = SnackBar( var snackBar = SnackBar(
@ -40,15 +38,16 @@ class _LogMealListScreenState extends State<LogMealListScreen> {
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => LogMealDetailScreen( builder: (context) => LogMealDetailScreen(
logEntry: widget.logEntry!, logEntry: widget.logEntry,
logMeal: meal, logMeal: meal,
), ),
), ),
).then((message) => refresh(message: message)); ).then((message) => reload(message: message));
} }
void onDelete(LogMeal meal) { void onDelete(LogMeal logMeal) {
meal.delete().then((_) => refresh(message: 'Meal deleted')); LogMeal.remove(logMeal.id);
reload(message: 'Meal deleted');
} }
void handleDeleteAction(LogMeal meal) async { void handleDeleteAction(LogMeal meal) async {
@ -63,43 +62,29 @@ class _LogMealListScreenState extends State<LogMealListScreen> {
} }
} }
@override
void initState() {
super.initState();
refresh();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return SingleChildScrollView( return Column(
padding: const EdgeInsets.only(top: 10.0), children: <Widget>[
child: Column( Expanded(
crossAxisAlignment: CrossAxisAlignment.center, child: widget.logEntry.meals.isNotEmpty ? ListView.builder(
children: [
FutureBuilder<List<LogMeal>>(
future: widget.logEntry!.meals,
builder: (context, snapshot) {
return ViewWithProgressIndicator(
snapshot: snapshot,
child: snapshot.data == null || snapshot.data!.isEmpty
? const Padding(
padding: EdgeInsets.all(10.0),
child: Text('No Meals for this Log Entry'),
)
: ListView.builder(
shrinkWrap: true, shrinkWrap: true,
itemCount: snapshot.data != null ? snapshot.data!.length : 0, itemCount: widget.logEntry.meals.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {
final meal = snapshot.data![index]; final meal = widget.logEntry.meals[index];
return ListTile( return ListTile(
onTap: () => handleEditAction(meal), onTap: () => handleEditAction(meal),
title: Row( title: Row(
children: [ children: [
Expanded(child: Text(meal.value)),
Expanded( Expanded(
child: Text(meal.value)), child: Text(meal.carbsPerPortion != null
? '${meal.carbsPerPortion} g carbs'
: '')),
Expanded( Expanded(
child: Text(meal.carbsPerPortion != null ? '${meal.carbsPerPortion} g carbs' : '')), child: Text(meal.bolus != null
Expanded(child: Text(meal.bolus != null ? '${meal.bolus} U' : '')) ? '${meal.bolus} U'
: ''))
], ],
), ),
trailing: IconButton( trailing: IconButton(
@ -111,12 +96,12 @@ class _LogMealListScreenState extends State<LogMealListScreen> {
), ),
); );
}, },
) : const Center(
child: Text(
'You have not added any Meals to this Log Entry yet!'),
), ),
);
},
), ),
], ],
),
); );
} }
} }

View File

@ -35,11 +35,13 @@ class _MealCategoryDetailScreenState extends State<MealCategoryDetailScreen> {
void handleSaveAction() async { void handleSaveAction() async {
if (_mealCategoryForm.currentState!.validate()) { if (_mealCategoryForm.currentState!.validate()) {
bool isNew = widget.mealCategory == null; bool isNew = widget.mealCategory == null;
isNew // isNew
? await MealCategory.save( // ? await MealCategory.save(
value: _valueController.text, notes: _notesController.text) // value: _valueController.text, notes: _notesController.text)
: await MealCategory.update(widget.mealCategory!.objectId!, // : await MealCategory.update(widget.mealCategory!.objectId!,
value: _valueController.text, notes: _notesController.text); // value: _valueController.text, notes: _notesController.text);
MealCategory.put(MealCategory(id: widget.mealCategory?.id ?? 0,
value: _valueController.text, notes: _notesController.text));
Navigator.pop(context, '${isNew ? 'New' : ''} Meal Category saved'); Navigator.pop(context, '${isNew ? 'New' : ''} Meal Category saved');
} }
} }

View File

@ -1,5 +1,5 @@
import 'package:diameter/components/dialogs.dart'; import 'package:diameter/components/dialogs.dart';
import 'package:diameter/components/progress_indicator.dart'; // import 'package:diameter/components/progress_indicator.dart';
import 'package:diameter/config.dart'; import 'package:diameter/config.dart';
import 'package:diameter/navigation.dart'; import 'package:diameter/navigation.dart';
import 'package:diameter/screens/meal/meal_category_detail.dart'; import 'package:diameter/screens/meal/meal_category_detail.dart';
@ -16,11 +16,11 @@ class MealCategoryListScreen extends StatefulWidget {
} }
class _MealCategoryListScreenState extends State<MealCategoryListScreen> { class _MealCategoryListScreenState extends State<MealCategoryListScreen> {
late Future<List<MealCategory>?> _mealCategories; List<MealCategory> _mealCategories = [];
void refresh({String? message}) { void refresh({String? message}) {
setState(() { setState(() {
_mealCategories = MealCategory.fetchAll(); _mealCategories = MealCategory.getAll();
}); });
setState(() { setState(() {
if (message != null) { if (message != null) {
@ -36,9 +36,8 @@ class _MealCategoryListScreenState extends State<MealCategoryListScreen> {
} }
void onDelete(MealCategory mealCategory) { void onDelete(MealCategory mealCategory) {
mealCategory MealCategory.remove(mealCategory.id);
.delete() refresh(message: 'Meal Category deleted');
.then((_) => refresh(message: 'Meal Category deleted'));
} }
void handleDeleteAction(MealCategory mealCategory) async { void handleDeleteAction(MealCategory mealCategory) async {
@ -77,27 +76,12 @@ class _MealCategoryListScreenState extends State<MealCategoryListScreen> {
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[ children: <Widget>[
Expanded( Expanded(
child: FutureBuilder<List<MealCategory>?>( child: _mealCategories.isNotEmpty ? ListView.builder(
future: _mealCategories,
builder: (context, snapshot) {
return ViewWithProgressIndicator(
snapshot: snapshot,
child: snapshot.data == null || snapshot.data!.isEmpty
? Row(
mainAxisAlignment: MainAxisAlignment.center,
children: const [
Padding(
padding: EdgeInsets.all(10.0),
child: Text('No Meal Categories'),
),
],
)
: ListView.builder(
padding: const EdgeInsets.only(top: 10.0), padding: const EdgeInsets.only(top: 10.0),
itemCount: itemCount: _mealCategories.length,
snapshot.data != null ? snapshot.data!.length : 0,
itemBuilder: (context, index) { itemBuilder: (context, index) {
final mealCategory = snapshot.data![index]; final mealCategory = _mealCategories[index];
return ListTile( return ListTile(
onTap: () { onTap: () {
Navigator.push( Navigator.push(
@ -126,9 +110,9 @@ class _MealCategoryListScreenState extends State<MealCategoryListScreen> {
], ],
), ),
); );
}),
);
}, },
): const Center(
child: Text('You have not created any Meal Categories yet!'),
), ),
), ),
], ],

View File

@ -8,6 +8,7 @@ import 'package:diameter/models/meal_category.dart';
import 'package:diameter/models/meal_portion_type.dart'; import 'package:diameter/models/meal_portion_type.dart';
import 'package:diameter/models/meal_source.dart'; import 'package:diameter/models/meal_source.dart';
import 'package:diameter/navigation.dart'; import 'package:diameter/navigation.dart';
// import 'package:diameter/objectbox.g.dart';
import 'package:diameter/settings.dart'; import 'package:diameter/settings.dart';
import 'package:diameter/utils/utils.dart'; import 'package:diameter/utils/utils.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -24,6 +25,7 @@ class MealDetailScreen extends StatefulWidget {
class _MealDetailScreenState extends State<MealDetailScreen> { class _MealDetailScreenState extends State<MealDetailScreen> {
final GlobalKey<FormState> _mealForm = GlobalKey<FormState>(); final GlobalKey<FormState> _mealForm = GlobalKey<FormState>();
final _valueController = TextEditingController(text: ''); final _valueController = TextEditingController(text: '');
final _carbsRatioController = TextEditingController(text: ''); final _carbsRatioController = TextEditingController(text: '');
final _portionSizeController = TextEditingController(text: ''); final _portionSizeController = TextEditingController(text: '');
@ -31,17 +33,18 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
final _delayedBolusRateController = TextEditingController(text: ''); final _delayedBolusRateController = TextEditingController(text: '');
final _delayedBolusDurationController = TextEditingController(text: ''); final _delayedBolusDurationController = TextEditingController(text: '');
final _notesController = TextEditingController(text: ''); final _notesController = TextEditingController(text: '');
String? _source;
String? _category;
String? _portionType;
String? _portionSizeAccuracy;
String? _carbsRatioAccuracy;
late Future<List<MealCategory>> _mealCategories; MealSource? _mealSource;
late Future<List<MealPortionType>> _mealPortionTypes; MealCategory? _mealCategory;
late Future<List<MealSource>> _mealSources; MealPortionType? _mealPortionType;
late Future<List<Accuracy>> _portionSizeAccuracies; Accuracy? _portionSizeAccuracy;
late Future<List<Accuracy>> _carbsRatioAccuracies; Accuracy? _carbsRatioAccuracy;
List<MealCategory> _mealCategories = [];
List<MealPortionType> _mealPortionTypes = [];
List<MealSource> _mealSources = [];
List<Accuracy> _portionSizeAccuracies = [];
List<Accuracy> _carbsRatioAccuracies = [];
bool isSaving = false; bool isSaving = false;
@ -49,6 +52,12 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
void initState() { void initState() {
super.initState(); super.initState();
_portionSizeAccuracies = Accuracy.getAllForPortionSize();
_carbsRatioAccuracies = Accuracy.getAllForCarbsRatio();
_mealCategories = MealCategory.getAll();
_mealPortionTypes = MealPortionType.getAll();
_mealSources = MealSource.getAll();
if (widget.meal != null) { if (widget.meal != null) {
_valueController.text = widget.meal!.value; _valueController.text = widget.meal!.value;
_carbsRatioController.text = (widget.meal!.carbsRatio ?? '').toString(); _carbsRatioController.text = (widget.meal!.carbsRatio ?? '').toString();
@ -61,18 +70,12 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
(widget.meal!.delayedBolusDuration ?? '').toString(); (widget.meal!.delayedBolusDuration ?? '').toString();
_notesController.text = widget.meal!.notes ?? ''; _notesController.text = widget.meal!.notes ?? '';
_source = widget.meal!.source; _mealSource = widget.meal!.mealSource.target;
_category = widget.meal!.category; _mealCategory = widget.meal!.mealCategory.target;
_portionType = widget.meal!.portionType; _mealPortionType = widget.meal!.mealPortionType.target;
_portionSizeAccuracy = widget.meal!.portionSizeAccuracy; _portionSizeAccuracy = widget.meal!.portionSizeAccuracy.target;
_carbsRatioAccuracy = widget.meal!.carbsRatioAccuracy; _carbsRatioAccuracy = widget.meal!.carbsRatioAccuracy.target;
} }
_mealCategories = MealCategory.fetchAll();
_mealPortionTypes = MealPortionType.fetchAll();
_mealSources = MealSource.fetchAll();
_portionSizeAccuracies = Accuracy.fetchAllForPortionSize();
_carbsRatioAccuracies = Accuracy.fetchAllForCarbsRatio();
} }
void handleSaveAction() async { void handleSaveAction() async {
@ -81,40 +84,62 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
}); });
if (_mealForm.currentState!.validate()) { if (_mealForm.currentState!.validate()) {
bool isNew = widget.meal == null; bool isNew = widget.meal == null;
isNew // isNew
? await Meal.save( // ? await Meal.save(
// value: _valueController.text,
// source: _mealSource,
// category: _mealCategory,
// portionType: _mealPortionType,
// carbsRatio: double.tryParse(_carbsRatioController.text),
// portionSize: double.tryParse(_portionSizeController.text),
// carbsPerPortion: double.tryParse(_carbsPerPortionController.text),
// // portionSizeAccuracy: _portionSizeAccuracy,
// // carbsRatioAccuracy: _carbsRatioAccuracy,
// portionSizeAccuracy: _portionSizeAccuracy?.id.toString(),
// carbsRatioAccuracy: _carbsRatioAccuracy?.id.toString(),
// delayedBolusDuration:
// int.tryParse(_delayedBolusDurationController.text),
// delayedBolusRate:
// double.tryParse(_delayedBolusRateController.text),
// notes: _notesController.text,
// )
// : await Meal.update(
// widget.meal!.objectId!,
// value: _valueController.text,
// source: _mealSource,
// category: _mealCategory,
// portionType: _mealPortionType,
// carbsRatio: double.tryParse(_carbsRatioController.text),
// portionSize: double.tryParse(_portionSizeController.text),
// carbsPerPortion: double.tryParse(_carbsPerPortionController.text),
// // portionSizeAccuracy: _portionSizeAccuracy,
// // carbsRatioAccuracy: _carbsRatioAccuracy,
// portionSizeAccuracy: _portionSizeAccuracy?.id.toString(),
// carbsRatioAccuracy: _carbsRatioAccuracy?.id.toString(),
// delayedBolusDuration:
// int.tryParse(_delayedBolusDurationController.text),
// delayedBolusRate:
// double.tryParse(_delayedBolusRateController.text),
// notes: _notesController.text,
// );
Meal meal = Meal(
id: widget.meal?.id ?? 0,
value: _valueController.text, value: _valueController.text,
source: _source,
category: _category,
portionType: _portionType,
carbsRatio: double.tryParse(_carbsRatioController.text), carbsRatio: double.tryParse(_carbsRatioController.text),
portionSize: double.tryParse(_portionSizeController.text), portionSize: double.tryParse(_portionSizeController.text),
carbsPerPortion: double.tryParse(_carbsPerPortionController.text), carbsPerPortion: double.tryParse(_carbsPerPortionController.text),
portionSizeAccuracy: _portionSizeAccuracy,
carbsRatioAccuracy: _carbsRatioAccuracy,
delayedBolusDuration: delayedBolusDuration:
int.tryParse(_delayedBolusDurationController.text), int.tryParse(_delayedBolusDurationController.text),
delayedBolusRate: delayedBolusRate: double.tryParse(_delayedBolusRateController.text),
double.tryParse(_delayedBolusRateController.text),
notes: _notesController.text,
)
: await Meal.update(
widget.meal!.objectId!,
value: _valueController.text,
source: _source,
category: _category,
portionType: _portionType,
carbsRatio: double.tryParse(_carbsRatioController.text),
portionSize: double.tryParse(_portionSizeController.text),
carbsPerPortion: double.tryParse(_carbsPerPortionController.text),
portionSizeAccuracy: _portionSizeAccuracy,
carbsRatioAccuracy: _carbsRatioAccuracy,
delayedBolusDuration:
int.tryParse(_delayedBolusDurationController.text),
delayedBolusRate:
double.tryParse(_delayedBolusRateController.text),
notes: _notesController.text, notes: _notesController.text,
); );
meal.mealSource.target = _mealSource;
meal.mealCategory.target = _mealCategory;
meal.mealPortionType.target = _mealPortionType;
meal.portionSizeAccuracy.target = _portionSizeAccuracy;
meal.carbsRatioAccuracy.target = _carbsRatioAccuracy;
Meal.put(meal);
Navigator.pop(context, '${isNew ? 'New' : ''} Meal Saved'); Navigator.pop(context, '${isNew ? 'New' : ''} Meal Saved');
} }
setState(() { setState(() {
@ -127,9 +152,9 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
if (showConfirmationDialogOnCancel && if (showConfirmationDialogOnCancel &&
((isNew && ((isNew &&
(_valueController.text != '' || (_valueController.text != '' ||
_source != null || _mealSource != null ||
_category != null || _mealCategory != null ||
_portionType != null || _mealPortionType != null ||
double.tryParse(_carbsRatioController.text) != null || double.tryParse(_carbsRatioController.text) != null ||
double.tryParse(_portionSizeController.text) != null || double.tryParse(_portionSizeController.text) != null ||
double.tryParse(_carbsPerPortionController.text) != null || double.tryParse(_carbsPerPortionController.text) != null ||
@ -141,17 +166,17 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
_notesController.text != '')) || _notesController.text != '')) ||
(!isNew && (!isNew &&
(_valueController.text != widget.meal!.value || (_valueController.text != widget.meal!.value ||
_source != widget.meal!.source || _mealSource != widget.meal!.mealSource.target ||
_category != widget.meal!.category || _mealCategory != widget.meal!.mealCategory.target ||
_portionType != widget.meal!.portionType || _mealPortionType != widget.meal!.mealPortionType.target ||
double.tryParse(_carbsRatioController.text) != double.tryParse(_carbsRatioController.text) !=
widget.meal!.carbsRatio || widget.meal!.carbsRatio ||
double.tryParse(_portionSizeController.text) != double.tryParse(_portionSizeController.text) !=
widget.meal!.portionSize || widget.meal!.portionSize ||
double.tryParse(_carbsPerPortionController.text) != double.tryParse(_carbsPerPortionController.text) !=
widget.meal!.carbsPerPortion || widget.meal!.carbsPerPortion ||
_carbsRatioAccuracy != widget.meal!.carbsRatioAccuracy || _carbsRatioAccuracy != widget.meal!.carbsRatioAccuracy.target ||
_portionSizeAccuracy != widget.meal!.portionSizeAccuracy || _portionSizeAccuracy != widget.meal!.portionSizeAccuracy.target ||
int.tryParse(_delayedBolusDurationController.text) != int.tryParse(_delayedBolusDurationController.text) !=
widget.meal!.delayedBolusDuration || widget.meal!.delayedBolusDuration ||
double.tryParse(_delayedBolusRateController.text) != double.tryParse(_delayedBolusRateController.text) !=
@ -167,30 +192,25 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
} }
} }
Future<void> onSelectMealSource(String? objectId) async { Future<void> onSelectMealSource(MealSource mealSource) async {
if (objectId != null) {
MealSource? mealSource = await MealSource.get(objectId);
if (mealSource != null) {
setState(() { setState(() {
_source = objectId; _mealSource = mealSource;
if (mealSource.defaultCarbsRatioAccuracy != null) { if (mealSource.defaultCarbsRatioAccuracy.hasValue) {
_carbsRatioAccuracy = _carbsRatioAccuracy =
mealSource.defaultCarbsRatioAccuracy.toString(); mealSource.defaultCarbsRatioAccuracy.target;
} }
if (mealSource.defaultPortionSizeAccuracy != null) { if (mealSource.defaultPortionSizeAccuracy.hasValue) {
_portionSizeAccuracy = _portionSizeAccuracy =
mealSource.defaultPortionSizeAccuracy.toString(); mealSource.defaultPortionSizeAccuracy.target;
} }
if (mealSource.defaultMealCategory != null) { if (mealSource.defaultMealCategory.hasValue) {
_category = mealSource.defaultMealCategory.toString(); _mealCategory = mealSource.defaultMealCategory.target;
} }
if (mealSource.defaultMealPortionType != null) { if (mealSource.defaultMealPortionType.hasValue) {
_portionType = mealSource.defaultMealPortionType.toString(); _mealPortionType = mealSource.defaultMealPortionType.target;
} }
}); });
} }
}
}
void calculateThirdMeasurementOfPortionCarbsRelation( void calculateThirdMeasurementOfPortionCarbsRelation(
{PortionCarbsParameter? parameterToBeCalculated}) { {PortionCarbsParameter? parameterToBeCalculated}) {
@ -261,37 +281,39 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
return null; return null;
}, },
), ),
StyledFutureDropdownButton<MealSource>( StyledDropdownButton<MealSource>(
selectedItem: _source, selectedItem: _mealSource,
label: 'Meal Source', label: 'Meal Source',
items: _mealSources, items: _mealSources,
getItemValue: (item) => item.objectId, // getItemValue: (item) => item.objectId,
renderItem: (item) => Text(item.value), renderItem: (item) => Text(item.value),
onChanged: (value) { onChanged: (value) {
if (value != null) {
onSelectMealSource(value); onSelectMealSource(value);
}
}, },
), ),
StyledFutureDropdownButton<MealCategory>( StyledDropdownButton<MealCategory>(
selectedItem: _category, selectedItem: _mealCategory,
label: 'Meal Category', label: 'Meal Category',
items: _mealCategories, items: _mealCategories,
getItemValue: (item) => item.objectId, // getItemValue: (item) => item.objectId,
renderItem: (item) => Text(item.value), renderItem: (item) => Text(item.value),
onChanged: (value) { onChanged: (value) {
setState(() { setState(() {
_category = value; _mealCategory = value;
}); });
}, },
), ),
StyledFutureDropdownButton<MealPortionType>( StyledDropdownButton<MealPortionType>(
selectedItem: _portionType, selectedItem: _mealPortionType,
label: 'Meal Portion Type', label: 'Meal Portion Type',
items: _mealPortionTypes, items: _mealPortionTypes,
getItemValue: (item) => item.objectId, // getItemValue: (item) => item.objectId,
renderItem: (item) => Text(item.value), renderItem: (item) => Text(item.value),
onChanged: (value) { onChanged: (value) {
setState(() { setState(() {
_portionType = value; _mealPortionType = value;
}); });
}, },
), ),
@ -351,11 +373,11 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
), ),
], ],
), ),
StyledFutureDropdownButton<Accuracy>( StyledDropdownButton<Accuracy>(
selectedItem: _portionSizeAccuracy, selectedItem: _portionSizeAccuracy,
label: 'Portion Size Accuracy', label: 'Portion Size Accuracy',
items: _portionSizeAccuracies, items: _portionSizeAccuracies,
getItemValue: (item) => item.objectId, // getItemValue: (item) => item.objectId,
renderItem: (item) => Text(item.value), renderItem: (item) => Text(item.value),
onChanged: (value) { onChanged: (value) {
setState(() { setState(() {
@ -363,6 +385,18 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
}); });
}, },
), ),
// StyledFutureDropdownButton<Accuracy>(
// selectedItem: _portionSizeAccuracy,
// label: 'Portion Size Accuracy',
// items: _portionSizeAccuracies,
// getItemValue: (item) => item.objectId,
// renderItem: (item) => Text(item.value),
// onChanged: (value) {
// setState(() {
// _portionSizeAccuracy = value;
// });
// },
// ),
Row( Row(
children: [ children: [
Expanded( Expanded(
@ -393,11 +427,11 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
), ),
], ],
), ),
StyledFutureDropdownButton<Accuracy>( StyledDropdownButton<Accuracy>(
selectedItem: _carbsRatioAccuracy, selectedItem: _carbsRatioAccuracy,
label: 'Carbs Ratio Accuracy', label: 'Carbs Ratio Accuracy',
items: _carbsRatioAccuracies, items: _carbsRatioAccuracies,
getItemValue: (item) => item.objectId, // getItemValue: (item) => item.objectId,
renderItem: (item) => Text(item.value), renderItem: (item) => Text(item.value),
onChanged: (value) { onChanged: (value) {
setState(() { setState(() {
@ -405,6 +439,18 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
}); });
}, },
), ),
// StyledFutureDropdownButton<Accuracy>(
// selectedItem: _carbsRatioAccuracy,
// label: 'Carbs Ratio Accuracy',
// items: _carbsRatioAccuracies,
// getItemValue: (item) => item.objectId,
// renderItem: (item) => Text(item.value),
// onChanged: (value) {
// setState(() {
// _carbsRatioAccuracy = value;
// });
// },
// ),
// TODO: display according to time format // TODO: display according to time format
TextFormField( TextFormField(
decoration: const InputDecoration( decoration: const InputDecoration(

View File

@ -1,5 +1,5 @@
import 'package:diameter/components/dialogs.dart'; import 'package:diameter/components/dialogs.dart';
import 'package:diameter/components/progress_indicator.dart'; // import 'package:diameter/components/progress_indicator.dart';
import 'package:diameter/config.dart'; import 'package:diameter/config.dart';
import 'package:diameter/models/meal.dart'; import 'package:diameter/models/meal.dart';
import 'package:diameter/navigation.dart'; import 'package:diameter/navigation.dart';
@ -16,11 +16,11 @@ class MealListScreen extends StatefulWidget {
} }
class _MealListScreenState extends State<MealListScreen> { class _MealListScreenState extends State<MealListScreen> {
late Future<List<Meal>?> _meals; List<Meal> _meals = [];
void refresh({String? message}) { void refresh({String? message}) {
setState(() { setState(() {
_meals = Meal.fetchAll(); _meals = Meal.getAll();
}); });
setState(() { setState(() {
if (message != null) { if (message != null) {
@ -36,7 +36,8 @@ class _MealListScreenState extends State<MealListScreen> {
} }
void onDelete(Meal meal) { void onDelete(Meal meal) {
meal.delete().then((_) => refresh(message: 'Meal deleted')); Meal.remove(meal.id);
refresh(message: 'Meal deleted');
} }
void handleDeleteAction(Meal meal) async { void handleDeleteAction(Meal meal) async {
@ -68,27 +69,12 @@ class _MealListScreenState extends State<MealListScreen> {
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[ children: <Widget>[
Expanded( Expanded(
child: FutureBuilder<List<Meal>?>( child: _meals.isNotEmpty ? ListView.builder(
future: _meals,
builder: (context, snapshot) {
return ViewWithProgressIndicator(
snapshot: snapshot,
child: snapshot.data == null || snapshot.data!.isEmpty
? Row(
mainAxisAlignment: MainAxisAlignment.center,
children: const [
Padding(
padding: EdgeInsets.all(10.0),
child: Text('No Meals'),
),
],
)
: ListView.builder(
padding: const EdgeInsets.all(10.0), padding: const EdgeInsets.all(10.0),
itemCount: itemCount: _meals.length,
snapshot.data != null ? snapshot.data!.length : 0,
itemBuilder: (context, index) { itemBuilder: (context, index) {
final meal = snapshot.data![index]; final meal = _meals[index];
return ListTile( return ListTile(
onTap: () { onTap: () {
Navigator.push( Navigator.push(
@ -113,9 +99,8 @@ class _MealListScreenState extends State<MealListScreen> {
), ),
); );
}, },
), ): const Center(
); child: Text('You have not created any Meals yet!'),
},
), ),
), ),
], ],

View File

@ -37,16 +37,21 @@ class _MealPortionTypeDetailScreenState
void handleSaveAction() async { void handleSaveAction() async {
if (_mealPortionTypeForm.currentState!.validate()) { if (_mealPortionTypeForm.currentState!.validate()) {
bool isNew = widget.mealPortionType == null; bool isNew = widget.mealPortionType == null;
isNew // isNew
? MealPortionType.save( // ? MealPortionType.save(
// value: _valueController.text,
// notes: _notesController.text,
// )
// : MealPortionType.update(
// widget.mealPortionType!.objectId!,
// value: _valueController.text,
// notes: _notesController.text,
// );
MealPortionType.put(MealPortionType(
id: widget.mealPortionType?.id ?? 0,
value: _valueController.text, value: _valueController.text,
notes: _notesController.text, notes: _notesController.text,
) ));
: MealPortionType.update(
widget.mealPortionType!.objectId!,
value: _valueController.text,
notes: _notesController.text,
);
Navigator.pop(context, '${isNew ? 'New' : ''} Meal Portion Type saved'); Navigator.pop(context, '${isNew ? 'New' : ''} Meal Portion Type saved');
} }
} }

View File

@ -1,5 +1,5 @@
import 'package:diameter/components/dialogs.dart'; import 'package:diameter/components/dialogs.dart';
import 'package:diameter/components/progress_indicator.dart'; // import 'package:diameter/components/progress_indicator.dart';
import 'package:diameter/config.dart'; import 'package:diameter/config.dart';
import 'package:diameter/navigation.dart'; import 'package:diameter/navigation.dart';
import 'package:diameter/screens/meal/meal_portion_type_detail.dart'; import 'package:diameter/screens/meal/meal_portion_type_detail.dart';
@ -17,11 +17,11 @@ class MealPortionTypeListScreen extends StatefulWidget {
} }
class _MealPortionTypeListScreenState extends State<MealPortionTypeListScreen> { class _MealPortionTypeListScreenState extends State<MealPortionTypeListScreen> {
late Future<List<MealPortionType>?> _mealPortionTypes; List<MealPortionType> _mealPortionTypes = [];
void refresh({String? message}) { void refresh({String? message}) {
setState(() { setState(() {
_mealPortionTypes = MealPortionType.fetchAll(); _mealPortionTypes = MealPortionType.getAll();
}); });
setState(() { setState(() {
if (message != null) { if (message != null) {
@ -37,9 +37,8 @@ class _MealPortionTypeListScreenState extends State<MealPortionTypeListScreen> {
} }
void onDelete(MealPortionType mealPortionType) { void onDelete(MealPortionType mealPortionType) {
mealPortionType MealPortionType.remove(mealPortionType.id);
.delete() refresh(message: 'Meal Portion Type deleted');
.then((_) => refresh(message: 'Meal Portion Type deleted'));
} }
void handleDeleteAction(MealPortionType mealPortionType) async { void handleDeleteAction(MealPortionType mealPortionType) async {
@ -75,28 +74,12 @@ class _MealPortionTypeListScreenState extends State<MealPortionTypeListScreen> {
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[ children: <Widget>[
Expanded( Expanded(
child: FutureBuilder<List<MealPortionType>?>( child: _mealPortionTypes.isNotEmpty ? ListView.builder(
future: _mealPortionTypes,
builder: (context, snapshot) {
return ViewWithProgressIndicator(
snapshot: snapshot,
child: snapshot.data == null || snapshot.data!.isEmpty
? Row(
mainAxisAlignment: MainAxisAlignment.center,
children: const [
Padding(
padding: EdgeInsets.all(10.0),
child: Text('No Meal Portion Types'),
)
],
)
: ListView.builder(
padding: const EdgeInsets.only(top: 10.0), padding: const EdgeInsets.only(top: 10.0),
itemCount: snapshot.data != null itemCount: _mealPortionTypes.length,
? snapshot.data!.length
: 0,
itemBuilder: (context, index) { itemBuilder: (context, index) {
final mealPortionType = snapshot.data![index]; final mealPortionType = _mealPortionTypes[index];
return ListTile( return ListTile(
onTap: () { onTap: () {
Navigator.push( Navigator.push(
@ -126,8 +109,9 @@ class _MealPortionTypeListScreenState extends State<MealPortionTypeListScreen> {
], ],
), ),
); );
}));
}, },
) : const Center(
child: Text('You have not created any Meal Portion Types yet!'),
), ),
), ),
], ],

View File

@ -2,11 +2,13 @@ import 'package:diameter/components/detail.dart';
import 'package:diameter/components/dialogs.dart'; import 'package:diameter/components/dialogs.dart';
import 'package:diameter/components/forms.dart'; import 'package:diameter/components/forms.dart';
import 'package:diameter/config.dart'; import 'package:diameter/config.dart';
// import 'package:diameter/main.dart';
import 'package:diameter/models/accuracy.dart'; import 'package:diameter/models/accuracy.dart';
import 'package:diameter/models/meal_category.dart'; import 'package:diameter/models/meal_category.dart';
import 'package:diameter/models/meal_portion_type.dart'; import 'package:diameter/models/meal_portion_type.dart';
import 'package:diameter/models/meal_source.dart'; import 'package:diameter/models/meal_source.dart';
import 'package:diameter/navigation.dart'; import 'package:diameter/navigation.dart';
// import 'package:diameter/objectbox.g.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class MealSourceDetailScreen extends StatefulWidget { class MealSourceDetailScreen extends StatefulWidget {
@ -21,61 +23,79 @@ class MealSourceDetailScreen extends StatefulWidget {
} }
class _MealSourceDetailScreenState extends State<MealSourceDetailScreen> { class _MealSourceDetailScreenState extends State<MealSourceDetailScreen> {
late Future<List<Accuracy>> _portionSizeAccuracies; List<Accuracy> _portionSizeAccuracies = [];
late Future<List<Accuracy>> _carbsRatioAccuracies; List<Accuracy> _carbsRatioAccuracies = [];
late Future<List<MealCategory>> _mealCategories; List<MealCategory> _mealCategories = [];
late Future<List<MealPortionType>> _mealPortionTypes; List<MealPortionType> _mealPortionTypes = [];
final GlobalKey<FormState> _mealSourceForm = GlobalKey<FormState>(); final GlobalKey<FormState> _mealSourceForm = GlobalKey<FormState>();
final _valueController = TextEditingController(text: ''); final _valueController = TextEditingController(text: '');
final _notesController = TextEditingController(text: ''); final _notesController = TextEditingController(text: '');
String? _defaultCarbsRatioAccuracy; Accuracy? _defaultCarbsRatioAccuracy;
String? _defaultPortionSizeAccuracy; Accuracy? _defaultPortionSizeAccuracy;
String? _defaultMealCategory; MealCategory? _defaultMealCategory;
String? _defaultMealPortionType; MealPortionType? _defaultMealPortionType;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_portionSizeAccuracies = Accuracy.getAllForPortionSize();
_carbsRatioAccuracies = Accuracy.getAllForCarbsRatio();
_mealCategories = MealCategory.getAll();
_mealPortionTypes = MealPortionType.getAll();
if (widget.mealSource != null) { if (widget.mealSource != null) {
_valueController.text = widget.mealSource!.value; _valueController.text = widget.mealSource!.value;
_notesController.text = widget.mealSource!.notes ?? ''; _notesController.text = widget.mealSource!.notes ?? '';
_defaultCarbsRatioAccuracy = widget.mealSource!.defaultCarbsRatioAccuracy;
_defaultPortionSizeAccuracy = _defaultPortionSizeAccuracy =
widget.mealSource!.defaultPortionSizeAccuracy; widget.mealSource!.defaultPortionSizeAccuracy.target;
_defaultMealCategory = widget.mealSource!.defaultMealCategory; _defaultCarbsRatioAccuracy = widget.mealSource!.defaultCarbsRatioAccuracy.target;
_defaultMealPortionType = widget.mealSource!.defaultMealPortionType;
}
_portionSizeAccuracies = Accuracy.fetchAllForPortionSize(); _defaultMealCategory = widget.mealSource!.defaultMealCategory.target;
_carbsRatioAccuracies = Accuracy.fetchAllForCarbsRatio(); _defaultMealPortionType =
_mealCategories = MealCategory.fetchAll(); widget.mealSource!.defaultMealPortionType.target;
_mealPortionTypes = MealPortionType.fetchAll(); }
} }
void handleSaveAction() async { void handleSaveAction() async {
bool isNew = widget.mealSource == null; bool isNew = widget.mealSource == null;
if (_mealSourceForm.currentState!.validate()) { if (_mealSourceForm.currentState!.validate()) {
isNew // isNew
? await MealSource.save( // ? await MealSource.save(
// value: _valueController.text,
// defaultCarbsRatioAccuracy: _defaultCarbsRatioAccuracy?.id.toString(),
// defaultPortionSizeAccuracy: _defaultPortionSizeAccuracy?.id.toString(),
// // defaultCarbsRatioAccuracy: _defaultCarbsRatioAccuracy,
// // defaultPortionSizeAccuracy: _defaultPortionSizeAccuracy,
// defaultMealCategory: _defaultMealCategory,
// defaultMealPortionType: _defaultMealPortionType,
// notes: _notesController.text,
// )
// : await MealSource.update(
// widget.mealSource!.objectId!,
// value: _valueController.text,
// defaultCarbsRatioAccuracy: _defaultCarbsRatioAccuracy?.id.toString(),
// defaultPortionSizeAccuracy: _defaultPortionSizeAccuracy?.id.toString(),
// // defaultCarbsRatioAccuracy: _defaultCarbsRatioAccuracy,
// // defaultPortionSizeAccuracy: _defaultPortionSizeAccuracy,
// defaultMealCategory: _defaultMealCategory,
// defaultMealPortionType: _defaultMealPortionType,
// notes: _notesController.text,
// );
MealSource mealSource = MealSource(
id: widget.mealSource?.id ?? 0,
value: _valueController.text, value: _valueController.text,
defaultCarbsRatioAccuracy: _defaultCarbsRatioAccuracy,
defaultPortionSizeAccuracy: _defaultPortionSizeAccuracy,
defaultMealCategory: _defaultMealCategory,
defaultMealPortionType: _defaultMealPortionType,
notes: _notesController.text,
)
: await MealSource.update(
widget.mealSource!.objectId!,
value: _valueController.text,
defaultCarbsRatioAccuracy: _defaultCarbsRatioAccuracy,
defaultPortionSizeAccuracy: _defaultPortionSizeAccuracy,
defaultMealCategory: _defaultMealCategory,
defaultMealPortionType: _defaultMealPortionType,
notes: _notesController.text, notes: _notesController.text,
); );
mealSource.defaultCarbsRatioAccuracy.target = _defaultCarbsRatioAccuracy;
mealSource.defaultPortionSizeAccuracy.target =
_defaultPortionSizeAccuracy;
mealSource.defaultMealCategory.target = _defaultMealCategory;
mealSource.defaultMealPortionType.target = _defaultMealPortionType;
MealSource.put(mealSource);
Navigator.pop(context, '${isNew ? 'New' : ''} Meal Source saved'); Navigator.pop(context, '${isNew ? 'New' : ''} Meal Source saved');
} }
} }
@ -93,13 +113,13 @@ class _MealSourceDetailScreenState extends State<MealSourceDetailScreen> {
(!isNew && (!isNew &&
(_valueController.text != widget.mealSource!.value || (_valueController.text != widget.mealSource!.value ||
_defaultCarbsRatioAccuracy != _defaultCarbsRatioAccuracy !=
widget.mealSource!.defaultCarbsRatioAccuracy || widget.mealSource!.defaultCarbsRatioAccuracy.target ||
_defaultPortionSizeAccuracy != _defaultPortionSizeAccuracy !=
widget.mealSource!.defaultPortionSizeAccuracy || widget.mealSource!.defaultPortionSizeAccuracy.target ||
_defaultMealCategory != _defaultMealCategory !=
widget.mealSource!.defaultMealCategory || widget.mealSource!.defaultMealCategory.target ||
_defaultMealPortionType != _defaultMealPortionType !=
widget.mealSource!.defaultMealPortionType || widget.mealSource!.defaultMealPortionType.target ||
_notesController.text != _notesController.text !=
(widget.mealSource!.notes ?? ''))))) { (widget.mealSource!.notes ?? ''))))) {
Dialogs.showCancelConfirmationDialog( Dialogs.showCancelConfirmationDialog(
@ -140,11 +160,10 @@ class _MealSourceDetailScreenState extends State<MealSourceDetailScreen> {
return null; return null;
}, },
), ),
StyledFutureDropdownButton<Accuracy>( StyledDropdownButton<Accuracy>(
selectedItem: _defaultCarbsRatioAccuracy, selectedItem: _defaultCarbsRatioAccuracy,
label: 'Default Carbs Ratio Accuracy', label: 'Default Carbs Ratio Accuracy',
items: _carbsRatioAccuracies, items: _carbsRatioAccuracies,
getItemValue: (item) => item.objectId,
renderItem: (item) => Text(item.value), renderItem: (item) => Text(item.value),
onChanged: (value) { onChanged: (value) {
setState(() { setState(() {
@ -152,11 +171,10 @@ class _MealSourceDetailScreenState extends State<MealSourceDetailScreen> {
}); });
}, },
), ),
StyledFutureDropdownButton<Accuracy>( StyledDropdownButton<Accuracy>(
selectedItem: _defaultPortionSizeAccuracy, selectedItem: _defaultPortionSizeAccuracy,
label: 'Default Portion Size Accuracy', label: 'Default Portion Size Accuracy',
items: _portionSizeAccuracies, items: _portionSizeAccuracies,
getItemValue: (item) => item.objectId,
renderItem: (item) => Text(item.value), renderItem: (item) => Text(item.value),
onChanged: (value) { onChanged: (value) {
setState(() { setState(() {
@ -164,11 +182,35 @@ class _MealSourceDetailScreenState extends State<MealSourceDetailScreen> {
}); });
}, },
), ),
StyledFutureDropdownButton<MealCategory>( // StyledFutureDropdownButton<Accuracy>(
// selectedItem: _defaultCarbsRatioAccuracy,
// label: 'Default Carbs Ratio Accuracy',
// items: _carbsRatioAccuracies,
// getItemValue: (item) => item.objectId,
// renderItem: (item) => Text(item.value),
// onChanged: (value) {
// setState(() {
// _defaultCarbsRatioAccuracy = value;
// });
// },
// ),
// StyledFutureDropdownButton<Accuracy>(
// selectedItem: _defaultPortionSizeAccuracy,
// label: 'Default Portion Size Accuracy',
// items: _portionSizeAccuracies,
// getItemValue: (item) => item.objectId,
// renderItem: (item) => Text(item.value),
// onChanged: (value) {
// setState(() {
// _defaultPortionSizeAccuracy = value;
// });
// },
// ),
StyledDropdownButton<MealCategory>(
selectedItem: _defaultMealCategory, selectedItem: _defaultMealCategory,
label: 'Default Meal Category', label: 'Default Meal Category',
items: _mealCategories, items: _mealCategories,
getItemValue: (item) => item.objectId, // getItemValue: (item) => item.objectId,
renderItem: (item) => Text(item.value), renderItem: (item) => Text(item.value),
onChanged: (value) { onChanged: (value) {
setState(() { setState(() {
@ -176,11 +218,11 @@ class _MealSourceDetailScreenState extends State<MealSourceDetailScreen> {
}); });
}, },
), ),
StyledFutureDropdownButton<MealPortionType>( StyledDropdownButton<MealPortionType>(
selectedItem: _defaultMealPortionType, selectedItem: _defaultMealPortionType,
label: 'Default Meal Portion Type', label: 'Default Meal Portion Type',
items: _mealPortionTypes, items: _mealPortionTypes,
getItemValue: (item) => item.objectId, // getItemValue: (item) => item.objectId,
renderItem: (item) => Text(item.value), renderItem: (item) => Text(item.value),
onChanged: (value) { onChanged: (value) {
setState(() { setState(() {

View File

@ -1,4 +1,6 @@
import 'package:diameter/components/progress_indicator.dart'; // import 'package:diameter/components/progress_indicator.dart';
import 'package:diameter/components/dialogs.dart';
import 'package:diameter/config.dart';
import 'package:diameter/models/meal_source.dart'; import 'package:diameter/models/meal_source.dart';
import 'package:diameter/navigation.dart'; import 'package:diameter/navigation.dart';
import 'package:diameter/screens/meal/meal_source_detail.dart'; import 'package:diameter/screens/meal/meal_source_detail.dart';
@ -14,11 +16,11 @@ class MealSourceListScreen extends StatefulWidget {
} }
class _MealSourceListScreenState extends State<MealSourceListScreen> { class _MealSourceListScreenState extends State<MealSourceListScreen> {
late Future<List<MealSource>?> _mealSources; List<MealSource> _mealSources = [];
void refresh({String? message}) { void refresh({String? message}) {
setState(() { setState(() {
_mealSources = MealSource.fetchAll(); _mealSources = MealSource.getAll();
}); });
setState(() { setState(() {
if (message != null) { if (message != null) {
@ -33,6 +35,23 @@ class _MealSourceListScreenState extends State<MealSourceListScreen> {
}); });
} }
void onDelete(MealSource mealSource) {
MealSource.remove(mealSource.id);
refresh(message: 'Meal Source deleted');
}
void handleDeleteAction(MealSource mealSource) async {
if (showConfirmationDialogOnDelete) {
Dialogs.showConfirmationDialog(
context: context,
onConfirm: () => onDelete(mealSource),
message: 'Are you sure you want to delete this Meal Source?',
);
} else {
onDelete(mealSource);
}
}
@override @override
void initState() { void initState() {
super.initState(); super.initState();
@ -53,40 +72,22 @@ class _MealSourceListScreenState extends State<MealSourceListScreen> {
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[ children: <Widget>[
Expanded( Expanded(
child: FutureBuilder<List<MealSource>?>( child: _mealSources.isNotEmpty ? ListView.builder(
future: _mealSources,
builder: (context, snapshot) {
return ViewWithProgressIndicator(
snapshot: snapshot,
child: snapshot.data == null || snapshot.data!.isEmpty
? Row(
mainAxisAlignment: MainAxisAlignment.center,
children: const [
Padding(
padding: EdgeInsets.all(10.0),
child: Text('No Meal Sources'),
)
],
)
: ListView.builder(
padding: const EdgeInsets.only(top: 10.0), padding: const EdgeInsets.only(top: 10.0),
itemCount: snapshot.data != null itemCount: _mealSources.length,
? snapshot.data!.length
: 0,
itemBuilder: (context, index) { itemBuilder: (context, index) {
final mealSource = snapshot.data![index]; final mealSource = _mealSources[index];
return ListTile( return ListTile(
onTap: () { onTap: () {
Navigator.push( Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => builder: (context) => MealSourceDetailScreen(
MealSourceDetailScreen(
mealSource: mealSource, mealSource: mealSource,
), ),
), ),
).then( ).then((message) => refresh(message: message));
(message) => refresh(message: message));
}, },
title: Text(mealSource.value), title: Text(mealSource.value),
subtitle: Text(mealSource.notes ?? ''), subtitle: Text(mealSource.notes ?? ''),
@ -99,18 +100,15 @@ class _MealSourceListScreenState extends State<MealSourceListScreen> {
color: Colors.blue, color: Colors.blue,
), ),
onPressed: () async { onPressed: () async {
// add confirmation dialog handleDeleteAction(mealSource);
await mealSource.delete().then((_) {
refresh(
message: 'Meal Source deleted');
});
}, },
), ),
], ],
), ),
); );
})); }
}, ) : const Center(
child: Text('You have not created any Meal Sources yet!'),
), ),
), ),
], ],

View File

View File

View File

@ -1,6 +1,20 @@
# Generated by pub # Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile # See https://dart.dev/tools/pub/glossary#lockfile
packages: packages:
_fe_analyzer_shared:
dependency: transitive
description:
name: _fe_analyzer_shared
url: "https://pub.dartlang.org"
source: hosted
version: "30.0.0"
analyzer:
dependency: transitive
description:
name: analyzer
url: "https://pub.dartlang.org"
source: hosted
version: "2.7.0"
args: args:
dependency: transitive dependency: transitive
description: description:
@ -14,7 +28,7 @@ packages:
name: async name: async
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.8.1" version: "2.8.2"
boolean_selector: boolean_selector:
dependency: transitive dependency: transitive
description: description:
@ -22,13 +36,69 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.1.0" version: "2.1.0"
build:
dependency: transitive
description:
name: build
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.1"
build_config:
dependency: transitive
description:
name: build_config
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.0"
build_daemon:
dependency: transitive
description:
name: build_daemon
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.1"
build_resolvers:
dependency: transitive
description:
name: build_resolvers
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.4"
build_runner:
dependency: "direct dev"
description:
name: build_runner
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.4"
build_runner_core:
dependency: transitive
description:
name: build_runner_core
url: "https://pub.dartlang.org"
source: hosted
version: "7.2.2"
built_collection:
dependency: transitive
description:
name: built_collection
url: "https://pub.dartlang.org"
source: hosted
version: "5.1.1"
built_value:
dependency: transitive
description:
name: built_value
url: "https://pub.dartlang.org"
source: hosted
version: "8.1.3"
characters: characters:
dependency: transitive dependency: transitive
description: description:
name: characters name: characters
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.1.0" version: "1.2.0"
charcode: charcode:
dependency: transitive dependency: transitive
description: description:
@ -36,6 +106,20 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.3.1" version: "1.3.1"
checked_yaml:
dependency: transitive
description:
name: checked_yaml
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.1"
cli_util:
dependency: transitive
description:
name: cli_util
url: "https://pub.dartlang.org"
source: hosted
version: "0.3.5"
clock: clock:
dependency: transitive dependency: transitive
description: description:
@ -43,6 +127,13 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.1.0" version: "1.1.0"
code_builder:
dependency: transitive
description:
name: code_builder
url: "https://pub.dartlang.org"
source: hosted
version: "4.1.0"
collection: collection:
dependency: transitive dependency: transitive
description: description:
@ -92,6 +183,13 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.2.0" version: "1.2.0"
convert:
dependency: transitive
description:
name: convert
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.1"
crypto: crypto:
dependency: transitive dependency: transitive
description: description:
@ -106,6 +204,13 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.0.3" version: "1.0.3"
dart_style:
dependency: transitive
description:
name: dart_style
url: "https://pub.dartlang.org"
source: hosted
version: "2.2.0"
dbus: dbus:
dependency: transitive dependency: transitive
description: description:
@ -141,6 +246,13 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "6.1.2" version: "6.1.2"
fixnum:
dependency: transitive
description:
name: fixnum
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.0"
flex_color_scheme: flex_color_scheme:
dependency: "direct main" dependency: "direct main"
description: description:
@ -170,6 +282,27 @@ packages:
description: flutter description: flutter
source: sdk source: sdk
version: "0.0.0" version: "0.0.0"
frontend_server_client:
dependency: transitive
description:
name: frontend_server_client
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.2"
glob:
dependency: transitive
description:
name: glob
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.2"
graphs:
dependency: transitive
description:
name: graphs
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0"
http: http:
dependency: transitive dependency: transitive
description: description:
@ -177,6 +310,13 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.13.4" version: "0.13.4"
http_multi_server:
dependency: transitive
description:
name: http_multi_server
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.1"
http_parser: http_parser:
dependency: transitive dependency: transitive
description: description:
@ -198,6 +338,13 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.17.0" version: "0.17.0"
io:
dependency: transitive
description:
name: io
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.3"
js: js:
dependency: transitive dependency: transitive
description: description:
@ -205,6 +352,13 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.6.3" version: "0.6.3"
json_annotation:
dependency: transitive
description:
name: json_annotation
url: "https://pub.dartlang.org"
source: hosted
version: "4.3.0"
lints: lints:
dependency: transitive dependency: transitive
description: description:
@ -212,13 +366,20 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.0.1" version: "1.0.1"
logging:
dependency: transitive
description:
name: logging
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.2"
matcher: matcher:
dependency: transitive dependency: transitive
description: description:
name: matcher name: matcher
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.12.10" version: "0.12.11"
meta: meta:
dependency: transitive dependency: transitive
description: description:
@ -226,6 +387,13 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.7.0" version: "1.7.0"
mime:
dependency: transitive
description:
name: mime
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.1"
mime_type: mime_type:
dependency: transitive dependency: transitive
description: description:
@ -247,6 +415,34 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.3.0" version: "0.3.0"
objectbox:
dependency: "direct main"
description:
name: objectbox
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.0"
objectbox_flutter_libs:
dependency: "direct main"
description:
name: objectbox_flutter_libs
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.0"
objectbox_generator:
dependency: "direct dev"
description:
name: objectbox_generator
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.0"
package_config:
dependency: transitive
description:
name: package_config
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.2"
package_info_plus: package_info_plus:
dependency: transitive dependency: transitive
description: description:
@ -316,7 +512,7 @@ packages:
name: path_provider name: path_provider
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.0.5" version: "2.0.6"
path_provider_linux: path_provider_linux:
dependency: transitive dependency: transitive
description: description:
@ -373,6 +569,13 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.0.2" version: "2.0.2"
pool:
dependency: transitive
description:
name: pool
url: "https://pub.dartlang.org"
source: hosted
version: "1.5.0"
process: process:
dependency: transitive dependency: transitive
description: description:
@ -387,6 +590,20 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "6.0.1" version: "6.0.1"
pub_semver:
dependency: transitive
description:
name: pub_semver
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0"
pubspec_parse:
dependency: transitive
description:
name: pubspec_parse
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0"
sembast: sembast:
dependency: transitive dependency: transitive
description: description:
@ -443,11 +660,32 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.0.2" version: "2.0.2"
shelf:
dependency: transitive
description:
name: shelf
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.0"
shelf_web_socket:
dependency: transitive
description:
name: shelf_web_socket
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.1"
sky_engine: sky_engine:
dependency: transitive dependency: transitive
description: flutter description: flutter
source: sdk source: sdk
version: "0.0.99" version: "0.0.99"
source_gen:
dependency: transitive
description:
name: source_gen
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.1"
source_span: source_span:
dependency: transitive dependency: transitive
description: description:
@ -483,6 +721,13 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.1.0" version: "2.1.0"
stream_transform:
dependency: transitive
description:
name: stream_transform
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.0"
string_scanner: string_scanner:
dependency: transitive dependency: transitive
description: description:
@ -510,7 +755,14 @@ packages:
name: test_api name: test_api
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.4.2" version: "0.4.3"
timing:
dependency: transitive
description:
name: timing
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.0"
typed_data: typed_data:
dependency: transitive dependency: transitive
description: description:
@ -531,7 +783,14 @@ packages:
name: vector_math name: vector_math
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.1.0" version: "2.1.1"
watcher:
dependency: transitive
description:
name: watcher
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.1"
web_socket_channel: web_socket_channel:
dependency: transitive dependency: transitive
description: description:
@ -567,6 +826,13 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.1.0" version: "2.1.0"
yaml:
dependency: transitive
description:
name: yaml
url: "https://pub.dartlang.org"
source: hosted
version: "3.1.0"
sdks: sdks:
dart: ">=2.14.0 <3.0.0" dart: ">=2.14.0 <3.0.0"
flutter: ">=2.5.0" flutter: ">=2.5.0"

View File

@ -1,12 +1,12 @@
name: diameter name: diameter
description: A logging app for type 1 diabetics. description: A logging app for type 1 diabetics.
publish_to: 'none' # Remove this line if you wish to publish to pub.dev publish_to: "none" # Remove this line if you wish to publish to pub.dev
version: 1.0.0+1 version: 1.0.0+1
environment: environment:
sdk: '>=2.12.0 <3.0.0' sdk: ">=2.12.0 <3.0.0"
dependencies: dependencies:
parse_server_sdk_flutter: ^3.1.0 parse_server_sdk_flutter: ^3.1.0
@ -18,12 +18,16 @@ dependencies:
flex_color_scheme: ^3.0.1 flex_color_scheme: ^3.0.1
shared_preferences: ^2.0.8 shared_preferences: ^2.0.8
intl: ^0.17.0 intl: ^0.17.0
objectbox: ^1.2.0
objectbox_flutter_libs: any
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:
sdk: flutter sdk: flutter
flutter_lints: ^1.0.4 flutter_lints: ^1.0.4
provider: ^6.0.1 provider: ^6.0.1
build_runner: ^2.0.0
objectbox_generator: any
flutter: flutter:
uses-material-design: true uses-material-design: true