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.

3
TODO
View File

@ -3,4 +3,5 @@ Todo:
☐ add active/deleted flag to all data models
☐ account for deleted/disabled elements in dropdowns
☐ 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 {
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 {
google()
mavenCentral()
maven { url "https://jitpack.io" }
}
}

View File

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

View File

@ -1,4 +1,5 @@
import 'package:diameter/components/app_theme.dart';
import 'package:diameter/object_box.dart';
import 'package:diameter/screens/accuracy_detail.dart';
import 'package:diameter/screens/basal/basal_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/navigation.dart';
void main() async {
late ObjectBox objectBox;
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await Parse().initialize(keyApplicationId, keyParseServerUrl,
clientKey: keyClientKey, debug: true);
await Parse().initialize(
keyApplicationId,
keyParseServerUrl,
clientKey: keyClientKey,
debug: true,
coreStore: await CoreStoreSharedPrefsImp.getInstance(),
);
Settings.loadSettingsIntoConfig();
objectBox = await ObjectBox.create();
runApp(
MaterialApp(
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 {
late String? objectId;
late String value;
late bool forCarbsRatio = false;
late bool forPortionSize = false;
late int? confidenceRating;
late String? notes;
static final Box<Accuracy> box = objectBox.store.box<Accuracy>();
Accuracy(ParseObject? object) {
if (object != null) {
objectId = object.get<String>('objectId');
value = object.get<String>('value')!;
forCarbsRatio = object.get<bool>('forCarbsRatio')!;
forPortionSize = object.get<bool>('forPortionSize')!;
confidenceRating = object.get<num>('confidenceRating') != null
? object.get<num>('confidenceRating')!.toInt()
: null;
notes = object.get<String>('notes');
int id;
String value;
bool forCarbsRatio;
bool forPortionSize;
int? confidenceRating;
String? notes;
Accuracy({
this.id = 0,
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 {
QueryBuilder<ParseObject> query =
QueryBuilder<ParseObject>(ParseObject('Accuracy'));
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>> 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();
static List<Accuracy> getAllForCarbsRatio() {
QueryBuilder<Accuracy> allForCarbsRatio = box
.query(Accuracy_.forCarbsRatio.equals(true))
..order(Accuracy_.confidenceRating);
return allForCarbsRatio.build().find();
}
}

View File

@ -1,115 +1,37 @@
// import 'package:diameter/utils/date_time_utils.dart';
// import 'package:flutter/material.dart';
import 'package:parse_server_sdk_flutter/parse_server_sdk.dart';
import 'package:diameter/main.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 {
late String? objectId;
late DateTime startTime;
late DateTime endTime;
late double units;
late String basalProfile;
@Entity()
class Basal {
static final Box<Basal> box = objectBox.store.box<Basal>();
Basal(ParseObject? object) {
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')!;
}
}
int id;
static Future<Basal?> get(String objectId) async {
QueryBuilder<ParseObject> query =
QueryBuilder<ParseObject>(ParseObject('Basal'))
..whereEqualTo('objectId', objectId);
final ParseResponse apiResponse = await query.query();
@Property(type: PropertyType.date)
DateTime startTime;
if (apiResponse.success && apiResponse.results != null) {
return Basal(apiResponse.result.first);
}
}
@Property(type: PropertyType.date)
DateTime endTime;
static Future<List<Basal>> fetchAllForBasalProfile(
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();
double units;
if (apiResponse.success && apiResponse.results != null) {
return apiResponse.results!.map((e) => Basal(e as ParseObject)).toList();
} else {
return [];
}
}
final basalProfile = ToOne<BasalProfile>();
static Future<void> save({
required DateTime startTime,
required DateTime endTime,
required double units,
required String basalProfile,
}) 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();
}
Basal({
this.id = 0,
required this.startTime,
required this.endTime,
this.units = 0,
});
static Future<void> update(
String objectId, {
DateTime? startTime,
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();
}
static Basal? get(int id) => box.get(id);
static void put(Basal basal) => box.put(basal);
static void remove(int id) => box.remove(id);
Future<void> delete() async {
var basal = ParseObject('Basal')..objectId = objectId;
await basal.delete();
static List<Basal> getAllForProfile(int id) {
QueryBuilder<Basal> builder = box.query()..order(Basal_.startTime);
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/models/basal.dart';
import 'package:diameter/main.dart';
import 'package:diameter/objectbox.g.dart';
@Entity()
class BasalProfile {
late String? objectId;
late String name;
late bool active = false;
late Future<List<Basal>> basalRates;
late String? notes;
static final Box<BasalProfile> box = objectBox.store.box<BasalProfile>();
BasalProfile(ParseObject? object) {
if (object != null) {
objectId = object.get<String>('objectId');
name = object.get<String>('name')!;
active = object.get<bool>('active')!;
basalRates = Basal.fetchAllForBasalProfile(this);
notes = object.get<String>('notes');
}
int id;
String name;
bool active;
String? notes;
BasalProfile({
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 {
QueryBuilder<ParseObject> query =
QueryBuilder<ParseObject>(ParseObject('BasalProfile'));
final ParseResponse apiResponse = await query.query();
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();
static void setAllInactive() {
box.putMany(box.getAll().map((element) {
element.active = false;
return element;
}).toList());
}
}

View File

@ -1,201 +1,40 @@
// import 'package:diameter/config.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/main.dart';
import 'package:diameter/models/bolus_profile.dart';
import 'package:diameter/objectbox.g.dart';
class Bolus extends DataTableContent {
late String? objectId;
late DateTime startTime;
late DateTime endTime;
late double units;
late double carbs;
late int? mgPerDl;
late double? mmolPerL;
late String bolusProfile;
@Entity()
class Bolus {
static final Box<Bolus> box = objectBox.store.box<Bolus>();
Bolus(ParseObject? object) {
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;
carbs = object.get<num>('carbs')!.toDouble();
mgPerDl = object.get<num>('mgPerDl') != null
? object.get<num>('mgPerDl')!.toInt()
: null;
mmolPerL = object.get<num>('mmolPerL') != null
? object.get<num>('mmolPerL')! / 100
: null;
bolusProfile =
object.get<ParseObject>('bolusProfile')!.get<String>('objectId')!;
}
}
int id;
@Property(type: PropertyType.date)
DateTime startTime;
@Property(type: PropertyType.date)
DateTime endTime;
double units;
double carbs;
int? mgPerDl;
double? mmolPerL;
static Future<Bolus?> get(String objectId) async {
QueryBuilder<ParseObject> query =
QueryBuilder<ParseObject>(ParseObject('Bolus'))
..whereEqualTo('objectId', objectId);
final bolusProfile = ToOne<BolusProfile>();
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) {
return Bolus(apiResponse.result.first);
}
}
static Bolus? get(int id) => box.get(id);
static void put(Bolus bolus) => box.put(bolus);
static void remove(int id) => box.remove(id);
static Future<List<Bolus>> fetchAllForBolusProfile(
BolusProfile bolusProfile) async {
QueryBuilder<ParseObject> query =
QueryBuilder<ParseObject>(ParseObject('Bolus'))
..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 List<Bolus> getAllForProfile(int id) {
QueryBuilder<Bolus> builder = box.query()..order(Bolus_.startTime);
builder.link(Bolus_.bolusProfile, BolusProfile_.id.equals(id));
return builder.build().find();
}
}
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/models/bolus.dart';
import 'package:diameter/main.dart';
import 'package:diameter/objectbox.g.dart';
@Entity()
class BolusProfile {
late String? objectId;
late String name;
late bool active = false;
late Future<List<Bolus>> bolusRates;
late String? notes;
static final Box<BolusProfile> box = objectBox.store.box<BolusProfile>();
BolusProfile(ParseObject? object) {
if (object != null) {
objectId = object.get<String>('objectId');
name = object.get<String>('name')!;
active = object.get<bool>('active')!;
bolusRates = Bolus.fetchAllForBolusProfile(this);
notes = object.get<String>('notes');
}
int id;
String name;
bool active;
String? notes;
BolusProfile({
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 {
QueryBuilder<ParseObject> query =
QueryBuilder<ParseObject>(ParseObject('BolusProfile'));
final ParseResponse apiResponse = await query.query();
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();
static void setAllInactive() {
box.putMany(box.getAll().map((element) {
element.active = false;
return element;
}).toList());
}
}

View File

@ -1,50 +1,55 @@
import 'package:diameter/models/log_meal.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/main.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 {
late String? objectId;
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;
static final Box<LogEntry> box = objectBox.store.box<LogEntry>();
LogEntry(ParseObject object) {
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');
}
int id;
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>>{};
List<LogEntry> entries = await fetchAll();
QueryBuilder<LogEntry> allByDate = box.query()..order(LogEntry_.time, flags: Order.descending);
List<LogEntry> entries = allByDate.build().find();
DateTime? date;
for (LogEntry entry in entries) {
@ -54,133 +59,4 @@ class LogEntry {
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_event_type.dart';
import 'package:diameter/utils/date_time_utils.dart';
import 'package:flutter/material.dart';
import 'package:parse_server_sdk_flutter/parse_server_sdk.dart';
import 'package:diameter/objectbox.g.dart';
class LogEvent extends DataTableContent {
late String? objectId;
late String logEntry;
late String? endLogEntry;
late String eventType;
late DateTime time;
late DateTime? endTime;
late bool hasEndTime;
late String? notes;
@Entity()
class LogEvent {
static final Box<LogEvent> box = objectBox.store.box<LogEvent>();
LogEvent(ParseObject? object) {
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');
}
}
int id;
static Future<LogEvent?> get(String objectId) async {
QueryBuilder<ParseObject> query =
QueryBuilder<ParseObject>(ParseObject('LogEvent'))
..whereEqualTo('objectId', objectId);
final ParseResponse apiResponse = await query.query();
@Property(type: PropertyType.date)
DateTime time;
if (apiResponse.success && apiResponse.results != null) {
return LogEvent(apiResponse.result.first);
}
}
@Property(type: PropertyType.date)
DateTime? endTime;
static Future<List<LogEvent>> fetchAllActive() async {
QueryBuilder<ParseObject> query =
QueryBuilder<ParseObject>(ParseObject('LogEvent'))
..whereEqualTo('hasEndTime', true)
..whereEqualTo('endTime', null);
final ParseResponse apiResponse = await query.query();
bool hasEndTime;
String? notes;
if (apiResponse.success && apiResponse.results != null) {
return apiResponse.results!
.map((e) => LogEvent(e as ParseObject))
.toList();
} else {
return [];
}
}
final logEntry = ToOne<LogEntry>();
final endLogEntry = ToOne<LogEntry>();
final eventType = ToOne<LogEventType>();
static Future<List<LogEvent>> fetchAllForLogEntry(LogEntry logEntry) async {
QueryBuilder<ParseObject> query = QueryBuilder<ParseObject>(
ParseObject('LogEvent'))
..whereEqualTo('logEntry',
(ParseObject('LogEntry')..objectId = logEntry.objectId!).toPointer());
final ParseResponse apiResponse = await query.query();
LogEvent({
this.id = 0,
required this.time,
this.endTime,
this.hasEndTime = false,
this.notes,
});
if (apiResponse.success && apiResponse.results != null) {
return apiResponse.results!
.map((e) => LogEvent(e as ParseObject))
.toList();
} else {
return [];
}
}
static LogEvent? get(int id) => box.get(id);
static List<LogEvent> getAll() => box.getAll();
static void put(LogEvent logEvent) => box.put(logEvent);
static void remove(int id) => box.remove(id);
static Future<List<LogEvent>> fetchAllEndedByEntry(LogEntry logEntry) async {
QueryBuilder<ParseObject> query = QueryBuilder<ParseObject>(
ParseObject('LogEvent'))
..whereEqualTo('endLogEntry',
(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 List<LogEvent> getAllOngoing() {
QueryBuilder<LogEvent> query =
box.query(LogEvent_.hasEndTime.equals(true) & LogEvent_.endTime.isNull())..order(LogEvent_.time);
return query.build().find();
}
}
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 {
late String? objectId;
late String value;
late bool hasEndTime;
late int? defaultReminderDuration;
late String? notes;
static final Box<LogEventType> box = objectBox.store.box<LogEventType>();
LogEventType(ParseObject object) {
objectId = object.get<String>('objectId');
value = object.get<String>('value')!;
hasEndTime = object.get<bool>('hasEndTime')!;
defaultReminderDuration = object.get<num>('defaultReminderDuration') != null
? object.get<num>('defaultReminderDuration')!.toInt()
: null;
notes = object.get<String>('notes');
}
int id;
String value;
bool hasEndTime;
int? defaultReminderDuration;
String? notes;
static Future<List<LogEventType>> fetchAll() async {
QueryBuilder<ParseObject> query =
QueryBuilder<ParseObject>(ParseObject('LogEventType'));
final ParseResponse apiResponse = await query.query();
LogEventType({
this.id = 0,
this.value = '',
this.hasEndTime = false,
this.defaultReminderDuration,
this.notes,
});
if (apiResponse.success && apiResponse.results != null) {
return apiResponse.results!
.map((e) => LogEventType(e as ParseObject))
.toList();
} 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();
}
static LogEventType? get(int id) => box.get(id);
static List<LogEventType> getAll() => box.getAll();
static void put(LogEventType logEventType) => box.put(logEventType);
static void remove(int id) => box.remove(id);
}

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:flutter/material.dart';
import 'package:parse_server_sdk_flutter/parse_server_sdk.dart';
import 'package:diameter/models/meal.dart';
import 'package:diameter/models/meal_category.dart';
import 'package:diameter/models/meal_portion_type.dart';
import 'package:diameter/models/meal_source.dart';
import 'package:diameter/models/accuracy.dart';
import 'package:objectbox/objectbox.dart';
class LogMeal extends DataTableContent {
late String? objectId;
late String logEntry;
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;
@Entity()
class LogMeal {
static final Box<LogMeal> box = objectBox.store.box<LogMeal>();
LogMeal(ParseObject object) {
objectId = object.get<String>('objectId');
logEntry = object.get<ParseObject>('logEntry')!.get<String>('objectId')!;
meal = object.get<ParseObject>('meal')?.get<String>('objectId');
value = object.get<String>('value')!;
source = object.get<ParseObject>('source')?.get<String>('objectId');
category = object.get<ParseObject>('category')?.get<String>('objectId');
portionType = object.get<ParseObject>('portionType')?.get<String>('objectId');
carbsRatio = object.get<num>('carbsRatio')?.toDouble();
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');
}
int id;
String value;
double? carbsRatio;
double? portionSize;
double? carbsPerPortion;
double? bolus;
int? delayedBolusDuration;
double? delayedBolusRate;
String? notes;
static Future<LogMeal?> get(String objectId) async {
QueryBuilder<ParseObject> query =
QueryBuilder<ParseObject>(ParseObject('LogMeal'))
..whereEqualTo('objectId', objectId);
final ParseResponse apiResponse = await query.query();
final logEntry = ToOne<LogEntry>();
final meal = ToOne<Meal>();
final mealSource = ToOne<MealSource>();
final mealCategory = ToOne<MealCategory>();
final mealPortionType = ToOne<MealPortionType>();
final portionSizeAccuracy = ToOne<Accuracy>();
final carbsRatioAccuracy = ToOne<Accuracy>();
if (apiResponse.success && apiResponse.results != null) {
return LogMeal(apiResponse.result.first);
}
}
LogMeal({
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 {
QueryBuilder<ParseObject> query = QueryBuilder<ParseObject>(
ParseObject('LogMeal'))
..whereEqualTo('logEntry',
(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'))),
];
}
static LogMeal? get(int id) => box.get(id);
static List<LogMeal> getAll() => box.getAll();
static void put(LogMeal logMeal) => box.put(logMeal);
static void remove(int id) => box.remove(id);
}

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 }
@Entity()
class Meal {
late String? objectId;
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;
static final Box<Meal> box = objectBox.store.box<Meal>();
Meal(ParseObject object) {
objectId = object.get<String>('objectId');
value = object.get<String>('value')!;
source = object.get<ParseObject>('source') != null
? object.get<ParseObject>('source')!.get<String>('objectId')
: null;
category = object.get<ParseObject>('category') != null
? object.get<ParseObject>('category')!.get<String>('objectId')
: 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');
}
int id;
String value;
double? carbsRatio;
double? portionSize;
double? carbsPerPortion;
int? delayedBolusDuration;
double? delayedBolusRate;
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) {
return apiResponse.results!.map((e) => Meal(e as ParseObject)).toList();
} else {
return [];
}
}
final mealSource = ToOne<MealSource>();
final mealCategory = ToOne<MealCategory>();
final mealPortionType = ToOne<MealPortionType>();
final portionSizeAccuracy = ToOne<Accuracy>();
final carbsRatioAccuracy = ToOne<Accuracy>();
static Future<Meal?> get(String objectId) async {
QueryBuilder<ParseObject> query =
QueryBuilder<ParseObject>(ParseObject('Meal'))
..whereEqualTo('objectId', objectId);
final ParseResponse apiResponse = await query.query();
Meal({
this.id = 0,
this.value = '',
this.carbsRatio,
this.portionSize,
this.carbsPerPortion,
this.delayedBolusDuration,
this.delayedBolusRate,
this.notes,
});
if (apiResponse.success && apiResponse.results != null) {
return Meal(apiResponse.result.first);
}
}
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();
}
static Meal? get(int id) => box.get(id);
static List<Meal> getAll() => box.getAll();
static void put(Meal meal) => box.put(meal);
static void remove(int id) => box.remove(id);
}

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 {
late String? objectId;
late String value;
late String? notes;
static final Box<MealCategory> box = objectBox.store.box<MealCategory>();
MealCategory(ParseObject? object) {
if (object != null) {
objectId = object.get<String>('objectId');
value = object.get<String>('value')!;
notes = object.get<String>('notes');
}
}
int id;
String value;
String? notes;
static Future<List<MealCategory>> fetchAll() async {
QueryBuilder<ParseObject> query =
QueryBuilder<ParseObject>(ParseObject('MealCategory'));
final ParseResponse apiResponse = await query.query();
MealCategory({
this.id = 0,
this.value = '',
this.notes,
});
if (apiResponse.success && apiResponse.results != null) {
return apiResponse.results!.map((e) => MealCategory(e as ParseObject)).toList();
} else {
return [];
}
}
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();
}
static MealCategory? get(int id) => box.get(id);
static List<MealCategory> getAll() => box.getAll();
static void put(MealCategory mealCategory) => box.put(mealCategory);
static void remove(int id) => box.remove(id);
}

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 {
late String? objectId;
late String value;
late String? notes;
static final Box<MealPortionType> box = objectBox.store.box<MealPortionType>();
MealPortionType(ParseObject? object) {
if (object != null) {
objectId = object.get<String>('objectId');
value = object.get<String>('value')!;
notes = object.get<String>('notes');
}
}
int id;
String value;
String? notes;
static Future<List<MealPortionType>> fetchAll() async {
QueryBuilder<ParseObject> query =
QueryBuilder<ParseObject>(ParseObject('MealPortionType'));
final ParseResponse apiResponse = await query.query();
MealPortionType({
this.id = 0,
this.value = '',
this.notes,
});
if (apiResponse.success && apiResponse.results != null) {
// return apiResponse.results as List<ParseObject>;
return apiResponse.results!.map((e) => MealPortionType(e as ParseObject)).toList();
} else {
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();
}
static MealPortionType? get(int id) => box.get(id);
static List<MealPortionType> getAll() => box.getAll();
static void put(MealPortionType mealPortionType) => box.put(mealPortionType);
static void remove(int id) => box.remove(id);
}

View File

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

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

View File

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

View File

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

View File

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

View File

@ -2,7 +2,7 @@ import 'package:diameter/components/dialogs.dart';
import 'package:diameter/config.dart';
import 'package:diameter/navigation.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/screens/basal/basal_profile_detail.dart';
@ -15,18 +15,17 @@ class BasalProfileListScreen extends StatefulWidget {
}
class _BasalProfileListScreenState extends State<BasalProfileListScreen> {
late Future<List<BasalProfile>?> _basalProfiles;
late List<BasalProfile> _basalProfiles;
Widget banner = Container();
bool pickActiveProfileMode = false;
void refresh({String? message}) {
setState(() {
pickActiveProfileMode = false;
_basalProfiles = BasalProfile.fetchAll();
_basalProfiles = BasalProfile.getAll();
});
_basalProfiles.then((list) => updateBanner(
list?.where((element) => element.active).length ?? 0,
list?.isNotEmpty ?? false));
// _basalProfiles.then((list) =>
updateBanner();
setState(() {
if (message != null) {
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(() {
banner = activeProfileCount != 1
? MaterialBanner(
@ -51,7 +51,7 @@ class _BasalProfileListScreenState extends State<BasalProfileListScreen> {
forceActionsBelow: true,
actions: activeProfileCount == 0
? [
isNotEmpty
_basalProfiles.isNotEmpty
? TextButton(
child: const Text('ACTIVATE A PROFILE'),
onPressed: handlePickActiveProfileAction,
@ -74,9 +74,8 @@ class _BasalProfileListScreenState extends State<BasalProfileListScreen> {
}
void onDelete(BasalProfile basalProfile) {
basalProfile
.delete()
.then((_) => refresh(message: 'Basal Profile deleted'));
BasalProfile.remove(basalProfile.id);
refresh(message: 'Basal Profile deleted');
}
void handleDeleteAction(BasalProfile basalProfile) async {
@ -92,10 +91,11 @@ class _BasalProfileListScreenState extends State<BasalProfileListScreen> {
}
void onPickActive(BasalProfile basalProfile) {
BasalProfile.setAllInactive(exception: basalProfile.objectId!).then((_) =>
refresh(
message:
'${basalProfile.name} has been set as your active Profile'));
BasalProfile.setAllInactive;
basalProfile.active = true;
BasalProfile.put(basalProfile);
// (exception: basalProfile.objectId!).then((_) =>
refresh(message: '${basalProfile.name} has been set as your active Profile');
}
void handlePickActiveProfileAction() {
@ -120,7 +120,7 @@ class _BasalProfileListScreenState extends State<BasalProfileListScreen> {
context,
MaterialPageRoute(
builder: (context) => BasalProfileDetailScreen(
basalProfile: basalProfile, active: active),
id: basalProfile?.id ?? 0, active: active),
),
).then((message) => refresh(message: message));
}
@ -155,25 +155,11 @@ class _BasalProfileListScreenState extends State<BasalProfileListScreen> {
children: [
banner,
Expanded(
child: FutureBuilder<List<BasalProfile>?>(
future: _basalProfiles,
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,
child: _basalProfiles.isNotEmpty ? ListView.builder(
itemCount: _basalProfiles.length,
itemBuilder: (context, index) {
final basalProfile = snapshot.data![index];
final basalProfile = _basalProfiles[index];
return ListTile(
tileColor: basalProfile.active
? 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 {
static const String routeName = '/bolus';
final BolusProfile bolusProfile;
final Bolus? bolus;
final int bolusProfileId;
final int id;
final TimeOfDay? suggestedStartTime;
final TimeOfDay? suggestedEndTime;
const BolusDetailScreen(
{Key? key,
required this.bolusProfile,
this.bolus,
this.bolusProfileId = 0,
this.id = 0,
this.suggestedStartTime,
this.suggestedEndTime})
: super(key: key);
@ -31,6 +31,10 @@ class BolusDetailScreen extends StatefulWidget {
}
class _BolusDetailScreenState extends State<BolusDetailScreen> {
Bolus? _bolus;
bool _isNew = true;
bool _isSaving = false;
final GlobalKey<FormState> _bolusForm = GlobalKey<FormState>();
TimeOfDay _startTime = const TimeOfDay(hour: 0, minute: 0);
@ -43,30 +47,41 @@ class _BolusDetailScreenState extends State<BolusDetailScreen> {
final _mgPerDlController = TextEditingController(text: '');
final _mmolPerLController = TextEditingController(text: '');
bool _isSaving = false;
@override
void initState() {
super.initState();
reload();
if (widget.suggestedStartTime != null) {
_startTime = widget.suggestedStartTime!;
}
if (widget.suggestedEndTime != null) {
_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();
_carbsController.text = widget.bolus!.carbs.toString();
_mgPerDlController.text = widget.bolus!.mgPerDl.toString();
_mmolPerLController.text = widget.bolus!.mmolPerL.toString();
if (_bolus != null) {
_startTime = TimeOfDay.fromDateTime(_bolus!.startTime);
_endTime = TimeOfDay.fromDateTime(_bolus!.endTime);
_unitsController.text = _bolus!.units.toString();
_carbsController.text = _bolus!.carbs.toString();
_mgPerDlController.text = _bolus!.mgPerDl.toString();
_mmolPerLController.text = _bolus!.mmolPerL.toString();
}
updateStartTime();
updateEndTime();
}
void reload() {
if (widget.id != 0) {
setState(() {
_bolus = Bolus.get(widget.id);
});
}
_isNew = _bolus == null;
}
void updateStartTime() {
_startTimeController.text = DateTimeUtils.displayTimeOfDay(_startTime);
}
@ -77,14 +92,14 @@ class _BolusDetailScreenState extends State<BolusDetailScreen> {
Future<String?> validateTimePeriod() async {
String? error;
List<Bolus> bolusRates =
await Bolus.fetchAllForBolusProfile(widget.bolusProfile);
List<Bolus> bolusRates = Bolus.getAllForProfile(widget.bolusProfileId);
// BolusProfile.get(widget.bolusProfileId)?.bolusRates ?? [];
// TODO use a query for the following checks instead?
// check for duplicates
if (bolusRates
.where((other) =>
(widget.bolus == null ||
widget.bolus!.objectId != other.objectId) &&
widget.id != other.id &&
_startTime.hour == other.startTime.hour &&
_startTime.minute == other.startTime.minute)
.isNotEmpty) {
@ -93,8 +108,7 @@ class _BolusDetailScreenState extends State<BolusDetailScreen> {
if (bolusRates
.where((other) =>
(widget.bolus == null ||
widget.bolus!.objectId != other.objectId) &&
(widget.id != other.id) &&
DateTimeUtils.convertTimeOfDayToDateTime(_startTime)
.isBefore(other.startTime) &&
DateTimeUtils.convertTimeOfDayToDateTime(_endTime)
@ -128,44 +142,34 @@ class _BolusDetailScreenState extends State<BolusDetailScreen> {
setState(() {
_isSaving = true;
});
if (_bolusForm.currentState!.validate()) {
await validateTimePeriod().then((value) async {
if (value != 'CANCEL') {
bool isNew = widget.bolus == null;
isNew
? await Bolus.save(
startTime:
DateTimeUtils.convertTimeOfDayToDateTime(_startTime),
Bolus bolus = Bolus(
id: widget.id,
startTime: DateTimeUtils.convertTimeOfDayToDateTime(_startTime),
endTime: DateTimeUtils.convertTimeOfDayToDateTime(_endTime),
units: double.parse(_unitsController.text),
bolusProfile: widget.bolusProfile.objectId!,
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),
units: double.tryParse(_unitsController.text) ?? 0,
carbs: double.tryParse(_carbsController.text) ?? 0,
mgPerDl: int.tryParse(_mgPerDlController.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(() {
_isSaving = false;
});
}
void handleCancelAction() {
bool isNew = widget.bolus == null;
if (showConfirmationDialogOnCancel &&
((isNew &&
((_isNew &&
(_startTime.hour != (widget.suggestedStartTime?.hour ?? 0) ||
_endTime.hour != (widget.suggestedEndTime?.hour ?? 0) ||
_startTime.minute !=
@ -175,21 +179,20 @@ class _BolusDetailScreenState extends State<BolusDetailScreen> {
(double.tryParse(_carbsController.text) ?? 0) != 0.0 ||
(int.tryParse(_mgPerDlController.text) ?? 0) != 0 ||
(double.tryParse(_mmolPerLController.text) ?? 0) != 0.0)) ||
(!isNew &&
(TimeOfDay.fromDateTime(widget.bolus!.startTime) !=
_startTime ||
TimeOfDay.fromDateTime(widget.bolus!.endTime) != _endTime ||
(!_isNew &&
(TimeOfDay.fromDateTime(_bolus!.startTime) != _startTime ||
TimeOfDay.fromDateTime(_bolus!.endTime) != _endTime ||
(double.tryParse(_unitsController.text) ?? 0) !=
widget.bolus!.units ||
_bolus!.units ||
(double.tryParse(_carbsController.text) ?? 0) !=
widget.bolus!.carbs ||
_bolus!.carbs ||
(double.tryParse(_mgPerDlController.text) ?? 0) !=
widget.bolus!.mgPerDl ||
_bolus!.mgPerDl ||
(double.tryParse(_mmolPerLController.text) ?? 0) !=
widget.bolus!.mmolPerL)))) {
_bolus!.mmolPerL)))) {
Dialogs.showCancelConfirmationDialog(
context: context,
isNew: isNew,
isNew: _isNew,
onSave: handleSaveAction,
);
} else {
@ -226,12 +229,10 @@ class _BolusDetailScreenState extends State<BolusDetailScreen> {
@override
Widget build(BuildContext context) {
bool isNew = widget.bolus == null;
return Scaffold(
appBar: AppBar(
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),
body: SingleChildScrollView(

View File

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

View File

@ -2,7 +2,6 @@ import 'package:diameter/components/dialogs.dart';
import 'package:diameter/config.dart';
import 'package:diameter/navigation.dart';
import 'package:flutter/material.dart';
import 'package:diameter/components/progress_indicator.dart';
import 'package:diameter/models/bolus_profile.dart';
import 'package:diameter/screens/bolus/bolus_profile_detail.dart';
@ -15,18 +14,18 @@ class BolusProfileListScreen extends StatefulWidget {
}
class _BolusProfileListScreenState extends State<BolusProfileListScreen> {
late Future<List<BolusProfile>?> _bolusProfiles;
List<BolusProfile> _bolusProfiles = [];
Widget banner = Container();
bool pickActiveProfileMode = false;
void refresh({String? message}) {
void reload({String? message}) {
setState(() {
pickActiveProfileMode = false;
_bolusProfiles = BolusProfile.fetchAll();
_bolusProfiles = BolusProfile.getAll();
});
_bolusProfiles.then((list) => updateBanner(
list?.where((element) => element.active).length ?? 0,
list?.isNotEmpty ?? false));
updateBanner();
setState(() {
if (message != null) {
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(() {
banner = activeProfileCount != 1
? MaterialBanner(
@ -51,7 +52,7 @@ class _BolusProfileListScreenState extends State<BolusProfileListScreen> {
forceActionsBelow: true,
actions: activeProfileCount == 0
? [
isNotEmpty
_bolusProfiles.isNotEmpty
? TextButton(
child: const Text('ACTIVATE A PROFILE'),
onPressed: handlePickActiveProfileAction,
@ -74,9 +75,8 @@ class _BolusProfileListScreenState extends State<BolusProfileListScreen> {
}
void onDelete(BolusProfile bolusProfile) {
bolusProfile
.delete()
.then((_) => refresh(message: 'Bolus Profile deleted'));
BolusProfile.remove(bolusProfile.id);
reload(message: 'Bolus Profile deleted');
}
void handleDeleteAction(BolusProfile bolusProfile) async {
@ -92,10 +92,11 @@ class _BolusProfileListScreenState extends State<BolusProfileListScreen> {
}
void onPickActive(BolusProfile bolusProfile) {
BolusProfile.setAllInactive(exception: bolusProfile.objectId!).then((_) =>
refresh(
message:
'${bolusProfile.name} has been set as your active Profile'));
BolusProfile.setAllInactive;
bolusProfile.active = true;
BolusProfile.put(bolusProfile);
reload(
message: '${bolusProfile.name} has been set as your active Profile');
}
void handlePickActiveProfileAction() {
@ -120,9 +121,9 @@ class _BolusProfileListScreenState extends State<BolusProfileListScreen> {
context,
MaterialPageRoute(
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) {
@ -136,7 +137,7 @@ class _BolusProfileListScreenState extends State<BolusProfileListScreen> {
@override
void initState() {
super.initState();
refresh();
reload();
}
@override
@ -145,7 +146,7 @@ class _BolusProfileListScreenState extends State<BolusProfileListScreen> {
appBar: AppBar(
title: const Text('Bolus Profiles'),
actions: <Widget>[
IconButton(onPressed: refresh, icon: const Icon(Icons.refresh))
IconButton(onPressed: reload, icon: const Icon(Icons.refresh))
],
),
drawer:
@ -155,30 +156,16 @@ class _BolusProfileListScreenState extends State<BolusProfileListScreen> {
children: [
banner,
Expanded(
child: FutureBuilder<List<BolusProfile>?>(
future: _bolusProfiles,
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,
child: _bolusProfiles.isNotEmpty ? ListView.builder(
itemCount: _bolusProfiles.length,
itemBuilder: (context, index) {
final bolusProfile = snapshot.data![index];
final bolusProfile = _bolusProfiles[index];
return ListTile(
tileColor: bolusProfile.active
? Colors.green.shade100
: null,
onTap: () {
// TODO: make pick active profile visually distinct
pickActiveProfileMode
? onPickActive(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/models/log_entry.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/utils/date_time_utils.dart';
import 'package:flutter/material.dart';
import 'package:diameter/components/progress_indicator.dart';
// import 'package:diameter/components/progress_indicator.dart';
class ActiveLogEventListScreen extends StatefulWidget {
static const String routeName = '/active-log-events';
@ -23,16 +24,11 @@ class ActiveLogEventListScreen extends StatefulWidget {
}
class _ActiveLogEventListScreenState extends State<ActiveLogEventListScreen> {
late Future<List<LogEvent>> _activeLogEvents;
late Future<List<LogEventType>> _logEventTypes;
List<LogEvent> _activeLogEvents = [];
void refresh({String? message}) {
setState(() {
_logEventTypes = LogEventType.fetchAll();
});
setState(() {
_activeLogEvents = LogEvent.fetchAllForLogEntry(widget.endLogEntry!);
_activeLogEvents = LogEvent.getAllOngoing();
});
setState(() {
@ -49,12 +45,10 @@ class _ActiveLogEventListScreenState extends State<ActiveLogEventListScreen> {
}
void onStop(LogEvent event) async {
// TODO: create new entry if no existing entry is given
await LogEvent.update(
event.objectId!,
endTime: DateTime.now(),
endLogEntry: widget.endLogEntry!.objectId!,
);
event.endTime = DateTime.now();
event.endLogEntry.target =
widget.endLogEntry ?? LogEntry(time: DateTime.now());
LogEvent.put(event);
refresh();
if (widget.onSetEndTime != null) {
widget.onSetEndTime!();
@ -74,7 +68,8 @@ class _ActiveLogEventListScreenState extends State<ActiveLogEventListScreen> {
}
void onDelete(LogEvent event) {
event.delete().then((_) => refresh(message: 'Event deleted'));
LogEvent.remove(event.id);
refresh(message: 'Event deleted');
}
void handleDeleteAction(LogEvent event) async {
@ -97,10 +92,7 @@ class _ActiveLogEventListScreenState extends State<ActiveLogEventListScreen> {
@override
Widget build(BuildContext context) {
bool isNew = widget.endLogEntry == null;
return isNew
? Container()
: Container(
return SingleChildScrollView(
padding: const EdgeInsets.only(top: 10.0),
child: Column(
children: [
@ -123,61 +115,49 @@ class _ActiveLogEventListScreenState extends State<ActiveLogEventListScreen> {
).then((message) => refresh(message: message));
},
),
IconButton(
icon: const Icon(Icons.refresh), onPressed: refresh),
IconButton(icon: const Icon(Icons.refresh), onPressed: refresh),
],
),
FutureBuilder<List<LogEvent>>(
future: _activeLogEvents,
builder: (context, snapshot) {
return ViewWithProgressIndicator(
snapshot: snapshot,
child: snapshot.data == null || snapshot.data!.isEmpty
? Container()
: ListBody(
_activeLogEvents.isNotEmpty ?
ListView.builder(
shrinkWrap: true,
itemCount: _activeLogEvents.length,
itemBuilder: (context, index) {
final event = _activeLogEvents[index];
return ListTile(
title: Row(
mainAxisSize: MainAxisSize.max,
children: [
// TODO: fix problems that this futurebuilder in futurebuilder creates
FutureBuilder<List<LogEventType>>(
future: _logEventTypes,
builder: (context, types) {
// return DataTable(
// columnSpacing: 10.0,
// showCheckboxColumn: false,
// rows: snapshot.data != null
// ? snapshot.data!.map((event) {
// return DataRow(
// cells: event.asDataTableCells(
// [
// IconButton(
// icon: const Icon(
// Icons.stop),
// iconSize: 16.0,
// onPressed: () =>
// handleStopAction(
// event),
// ),
// IconButton(
// icon: const Icon(
// Icons.delete),
// iconSize: 16.0,
// onPressed: () =>
// handleDeleteAction(
// event),
// ),
// ],
// types: types.data,
// ),
// );
// }).toList()
// : [],
// columns: LogEvent.asDataTableColumns(),
// );
return Container();
})
Expanded(
child: Text(event.eventType.target?.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: () => handleStopAction(event),
),
IconButton(
icon: const Icon(
Icons.delete,
color: Colors.blue,
),
onPressed: () => handleDeleteAction(event),
),
],
),
);
},
) : 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/progress_indicator.dart';
import 'package:diameter/config.dart';
import 'package:diameter/models/log_entry.dart';
import 'package:diameter/navigation.dart';
@ -16,11 +15,11 @@ class LogScreen extends StatefulWidget {
}
class _LogScreenState extends State<LogScreen> {
late Future<Map<DateTime, List<LogEntry>>?> _logEntryDailyMap;
late Map<DateTime, List<LogEntry>> _logEntryDailyMap;
void refresh({String? message}) {
setState(() {
_logEntryDailyMap = LogEntry.createDailyEntryMap();
_logEntryDailyMap = LogEntry.getDailyEntryMap();
});
setState(() {
if (message != null) {
@ -36,7 +35,8 @@ class _LogScreenState extends State<LogScreen> {
}
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 {
@ -60,42 +60,29 @@ class _LogScreenState extends State<LogScreen> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Log Entries'), actions: <Widget>[
IconButton(onPressed: refresh, icon: const Icon(Icons.refresh))
]),
appBar: AppBar(
title: const Text('Log Entries'),
actions: <Widget>[
IconButton(
onPressed: refresh,
icon: const Icon(Icons.refresh)
),
],
),
drawer: const Navigation(currentLocation: LogScreen.routeName),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Expanded(
child: FutureBuilder<Map<DateTime, List<LogEntry>>?>(
future: _logEntryDailyMap,
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(
child: SingleChildScrollView(
child: _logEntryDailyMap.isNotEmpty ? ListView.builder(
shrinkWrap: true,
padding: const EdgeInsets.all(10.0),
itemCount: snapshot.data != null
? snapshot.data!.length
: 0,
itemCount: _logEntryDailyMap.length,
itemBuilder: (context, dateIndex) {
List<DateTime> dateList =
snapshot.data!.keys.toList();
dateList.sort((a, b) => a.compareTo(b) * -1);
List<DateTime> dateList = _logEntryDailyMap.keys.toList();
final date = dateList[dateIndex];
final entryList = snapshot.data![date];
final entryList = _logEntryDailyMap[date];
return ListBody(
children: [
Text(DateTimeUtils.displayDate(date)),
@ -112,7 +99,7 @@ class _LogScreenState extends State<LogScreen> {
MaterialPageRoute(
builder: (context) =>
LogEntryScreen(
entry: logEntry),
id: logEntry.id),
),
).then((message) => refresh(
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/config.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/screens/log/log_entry_form.dart';
import 'package:diameter/screens/log/log_event_detail.dart';
@ -14,14 +12,19 @@ import 'package:flutter/material.dart';
class LogEntryScreen extends StatefulWidget {
static const String routeName = '/log-entry';
final LogEntry? entry;
const LogEntryScreen({Key? key, this.entry}) : super(key: key);
final int id;
const LogEntryScreen({Key? key, this.id = 0}) : super(key: key);
@override
_LogEntryScreenState createState() => _LogEntryScreenState();
}
class _LogEntryScreenState extends State<LogEntryScreen> {
LogEntry? _logEntry;
bool _isNew = true;
bool _isSaving = false;
final GlobalKey<FormState> logEntryForm = GlobalKey<FormState>();
late FloatingActionButton addMealButton;
@ -34,8 +37,6 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
List<Widget> appBarActions = [];
DetailBottomRow? bottomNav;
bool _isSaving = false;
final formDataControllers = <String, TextEditingController>{
'time': TextEditingController(text: ''),
'mgPerDl': TextEditingController(text: ''),
@ -46,151 +47,42 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
'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
void initState() {
super.initState();
if (widget.entry != null) {
formDataControllers['time']!.text = widget.entry!.time.toString();
reload();
if (_logEntry != null) {
formDataControllers['time']!.text = _logEntry!.time.toString();
formDataControllers['mgPerDl']!.text =
(widget.entry!.mgPerDl ?? '').toString();
(_logEntry!.mgPerDl ?? '').toString();
formDataControllers['mmolPerL']!.text =
(widget.entry!.mmolPerL ?? '').toString();
(_logEntry!.mmolPerL ?? '').toString();
formDataControllers['bolusGlucose']!.text =
(widget.entry!.bolusGlucose ?? '').toString();
(_logEntry!.bolusGlucose ?? '').toString();
formDataControllers['delayedBolusRate']!.text =
(widget.entry!.delayedBolusRatio ?? '').toString();
(_logEntry!.delayedBolusRate ?? '').toString();
formDataControllers['delayedBolusDuration']!.text =
(widget.entry!.delayedBolusDuration ?? '').toString();
formDataControllers['notes']!.text = widget.entry!.notes ?? '';
(_logEntry!.delayedBolusDuration ?? '').toString();
formDataControllers['notes']!.text = _logEntry!.notes ?? '';
} else {
formDataControllers['time']!.text = DateTime.now().toString();
}
addMealButton = FloatingActionButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) {
return LogMealDetailScreen(logEntry: widget.entry!);
},
),
).then((message) => refreshLists(message: message));
},
onPressed: handleAddNewMeal,
child: const Icon(Icons.add),
);
addEventButton = FloatingActionButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) {
return LogEventDetailScreen(logEntry: widget.entry!);
},
),
).then((message) => refreshLists(message: message));
},
onPressed: handleAddNewEvent,
child: const Icon(Icons.add),
);
refreshButton = IconButton(
icon: const Icon(Icons.refresh),
onPressed: refreshLists,
onPressed: reload,
);
closeButton = IconButton(
@ -208,8 +100,118 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
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) {
if (widget.entry != null) {
if (_logEntry != null) {
setState(() {
switch (index) {
case 1:
@ -233,10 +235,8 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
@override
Widget build(BuildContext context) {
bool isNew = widget.entry == null;
return DefaultTabController(
length: isNew ? 1 : 3,
length: _isNew ? 1 : 3,
child: Builder(builder: (BuildContext context) {
final TabController tabController = DefaultTabController.of(context)!;
tabController.addListener(() {
@ -247,15 +247,15 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
formState: logEntryForm, controllers: formDataControllers),
];
if (!isNew) {
tabs.add(LogMealListScreen(logEntry: widget.entry));
tabs.add(LogEventListScreen(logEntry: widget.entry));
if (!_isNew) {
tabs.add(LogMealListScreen(logEntry: _logEntry!, reload: reload));
tabs.add(LogEventListScreen(logEntry: _logEntry!, reload: reload));
}
return Scaffold(
appBar: AppBar(
title: Text(isNew ? 'New Log Entry' : 'Edit Log Entry'),
bottom: isNew
title: Text(_isNew ? 'New Log Entry' : 'Edit Log Entry'),
bottom: _isNew
? PreferredSize(child: Container(), preferredSize: Size.zero)
: const TabBar(
tabs: [
@ -273,7 +273,7 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
bottomNavigationBar: bottomNav,
floatingActionButton: actionButton,
floatingActionButtonLocation:
FloatingActionButtonLocation.centerDocked,
FloatingActionButtonLocation.endFloat,
);
}),
);

View File

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

View File

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

View File

@ -2,35 +2,25 @@ import 'package:diameter/components/dialogs.dart';
import 'package:diameter/config.dart';
import 'package:diameter/models/log_entry.dart';
import 'package:diameter/models/log_event.dart';
import 'package:diameter/models/log_event_type.dart';
import 'package:diameter/screens/log/active_log_event_list.dart';
import 'package:diameter/screens/log/log_event_detail.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:diameter/components/progress_indicator.dart';
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
_LogEventListScreenState createState() => _LogEventListScreenState();
}
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(() {
if (message != null) {
var snackBar = SnackBar(
@ -42,7 +32,6 @@ class _LogEventListScreenState extends State<LogEventListScreen> {
..showSnackBar(snackBar);
}
});
_logEventTypes = LogEventType.fetchAll();
}
void handleEditAction(LogEvent event) {
@ -50,15 +39,16 @@ class _LogEventListScreenState extends State<LogEventListScreen> {
context,
MaterialPageRoute(
builder: (context) => LogEventDetailScreen(
endLogEntry: widget.logEntry!,
endLogEntry: widget.logEntry,
logEvent: event,
),
),
).then((message) => refresh(message: message));
).then((message) => reload(message: message));
}
void onDelete(LogEvent logEvent) {
logEvent.delete().then((_) => refresh(message: 'Event deleted'));
LogEvent.remove(logEvent.id);
reload(message: 'Event deleted');
}
void handleDeleteAction(LogEvent logEvent) async {
@ -73,40 +63,19 @@ class _LogEventListScreenState extends State<LogEventListScreen> {
}
}
@override
void initState() {
super.initState();
refresh();
}
@override
Widget build(BuildContext context) {
return SingleChildScrollView(
padding: const EdgeInsets.only(top: 10.0),
child: Column(
return Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
// TODO: add button for active events
FutureBuilder<List<LogEvent>>(
future: widget.logEntry!.events,
builder: (context, snapshot) {
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(
Expanded(
child: (widget.logEntry.events.isNotEmpty || widget.logEntry.endedEvents.isNotEmpty)
? ListView.builder(
shrinkWrap: true,
itemCount: snapshot.data != null
? snapshot.data!.length
: 0,
itemCount: widget.logEntry.events.length + widget.logEntry.endedEvents.length,
itemBuilder: (context, index) {
final event = snapshot.data![index];
final event = (widget.logEntry.events + widget.logEntry.endedEvents)[index];
return ListTile(
onTap: () {
handleEditAction(event);
@ -115,12 +84,7 @@ class _LogEventListScreenState extends State<LogEventListScreen> {
mainAxisSize: MainAxisSize.max,
children: [
Expanded(
child: Text(types.data
?.firstWhere((element) =>
element.objectId ==
event.eventType)
.value ??
'')),
child: Text(event.eventType.target?.value ?? '')),
],
),
subtitle: Text(
@ -133,75 +97,17 @@ class _LogEventListScreenState extends State<LogEventListScreen> {
Icons.delete,
color: Colors.blue,
),
onPressed: () =>
handleDeleteAction(event),
onPressed: () => 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()) {
bool isNew = widget.logEventType == null;
isNew
? await LogEventType.save(
// isNew
// ? 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,
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,
);
));
Navigator.pop(context, '${isNew ? 'New' : ''} Log Event Type Saved');
}
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/navigation.dart';
import 'package:diameter/screens/log/log_event_type_detail.dart';
@ -13,11 +13,11 @@ class LogEventTypeListScreen extends StatefulWidget {
}
class _LogEventTypeListScreenState extends State<LogEventTypeListScreen> {
late Future<List<LogEventType>?> _logEventTypes;
List<LogEventType> _logEventTypes = [];
void refresh({String? message}) {
setState(() {
_logEventTypes = LogEventType.fetchAll();
_logEventTypes = LogEventType.getAll();
});
setState(() {
if (message != null) {
@ -50,27 +50,12 @@ class _LogEventTypeListScreenState extends State<LogEventTypeListScreen> {
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Expanded(
child: FutureBuilder<List<LogEventType>?>(
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(
child: _logEventTypes.isNotEmpty ? ListView.builder(
padding: const EdgeInsets.all(10.0),
itemCount:
snapshot.data != null ? snapshot.data!.length : 0,
itemCount: _logEventTypes.length,
itemBuilder: (context, index) {
final logEventType = snapshot.data![index];
// final logEventType = snapshot.data![index];
final logEventType = _logEventTypes[index];
return ListTile(
onTap: () {
Navigator.push(
@ -89,10 +74,11 @@ class _LogEventTypeListScreenState extends State<LogEventTypeListScreen> {
children: [
IconButton(
onPressed: () async {
await logEventType.delete().then((_) {
LogEventType.remove(logEventType.id);
// await logEventType.delete().then((_) {
refresh(
message: 'Log Event Type deleted');
});
// });
},
icon: const Icon(Icons.delete,
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> {
final GlobalKey<FormState> _logMealForm = GlobalKey<FormState>();
final _valueController = TextEditingController(text: '');
final _carbsRatioController = TextEditingController(text: '');
final _portionSizeController = TextEditingController(text: '');
@ -35,19 +36,19 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
final _delayedBolusRateController = TextEditingController(text: '');
final _delayedBolusDurationController = TextEditingController(text: '');
final _notesController = TextEditingController(text: '');
String? _meal;
String? _source;
String? _category;
String? _portionType;
String? _portionSizeAccuracy;
String? _carbsRatioAccuracy;
Meal? _meal;
MealSource? _mealSource;
MealCategory? _mealCategory;
MealPortionType? _mealPortionType;
Accuracy? _portionSizeAccuracy;
Accuracy? _carbsRatioAccuracy;
late Future<List<Meal>> _meals;
late Future<List<MealCategory>> _mealCategories;
late Future<List<MealPortionType>> _mealPortionTypes;
late Future<List<MealSource>> _mealSources;
late Future<List<Accuracy>> _portionSizeAccuracies;
late Future<List<Accuracy>> _carbsRatioAccuracies;
List<Meal> _meals = [];
List<MealCategory> _mealCategories = [];
List<MealPortionType> _mealPortionTypes = [];
List<MealSource> _mealSources = [];
List<Accuracy> _portionSizeAccuracies = [];
List<Accuracy> _carbsRatioAccuracies = [];
bool _isSaving = false;
@ -55,6 +56,13 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
void 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) {
_valueController.text = widget.logMeal!.value;
_carbsRatioController.text =
@ -70,28 +78,23 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
(widget.logMeal!.delayedBolusDuration ?? '').toString();
_notesController.text = widget.logMeal!.notes ?? '';
_meal = widget.logMeal!.meal;
_source = widget.logMeal!.source;
_category = widget.logMeal!.category;
_portionType = widget.logMeal!.portionType;
_portionSizeAccuracy = widget.logMeal!.portionSizeAccuracy;
_carbsRatioAccuracy = widget.logMeal!.carbsRatioAccuracy;
// _meal = widget.logMeal!.meal;
// _source = widget.logMeal!.source;
// _category = widget.logMeal!.category;
// _portionType = widget.logMeal!.portionType;
// _portionSizeAccuracy = _portionSizeAccuracies.firstWhere((element) =>
// 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();
_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) {
Future<void> onSelectMeal(Meal meal) async {
setState(() {
_meal = objectId;
_meal = meal;
_valueController.text = meal.value;
if (meal.carbsRatio != null) {
_carbsRatioController.text = meal.carbsRatio.toString();
@ -109,25 +112,23 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
_delayedBolusDurationController.text =
meal.delayedBolusDuration.toString();
}
if (meal.source != null) {
_source = meal.source;
if (meal.mealSource.hasValue) {
_mealSource = meal.mealSource.target;
}
if (meal.category != null) {
_category = meal.category;
if (meal.mealCategory.hasValue) {
_mealCategory = meal.mealCategory.target;
}
if (meal.portionType != null) {
_portionType = meal.portionType;
if (meal.mealPortionType.hasValue) {
_mealPortionType = meal.mealPortionType.target;
}
if (meal.portionSizeAccuracy != null) {
_portionSizeAccuracy = meal.portionSizeAccuracy;
if (meal.portionSizeAccuracy.hasValue) {
_portionSizeAccuracy = meal.portionSizeAccuracy.target;
}
if (meal.carbsRatioAccuracy != null) {
_carbsRatioAccuracy = meal.carbsRatioAccuracy;
if (meal.carbsRatioAccuracy.hasValue) {
_carbsRatioAccuracy = meal.carbsRatioAccuracy.target;
}
});
}
}
}
void handleSaveAction() async {
setState(() {
@ -135,45 +136,69 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
});
if (_logMealForm.currentState!.validate()) {
bool isNew = widget.logMeal == null;
isNew
? await LogMeal.save(
logEntry: widget.logEntry.objectId!,
meal: _meal,
// isNew
// ? await LogMeal.save(
// logEntry: widget.logEntry.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,
// )
// : 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,
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,
)
: 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),
delayedBolusRate: double.tryParse(_delayedBolusRateController.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');
}
setState(() {
@ -187,9 +212,9 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
((isNew &&
(_valueController.text != '' ||
_meal != null ||
_source != null ||
_category != null ||
_portionType != null ||
_mealSource != null ||
_mealCategory != null ||
_mealPortionType != null ||
double.tryParse(_carbsRatioController.text) != null ||
double.tryParse(_portionSizeController.text) != null ||
double.tryParse(_carbsPerPortionController.text) != null ||
@ -202,20 +227,25 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
_notesController.text != '')) ||
(!isNew &&
(_valueController.text != widget.logMeal!.value ||
_meal != widget.logMeal!.meal ||
_source != widget.logMeal!.source ||
_category != widget.logMeal!.category ||
_portionType != widget.logMeal!.portionType ||
_meal != widget.logMeal!.meal.target ||
_mealSource != widget.logMeal!.mealSource.target ||
_mealCategory != widget.logMeal!.mealCategory.target ||
_mealPortionType != widget.logMeal!.mealPortionType.target ||
double.tryParse(_carbsRatioController.text) !=
widget.logMeal!.carbsRatio ||
double.tryParse(_portionSizeController.text) !=
widget.logMeal!.portionSize ||
double.tryParse(_carbsPerPortionController.text) !=
widget.logMeal!.carbsPerPortion ||
_carbsRatioAccuracy != widget.logMeal!.carbsRatioAccuracy ||
// _carbsRatioAccuracy != widget.logMeal!.carbsRatioAccuracy ||
// _portionSizeAccuracy !=
// widget.logMeal!.portionSizeAccuracy ||
_carbsRatioAccuracy !=
widget.logMeal!.carbsRatioAccuracy.target ||
_portionSizeAccuracy !=
widget.logMeal!.portionSizeAccuracy ||
double.tryParse(_bolusController.text) != widget.logMeal!.bolus ||
widget.logMeal!.portionSizeAccuracy.target ||
double.tryParse(_bolusController.text) !=
widget.logMeal!.bolus ||
int.tryParse(_delayedBolusDurationController.text) !=
widget.logMeal!.delayedBolusDuration ||
double.tryParse(_delayedBolusRateController.text) !=
@ -300,49 +330,51 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
return null;
},
),
StyledFutureDropdownButton<Meal>(
StyledDropdownButton<Meal>(
selectedItem: _meal,
label: 'Meal',
items: _meals,
getItemValue: (item) => item.objectId,
// getItemValue: (item) => item.objectId,
renderItem: (item) => Text(item.value),
onChanged: (value) {
if (value != null) {
onSelectMeal(value);
}
},
),
StyledFutureDropdownButton<MealSource>(
selectedItem: _source,
StyledDropdownButton<MealSource>(
selectedItem: _mealSource,
label: 'Meal Source',
items: _mealSources,
getItemValue: (item) => item.objectId,
// getItemValue: (item) => item.objectId,
renderItem: (item) => Text(item.value),
onChanged: (value) {
setState(() {
_source = value;
_mealSource = value;
});
},
),
StyledFutureDropdownButton<MealCategory>(
selectedItem: _category,
StyledDropdownButton<MealCategory>(
selectedItem: _mealCategory,
label: 'Meal Category',
items: _mealCategories,
getItemValue: (item) => item.objectId,
// getItemValue: (item) => item.objectId,
renderItem: (item) => Text(item.value),
onChanged: (value) {
setState(() {
_category = value;
_mealCategory = value;
});
},
),
StyledFutureDropdownButton<MealPortionType>(
selectedItem: _portionType,
StyledDropdownButton<MealPortionType>(
selectedItem: _mealPortionType,
label: 'Meal Portion Type',
items: _mealPortionTypes,
getItemValue: (item) => item.objectId,
// getItemValue: (item) => item.objectId,
renderItem: (item) => Text(item.value),
onChanged: (value) {
setState(() {
_portionType = value;
_mealPortionType = value;
});
},
),
@ -402,11 +434,11 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
),
],
),
StyledFutureDropdownButton<Accuracy>(
StyledDropdownButton<Accuracy>(
selectedItem: _portionSizeAccuracy,
label: 'Portion Size Accuracy',
items: _portionSizeAccuracies,
getItemValue: (item) => item.objectId,
// getItemValue: (item) => item.objectId,
renderItem: (item) => Text(item.value),
onChanged: (value) {
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(
children: [
Expanded(
@ -444,11 +488,11 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
),
],
),
StyledFutureDropdownButton<Accuracy>(
StyledDropdownButton<Accuracy>(
selectedItem: _carbsRatioAccuracy,
label: 'Carbs Ratio Accuracy',
items: _carbsRatioAccuracies,
getItemValue: (item) => item.objectId,
// getItemValue: (item) => item.objectId,
renderItem: (item) => Text(item.value),
onChanged: (value) {
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(
decoration: const InputDecoration(
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/screens/log/log_meal_detail.dart';
import 'package:flutter/material.dart';
import 'package:diameter/components/progress_indicator.dart';
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
_LogMealListScreenState createState() => _LogMealListScreenState();
}
class _LogMealListScreenState extends State<LogMealListScreen> {
void refresh({String? message}) {
if (widget.logEntry != null) {
setState(() {
widget.logEntry!.meals = LogMeal.fetchAllForLogEntry(widget.logEntry!);
});
}
void reload({String? message}) {
widget.reload();
setState(() {
if (message != null) {
var snackBar = SnackBar(
@ -40,15 +38,16 @@ class _LogMealListScreenState extends State<LogMealListScreen> {
context,
MaterialPageRoute(
builder: (context) => LogMealDetailScreen(
logEntry: widget.logEntry!,
logEntry: widget.logEntry,
logMeal: meal,
),
),
).then((message) => refresh(message: message));
).then((message) => reload(message: message));
}
void onDelete(LogMeal meal) {
meal.delete().then((_) => refresh(message: 'Meal deleted'));
void onDelete(LogMeal logMeal) {
LogMeal.remove(logMeal.id);
reload(message: 'Meal deleted');
}
void handleDeleteAction(LogMeal meal) async {
@ -63,43 +62,29 @@ class _LogMealListScreenState extends State<LogMealListScreen> {
}
}
@override
void initState() {
super.initState();
refresh();
}
@override
Widget build(BuildContext context) {
return SingleChildScrollView(
padding: const EdgeInsets.only(top: 10.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
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(
return Column(
children: <Widget>[
Expanded(
child: widget.logEntry.meals.isNotEmpty ? ListView.builder(
shrinkWrap: true,
itemCount: snapshot.data != null ? snapshot.data!.length : 0,
itemCount: widget.logEntry.meals.length,
itemBuilder: (context, index) {
final meal = snapshot.data![index];
final meal = widget.logEntry.meals[index];
return ListTile(
onTap: () => handleEditAction(meal),
title: Row(
children: [
Expanded(child: Text(meal.value)),
Expanded(
child: Text(meal.value)),
child: Text(meal.carbsPerPortion != null
? '${meal.carbsPerPortion} g carbs'
: '')),
Expanded(
child: Text(meal.carbsPerPortion != null ? '${meal.carbsPerPortion} g carbs' : '')),
Expanded(child: Text(meal.bolus != null ? '${meal.bolus} U' : ''))
child: Text(meal.bolus != null
? '${meal.bolus} U'
: ''))
],
),
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 {
if (_mealCategoryForm.currentState!.validate()) {
bool isNew = widget.mealCategory == null;
isNew
? await MealCategory.save(
value: _valueController.text, notes: _notesController.text)
: await MealCategory.update(widget.mealCategory!.objectId!,
value: _valueController.text, notes: _notesController.text);
// isNew
// ? await MealCategory.save(
// value: _valueController.text, notes: _notesController.text)
// : await MealCategory.update(widget.mealCategory!.objectId!,
// 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');
}
}

View File

@ -1,5 +1,5 @@
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/navigation.dart';
import 'package:diameter/screens/meal/meal_category_detail.dart';
@ -16,11 +16,11 @@ class MealCategoryListScreen extends StatefulWidget {
}
class _MealCategoryListScreenState extends State<MealCategoryListScreen> {
late Future<List<MealCategory>?> _mealCategories;
List<MealCategory> _mealCategories = [];
void refresh({String? message}) {
setState(() {
_mealCategories = MealCategory.fetchAll();
_mealCategories = MealCategory.getAll();
});
setState(() {
if (message != null) {
@ -36,9 +36,8 @@ class _MealCategoryListScreenState extends State<MealCategoryListScreen> {
}
void onDelete(MealCategory mealCategory) {
mealCategory
.delete()
.then((_) => refresh(message: 'Meal Category deleted'));
MealCategory.remove(mealCategory.id);
refresh(message: 'Meal Category deleted');
}
void handleDeleteAction(MealCategory mealCategory) async {
@ -77,27 +76,12 @@ class _MealCategoryListScreenState extends State<MealCategoryListScreen> {
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Expanded(
child: FutureBuilder<List<MealCategory>?>(
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(
child: _mealCategories.isNotEmpty ? ListView.builder(
padding: const EdgeInsets.only(top: 10.0),
itemCount:
snapshot.data != null ? snapshot.data!.length : 0,
itemCount: _mealCategories.length,
itemBuilder: (context, index) {
final mealCategory = snapshot.data![index];
final mealCategory = _mealCategories[index];
return ListTile(
onTap: () {
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_source.dart';
import 'package:diameter/navigation.dart';
// import 'package:diameter/objectbox.g.dart';
import 'package:diameter/settings.dart';
import 'package:diameter/utils/utils.dart';
import 'package:flutter/material.dart';
@ -24,6 +25,7 @@ class MealDetailScreen extends StatefulWidget {
class _MealDetailScreenState extends State<MealDetailScreen> {
final GlobalKey<FormState> _mealForm = GlobalKey<FormState>();
final _valueController = TextEditingController(text: '');
final _carbsRatioController = TextEditingController(text: '');
final _portionSizeController = TextEditingController(text: '');
@ -31,17 +33,18 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
final _delayedBolusRateController = TextEditingController(text: '');
final _delayedBolusDurationController = TextEditingController(text: '');
final _notesController = TextEditingController(text: '');
String? _source;
String? _category;
String? _portionType;
String? _portionSizeAccuracy;
String? _carbsRatioAccuracy;
late Future<List<MealCategory>> _mealCategories;
late Future<List<MealPortionType>> _mealPortionTypes;
late Future<List<MealSource>> _mealSources;
late Future<List<Accuracy>> _portionSizeAccuracies;
late Future<List<Accuracy>> _carbsRatioAccuracies;
MealSource? _mealSource;
MealCategory? _mealCategory;
MealPortionType? _mealPortionType;
Accuracy? _portionSizeAccuracy;
Accuracy? _carbsRatioAccuracy;
List<MealCategory> _mealCategories = [];
List<MealPortionType> _mealPortionTypes = [];
List<MealSource> _mealSources = [];
List<Accuracy> _portionSizeAccuracies = [];
List<Accuracy> _carbsRatioAccuracies = [];
bool isSaving = false;
@ -49,6 +52,12 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
void initState() {
super.initState();
_portionSizeAccuracies = Accuracy.getAllForPortionSize();
_carbsRatioAccuracies = Accuracy.getAllForCarbsRatio();
_mealCategories = MealCategory.getAll();
_mealPortionTypes = MealPortionType.getAll();
_mealSources = MealSource.getAll();
if (widget.meal != null) {
_valueController.text = widget.meal!.value;
_carbsRatioController.text = (widget.meal!.carbsRatio ?? '').toString();
@ -61,18 +70,12 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
(widget.meal!.delayedBolusDuration ?? '').toString();
_notesController.text = widget.meal!.notes ?? '';
_source = widget.meal!.source;
_category = widget.meal!.category;
_portionType = widget.meal!.portionType;
_portionSizeAccuracy = widget.meal!.portionSizeAccuracy;
_carbsRatioAccuracy = widget.meal!.carbsRatioAccuracy;
_mealSource = widget.meal!.mealSource.target;
_mealCategory = widget.meal!.mealCategory.target;
_mealPortionType = widget.meal!.mealPortionType.target;
_portionSizeAccuracy = widget.meal!.portionSizeAccuracy.target;
_carbsRatioAccuracy = widget.meal!.carbsRatioAccuracy.target;
}
_mealCategories = MealCategory.fetchAll();
_mealPortionTypes = MealPortionType.fetchAll();
_mealSources = MealSource.fetchAll();
_portionSizeAccuracies = Accuracy.fetchAllForPortionSize();
_carbsRatioAccuracies = Accuracy.fetchAllForCarbsRatio();
}
void handleSaveAction() async {
@ -81,40 +84,62 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
});
if (_mealForm.currentState!.validate()) {
bool isNew = widget.meal == null;
isNew
? await Meal.save(
// isNew
// ? 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,
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,
)
: 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),
delayedBolusRate: double.tryParse(_delayedBolusRateController.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');
}
setState(() {
@ -127,9 +152,9 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
if (showConfirmationDialogOnCancel &&
((isNew &&
(_valueController.text != '' ||
_source != null ||
_category != null ||
_portionType != null ||
_mealSource != null ||
_mealCategory != null ||
_mealPortionType != null ||
double.tryParse(_carbsRatioController.text) != null ||
double.tryParse(_portionSizeController.text) != null ||
double.tryParse(_carbsPerPortionController.text) != null ||
@ -141,17 +166,17 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
_notesController.text != '')) ||
(!isNew &&
(_valueController.text != widget.meal!.value ||
_source != widget.meal!.source ||
_category != widget.meal!.category ||
_portionType != widget.meal!.portionType ||
_mealSource != widget.meal!.mealSource.target ||
_mealCategory != widget.meal!.mealCategory.target ||
_mealPortionType != widget.meal!.mealPortionType.target ||
double.tryParse(_carbsRatioController.text) !=
widget.meal!.carbsRatio ||
double.tryParse(_portionSizeController.text) !=
widget.meal!.portionSize ||
double.tryParse(_carbsPerPortionController.text) !=
widget.meal!.carbsPerPortion ||
_carbsRatioAccuracy != widget.meal!.carbsRatioAccuracy ||
_portionSizeAccuracy != widget.meal!.portionSizeAccuracy ||
_carbsRatioAccuracy != widget.meal!.carbsRatioAccuracy.target ||
_portionSizeAccuracy != widget.meal!.portionSizeAccuracy.target ||
int.tryParse(_delayedBolusDurationController.text) !=
widget.meal!.delayedBolusDuration ||
double.tryParse(_delayedBolusRateController.text) !=
@ -167,30 +192,25 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
}
}
Future<void> onSelectMealSource(String? objectId) async {
if (objectId != null) {
MealSource? mealSource = await MealSource.get(objectId);
if (mealSource != null) {
Future<void> onSelectMealSource(MealSource mealSource) async {
setState(() {
_source = objectId;
if (mealSource.defaultCarbsRatioAccuracy != null) {
_mealSource = mealSource;
if (mealSource.defaultCarbsRatioAccuracy.hasValue) {
_carbsRatioAccuracy =
mealSource.defaultCarbsRatioAccuracy.toString();
mealSource.defaultCarbsRatioAccuracy.target;
}
if (mealSource.defaultPortionSizeAccuracy != null) {
if (mealSource.defaultPortionSizeAccuracy.hasValue) {
_portionSizeAccuracy =
mealSource.defaultPortionSizeAccuracy.toString();
mealSource.defaultPortionSizeAccuracy.target;
}
if (mealSource.defaultMealCategory != null) {
_category = mealSource.defaultMealCategory.toString();
if (mealSource.defaultMealCategory.hasValue) {
_mealCategory = mealSource.defaultMealCategory.target;
}
if (mealSource.defaultMealPortionType != null) {
_portionType = mealSource.defaultMealPortionType.toString();
if (mealSource.defaultMealPortionType.hasValue) {
_mealPortionType = mealSource.defaultMealPortionType.target;
}
});
}
}
}
void calculateThirdMeasurementOfPortionCarbsRelation(
{PortionCarbsParameter? parameterToBeCalculated}) {
@ -261,37 +281,39 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
return null;
},
),
StyledFutureDropdownButton<MealSource>(
selectedItem: _source,
StyledDropdownButton<MealSource>(
selectedItem: _mealSource,
label: 'Meal Source',
items: _mealSources,
getItemValue: (item) => item.objectId,
// getItemValue: (item) => item.objectId,
renderItem: (item) => Text(item.value),
onChanged: (value) {
if (value != null) {
onSelectMealSource(value);
}
},
),
StyledFutureDropdownButton<MealCategory>(
selectedItem: _category,
StyledDropdownButton<MealCategory>(
selectedItem: _mealCategory,
label: 'Meal Category',
items: _mealCategories,
getItemValue: (item) => item.objectId,
// getItemValue: (item) => item.objectId,
renderItem: (item) => Text(item.value),
onChanged: (value) {
setState(() {
_category = value;
_mealCategory = value;
});
},
),
StyledFutureDropdownButton<MealPortionType>(
selectedItem: _portionType,
StyledDropdownButton<MealPortionType>(
selectedItem: _mealPortionType,
label: 'Meal Portion Type',
items: _mealPortionTypes,
getItemValue: (item) => item.objectId,
// getItemValue: (item) => item.objectId,
renderItem: (item) => Text(item.value),
onChanged: (value) {
setState(() {
_portionType = value;
_mealPortionType = value;
});
},
),
@ -351,11 +373,11 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
),
],
),
StyledFutureDropdownButton<Accuracy>(
StyledDropdownButton<Accuracy>(
selectedItem: _portionSizeAccuracy,
label: 'Portion Size Accuracy',
items: _portionSizeAccuracies,
getItemValue: (item) => item.objectId,
// getItemValue: (item) => item.objectId,
renderItem: (item) => Text(item.value),
onChanged: (value) {
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(
children: [
Expanded(
@ -393,11 +427,11 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
),
],
),
StyledFutureDropdownButton<Accuracy>(
StyledDropdownButton<Accuracy>(
selectedItem: _carbsRatioAccuracy,
label: 'Carbs Ratio Accuracy',
items: _carbsRatioAccuracies,
getItemValue: (item) => item.objectId,
// getItemValue: (item) => item.objectId,
renderItem: (item) => Text(item.value),
onChanged: (value) {
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
TextFormField(
decoration: const InputDecoration(

View File

@ -1,5 +1,5 @@
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/models/meal.dart';
import 'package:diameter/navigation.dart';
@ -16,11 +16,11 @@ class MealListScreen extends StatefulWidget {
}
class _MealListScreenState extends State<MealListScreen> {
late Future<List<Meal>?> _meals;
List<Meal> _meals = [];
void refresh({String? message}) {
setState(() {
_meals = Meal.fetchAll();
_meals = Meal.getAll();
});
setState(() {
if (message != null) {
@ -36,7 +36,8 @@ class _MealListScreenState extends State<MealListScreen> {
}
void onDelete(Meal meal) {
meal.delete().then((_) => refresh(message: 'Meal deleted'));
Meal.remove(meal.id);
refresh(message: 'Meal deleted');
}
void handleDeleteAction(Meal meal) async {
@ -68,27 +69,12 @@ class _MealListScreenState extends State<MealListScreen> {
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Expanded(
child: FutureBuilder<List<Meal>?>(
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(
child: _meals.isNotEmpty ? ListView.builder(
padding: const EdgeInsets.all(10.0),
itemCount:
snapshot.data != null ? snapshot.data!.length : 0,
itemCount: _meals.length,
itemBuilder: (context, index) {
final meal = snapshot.data![index];
final meal = _meals[index];
return ListTile(
onTap: () {
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 {
if (_mealPortionTypeForm.currentState!.validate()) {
bool isNew = widget.mealPortionType == null;
isNew
? MealPortionType.save(
// isNew
// ? 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,
notes: _notesController.text,
)
: MealPortionType.update(
widget.mealPortionType!.objectId!,
value: _valueController.text,
notes: _notesController.text,
);
));
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/progress_indicator.dart';
// import 'package:diameter/components/progress_indicator.dart';
import 'package:diameter/config.dart';
import 'package:diameter/navigation.dart';
import 'package:diameter/screens/meal/meal_portion_type_detail.dart';
@ -17,11 +17,11 @@ class MealPortionTypeListScreen extends StatefulWidget {
}
class _MealPortionTypeListScreenState extends State<MealPortionTypeListScreen> {
late Future<List<MealPortionType>?> _mealPortionTypes;
List<MealPortionType> _mealPortionTypes = [];
void refresh({String? message}) {
setState(() {
_mealPortionTypes = MealPortionType.fetchAll();
_mealPortionTypes = MealPortionType.getAll();
});
setState(() {
if (message != null) {
@ -37,9 +37,8 @@ class _MealPortionTypeListScreenState extends State<MealPortionTypeListScreen> {
}
void onDelete(MealPortionType mealPortionType) {
mealPortionType
.delete()
.then((_) => refresh(message: 'Meal Portion Type deleted'));
MealPortionType.remove(mealPortionType.id);
refresh(message: 'Meal Portion Type deleted');
}
void handleDeleteAction(MealPortionType mealPortionType) async {
@ -75,28 +74,12 @@ class _MealPortionTypeListScreenState extends State<MealPortionTypeListScreen> {
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Expanded(
child: FutureBuilder<List<MealPortionType>?>(
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(
child: _mealPortionTypes.isNotEmpty ? ListView.builder(
padding: const EdgeInsets.only(top: 10.0),
itemCount: snapshot.data != null
? snapshot.data!.length
: 0,
itemCount: _mealPortionTypes.length,
itemBuilder: (context, index) {
final mealPortionType = snapshot.data![index];
final mealPortionType = _mealPortionTypes[index];
return ListTile(
onTap: () {
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/forms.dart';
import 'package:diameter/config.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:diameter/navigation.dart';
// import 'package:diameter/objectbox.g.dart';
import 'package:flutter/material.dart';
class MealSourceDetailScreen extends StatefulWidget {
@ -21,61 +23,79 @@ class MealSourceDetailScreen extends StatefulWidget {
}
class _MealSourceDetailScreenState extends State<MealSourceDetailScreen> {
late Future<List<Accuracy>> _portionSizeAccuracies;
late Future<List<Accuracy>> _carbsRatioAccuracies;
late Future<List<MealCategory>> _mealCategories;
late Future<List<MealPortionType>> _mealPortionTypes;
List<Accuracy> _portionSizeAccuracies = [];
List<Accuracy> _carbsRatioAccuracies = [];
List<MealCategory> _mealCategories = [];
List<MealPortionType> _mealPortionTypes = [];
final GlobalKey<FormState> _mealSourceForm = GlobalKey<FormState>();
final _valueController = TextEditingController(text: '');
final _notesController = TextEditingController(text: '');
String? _defaultCarbsRatioAccuracy;
String? _defaultPortionSizeAccuracy;
String? _defaultMealCategory;
String? _defaultMealPortionType;
Accuracy? _defaultCarbsRatioAccuracy;
Accuracy? _defaultPortionSizeAccuracy;
MealCategory? _defaultMealCategory;
MealPortionType? _defaultMealPortionType;
@override
void initState() {
super.initState();
_portionSizeAccuracies = Accuracy.getAllForPortionSize();
_carbsRatioAccuracies = Accuracy.getAllForCarbsRatio();
_mealCategories = MealCategory.getAll();
_mealPortionTypes = MealPortionType.getAll();
if (widget.mealSource != null) {
_valueController.text = widget.mealSource!.value;
_notesController.text = widget.mealSource!.notes ?? '';
_defaultCarbsRatioAccuracy = widget.mealSource!.defaultCarbsRatioAccuracy;
_defaultPortionSizeAccuracy =
widget.mealSource!.defaultPortionSizeAccuracy;
_defaultMealCategory = widget.mealSource!.defaultMealCategory;
_defaultMealPortionType = widget.mealSource!.defaultMealPortionType;
}
widget.mealSource!.defaultPortionSizeAccuracy.target;
_defaultCarbsRatioAccuracy = widget.mealSource!.defaultCarbsRatioAccuracy.target;
_portionSizeAccuracies = Accuracy.fetchAllForPortionSize();
_carbsRatioAccuracies = Accuracy.fetchAllForCarbsRatio();
_mealCategories = MealCategory.fetchAll();
_mealPortionTypes = MealPortionType.fetchAll();
_defaultMealCategory = widget.mealSource!.defaultMealCategory.target;
_defaultMealPortionType =
widget.mealSource!.defaultMealPortionType.target;
}
}
void handleSaveAction() async {
bool isNew = widget.mealSource == null;
if (_mealSourceForm.currentState!.validate()) {
isNew
? await MealSource.save(
// isNew
// ? 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,
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,
);
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');
}
}
@ -93,13 +113,13 @@ class _MealSourceDetailScreenState extends State<MealSourceDetailScreen> {
(!isNew &&
(_valueController.text != widget.mealSource!.value ||
_defaultCarbsRatioAccuracy !=
widget.mealSource!.defaultCarbsRatioAccuracy ||
widget.mealSource!.defaultCarbsRatioAccuracy.target ||
_defaultPortionSizeAccuracy !=
widget.mealSource!.defaultPortionSizeAccuracy ||
widget.mealSource!.defaultPortionSizeAccuracy.target ||
_defaultMealCategory !=
widget.mealSource!.defaultMealCategory ||
widget.mealSource!.defaultMealCategory.target ||
_defaultMealPortionType !=
widget.mealSource!.defaultMealPortionType ||
widget.mealSource!.defaultMealPortionType.target ||
_notesController.text !=
(widget.mealSource!.notes ?? ''))))) {
Dialogs.showCancelConfirmationDialog(
@ -140,11 +160,10 @@ class _MealSourceDetailScreenState extends State<MealSourceDetailScreen> {
return null;
},
),
StyledFutureDropdownButton<Accuracy>(
StyledDropdownButton<Accuracy>(
selectedItem: _defaultCarbsRatioAccuracy,
label: 'Default Carbs Ratio Accuracy',
items: _carbsRatioAccuracies,
getItemValue: (item) => item.objectId,
renderItem: (item) => Text(item.value),
onChanged: (value) {
setState(() {
@ -152,11 +171,10 @@ class _MealSourceDetailScreenState extends State<MealSourceDetailScreen> {
});
},
),
StyledFutureDropdownButton<Accuracy>(
StyledDropdownButton<Accuracy>(
selectedItem: _defaultPortionSizeAccuracy,
label: 'Default Portion Size Accuracy',
items: _portionSizeAccuracies,
getItemValue: (item) => item.objectId,
renderItem: (item) => Text(item.value),
onChanged: (value) {
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,
label: 'Default Meal Category',
items: _mealCategories,
getItemValue: (item) => item.objectId,
// getItemValue: (item) => item.objectId,
renderItem: (item) => Text(item.value),
onChanged: (value) {
setState(() {
@ -176,11 +218,11 @@ class _MealSourceDetailScreenState extends State<MealSourceDetailScreen> {
});
},
),
StyledFutureDropdownButton<MealPortionType>(
StyledDropdownButton<MealPortionType>(
selectedItem: _defaultMealPortionType,
label: 'Default Meal Portion Type',
items: _mealPortionTypes,
getItemValue: (item) => item.objectId,
// getItemValue: (item) => item.objectId,
renderItem: (item) => Text(item.value),
onChanged: (value) {
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/navigation.dart';
import 'package:diameter/screens/meal/meal_source_detail.dart';
@ -14,11 +16,11 @@ class MealSourceListScreen extends StatefulWidget {
}
class _MealSourceListScreenState extends State<MealSourceListScreen> {
late Future<List<MealSource>?> _mealSources;
List<MealSource> _mealSources = [];
void refresh({String? message}) {
setState(() {
_mealSources = MealSource.fetchAll();
_mealSources = MealSource.getAll();
});
setState(() {
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
void initState() {
super.initState();
@ -53,40 +72,22 @@ class _MealSourceListScreenState extends State<MealSourceListScreen> {
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Expanded(
child: FutureBuilder<List<MealSource>?>(
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(
child: _mealSources.isNotEmpty ? ListView.builder(
padding: const EdgeInsets.only(top: 10.0),
itemCount: snapshot.data != null
? snapshot.data!.length
: 0,
itemCount: _mealSources.length,
itemBuilder: (context, index) {
final mealSource = snapshot.data![index];
final mealSource = _mealSources[index];
return ListTile(
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
MealSourceDetailScreen(
builder: (context) => MealSourceDetailScreen(
mealSource: mealSource,
),
),
).then(
(message) => refresh(message: message));
).then((message) => refresh(message: message));
},
title: Text(mealSource.value),
subtitle: Text(mealSource.notes ?? ''),
@ -99,18 +100,15 @@ class _MealSourceListScreenState extends State<MealSourceListScreen> {
color: Colors.blue,
),
onPressed: () async {
// add confirmation dialog
await mealSource.delete().then((_) {
refresh(
message: 'Meal Source deleted');
});
handleDeleteAction(mealSource);
},
),
],
),
);
}));
},
}
) : 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
# See https://dart.dev/tools/pub/glossary#lockfile
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:
dependency: transitive
description:
@ -14,7 +28,7 @@ packages:
name: async
url: "https://pub.dartlang.org"
source: hosted
version: "2.8.1"
version: "2.8.2"
boolean_selector:
dependency: transitive
description:
@ -22,13 +36,69 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
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:
dependency: transitive
description:
name: characters
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0"
version: "1.2.0"
charcode:
dependency: transitive
description:
@ -36,6 +106,20 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
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:
dependency: transitive
description:
@ -43,6 +127,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.0"
code_builder:
dependency: transitive
description:
name: code_builder
url: "https://pub.dartlang.org"
source: hosted
version: "4.1.0"
collection:
dependency: transitive
description:
@ -92,6 +183,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.0"
convert:
dependency: transitive
description:
name: convert
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.1"
crypto:
dependency: transitive
description:
@ -106,6 +204,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.3"
dart_style:
dependency: transitive
description:
name: dart_style
url: "https://pub.dartlang.org"
source: hosted
version: "2.2.0"
dbus:
dependency: transitive
description:
@ -141,6 +246,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "6.1.2"
fixnum:
dependency: transitive
description:
name: fixnum
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.0"
flex_color_scheme:
dependency: "direct main"
description:
@ -170,6 +282,27 @@ packages:
description: flutter
source: sdk
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:
dependency: transitive
description:
@ -177,6 +310,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
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:
dependency: transitive
description:
@ -198,6 +338,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "0.17.0"
io:
dependency: transitive
description:
name: io
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.3"
js:
dependency: transitive
description:
@ -205,6 +352,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "0.6.3"
json_annotation:
dependency: transitive
description:
name: json_annotation
url: "https://pub.dartlang.org"
source: hosted
version: "4.3.0"
lints:
dependency: transitive
description:
@ -212,13 +366,20 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.1"
logging:
dependency: transitive
description:
name: logging
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.2"
matcher:
dependency: transitive
description:
name: matcher
url: "https://pub.dartlang.org"
source: hosted
version: "0.12.10"
version: "0.12.11"
meta:
dependency: transitive
description:
@ -226,6 +387,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.7.0"
mime:
dependency: transitive
description:
name: mime
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.1"
mime_type:
dependency: transitive
description:
@ -247,6 +415,34 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
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:
dependency: transitive
description:
@ -316,7 +512,7 @@ packages:
name: path_provider
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.5"
version: "2.0.6"
path_provider_linux:
dependency: transitive
description:
@ -373,6 +569,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.2"
pool:
dependency: transitive
description:
name: pool
url: "https://pub.dartlang.org"
source: hosted
version: "1.5.0"
process:
dependency: transitive
description:
@ -387,6 +590,20 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
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:
dependency: transitive
description:
@ -443,11 +660,32 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
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:
dependency: transitive
description: flutter
source: sdk
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:
dependency: transitive
description:
@ -483,6 +721,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
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:
dependency: transitive
description:
@ -510,7 +755,14 @@ packages:
name: test_api
url: "https://pub.dartlang.org"
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:
dependency: transitive
description:
@ -531,7 +783,14 @@ packages:
name: vector_math
url: "https://pub.dartlang.org"
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:
dependency: transitive
description:
@ -567,6 +826,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0"
yaml:
dependency: transitive
description:
name: yaml
url: "https://pub.dartlang.org"
source: hosted
version: "3.1.0"
sdks:
dart: ">=2.14.0 <3.0.0"
flutter: ">=2.5.0"

View File

@ -1,12 +1,12 @@
name: diameter
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
environment:
sdk: '>=2.12.0 <3.0.0'
sdk: ">=2.12.0 <3.0.0"
dependencies:
parse_server_sdk_flutter: ^3.1.0
@ -18,12 +18,16 @@ dependencies:
flex_color_scheme: ^3.0.1
shared_preferences: ^2.0.8
intl: ^0.17.0
objectbox: ^1.2.0
objectbox_flutter_libs: any
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^1.0.4
provider: ^6.0.1
build_runner: ^2.0.0
objectbox_generator: any
flutter:
uses-material-design: true