Merge branch 'main' of https://git.sudo.ca/spinel/diameter into main
This commit is contained in:
commit
481dc60996
@ -68,4 +68,19 @@ class LogEntry {
|
||||
String toString() {
|
||||
return DateTimeUtils.displayDateTime(time);
|
||||
}
|
||||
|
||||
static List<LogEntry> getAllForDate(DateTime date) {
|
||||
DateTime startOfDay = DateTime(date.year, date.month, date.day);
|
||||
DateTime endOfDay = startOfDay.add(const Duration(days: 1));
|
||||
QueryBuilder<LogEntry> builder = box.query(LogEntry_.deleted.equals(false))
|
||||
..order(LogEntry_.time, flags: Order.descending);
|
||||
return builder.build().find().where((entry) {
|
||||
return (entry.time.compareTo(startOfDay) >= 0 && entry.time.isBefore(endOfDay));
|
||||
}).toList();
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return DateTimeUtils.displayDateTime(time);
|
||||
}
|
||||
}
|
||||
|
@ -44,7 +44,6 @@ class LogEvent {
|
||||
|
||||
// methods
|
||||
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) {
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1234,7 +1234,7 @@ ModelDefinition getObjectBoxModel() {
|
||||
final buffer = fb.BufferContext(fbData);
|
||||
final rootOffset = buffer.derefObject(0);
|
||||
|
||||
final object = Bolus(
|
||||
final object = LogEventType(
|
||||
id: const fb.Int64Reader().vTableGet(buffer, rootOffset, 4, 0),
|
||||
deleted: const fb.BoolReader()
|
||||
.vTableGet(buffer, rootOffset, 20, false),
|
||||
@ -1250,190 +1250,6 @@ ModelDefinition getObjectBoxModel() {
|
||||
.vTableGetNullable(buffer, rootOffset, 14),
|
||||
mmolPerL: const fb.Float64Reader()
|
||||
.vTableGetNullable(buffer, rootOffset, 16));
|
||||
object.bolusProfile.targetId =
|
||||
const fb.Int64Reader().vTableGet(buffer, rootOffset, 18, 0);
|
||||
object.bolusProfile.attach(store);
|
||||
return object;
|
||||
}),
|
||||
BolusProfile: EntityDefinition<BolusProfile>(
|
||||
model: _entities[3],
|
||||
toOneRelations: (BolusProfile object) => [],
|
||||
toManyRelations: (BolusProfile object) => {},
|
||||
getId: (BolusProfile object) => object.id,
|
||||
setId: (BolusProfile object, int id) {
|
||||
object.id = id;
|
||||
},
|
||||
objectToFB: (BolusProfile object, fb.Builder fbb) {
|
||||
final nameOffset = fbb.writeString(object.name);
|
||||
final notesOffset =
|
||||
object.notes == null ? null : fbb.writeString(object.notes!);
|
||||
fbb.startTable(6);
|
||||
fbb.addInt64(0, object.id);
|
||||
fbb.addOffset(1, nameOffset);
|
||||
fbb.addBool(2, object.active);
|
||||
fbb.addOffset(3, notesOffset);
|
||||
fbb.addBool(4, object.deleted);
|
||||
fbb.finish(fbb.endTable());
|
||||
return object.id;
|
||||
},
|
||||
objectFromFB: (Store store, ByteData fbData) {
|
||||
final buffer = fb.BufferContext(fbData);
|
||||
final rootOffset = buffer.derefObject(0);
|
||||
|
||||
final object = BolusProfile(
|
||||
id: const fb.Int64Reader().vTableGet(buffer, rootOffset, 4, 0),
|
||||
deleted: const fb.BoolReader()
|
||||
.vTableGet(buffer, rootOffset, 12, false),
|
||||
name: const fb.StringReader(asciiOptimization: true)
|
||||
.vTableGet(buffer, rootOffset, 6, ''),
|
||||
active:
|
||||
const fb.BoolReader().vTableGet(buffer, rootOffset, 8, false),
|
||||
notes: const fb.StringReader(asciiOptimization: true)
|
||||
.vTableGetNullable(buffer, rootOffset, 10));
|
||||
|
||||
return object;
|
||||
}),
|
||||
LogEntry: EntityDefinition<LogEntry>(
|
||||
model: _entities[4],
|
||||
toOneRelations: (LogEntry object) => [],
|
||||
toManyRelations: (LogEntry object) => {},
|
||||
getId: (LogEntry object) => object.id,
|
||||
setId: (LogEntry object, int id) {
|
||||
object.id = id;
|
||||
},
|
||||
objectToFB: (LogEntry object, fb.Builder fbb) {
|
||||
final notesOffset =
|
||||
object.notes == null ? null : fbb.writeString(object.notes!);
|
||||
fbb.startTable(11);
|
||||
fbb.addInt64(0, object.id);
|
||||
fbb.addInt64(1, object.time.millisecondsSinceEpoch);
|
||||
fbb.addInt64(2, object.mgPerDl);
|
||||
fbb.addFloat64(3, object.mmolPerL);
|
||||
fbb.addOffset(7, notesOffset);
|
||||
fbb.addBool(8, object.deleted);
|
||||
fbb.addFloat64(9, object.glucoseTrend);
|
||||
fbb.finish(fbb.endTable());
|
||||
return object.id;
|
||||
},
|
||||
objectFromFB: (Store store, ByteData fbData) {
|
||||
final buffer = fb.BufferContext(fbData);
|
||||
final rootOffset = buffer.derefObject(0);
|
||||
|
||||
final object = LogEntry(
|
||||
id: const fb.Int64Reader().vTableGet(buffer, rootOffset, 4, 0),
|
||||
deleted: const fb.BoolReader()
|
||||
.vTableGet(buffer, rootOffset, 20, false),
|
||||
time: DateTime.fromMillisecondsSinceEpoch(
|
||||
const fb.Int64Reader().vTableGet(buffer, rootOffset, 6, 0)),
|
||||
mgPerDl: const fb.Int64Reader()
|
||||
.vTableGetNullable(buffer, rootOffset, 8),
|
||||
mmolPerL: const fb.Float64Reader()
|
||||
.vTableGetNullable(buffer, rootOffset, 10),
|
||||
glucoseTrend: const fb.Float64Reader()
|
||||
.vTableGetNullable(buffer, rootOffset, 22),
|
||||
notes: const fb.StringReader(asciiOptimization: true)
|
||||
.vTableGetNullable(buffer, rootOffset, 18));
|
||||
|
||||
return object;
|
||||
}),
|
||||
LogEvent: EntityDefinition<LogEvent>(
|
||||
model: _entities[5],
|
||||
toOneRelations: (LogEvent object) =>
|
||||
[object.eventType, object.bolusProfile, object.basalProfile],
|
||||
toManyRelations: (LogEvent object) => {},
|
||||
getId: (LogEvent object) => object.id,
|
||||
setId: (LogEvent object, int id) {
|
||||
object.id = id;
|
||||
},
|
||||
objectToFB: (LogEvent object, fb.Builder fbb) {
|
||||
final notesOffset =
|
||||
object.notes == null ? null : fbb.writeString(object.notes!);
|
||||
fbb.startTable(13);
|
||||
fbb.addInt64(0, object.id);
|
||||
fbb.addInt64(1, object.time.millisecondsSinceEpoch);
|
||||
fbb.addInt64(2, object.endTime?.millisecondsSinceEpoch);
|
||||
fbb.addBool(3, object.hasEndTime);
|
||||
fbb.addOffset(4, notesOffset);
|
||||
fbb.addInt64(7, object.eventType.targetId);
|
||||
fbb.addBool(8, object.deleted);
|
||||
fbb.addInt64(9, object.bolusProfile.targetId);
|
||||
fbb.addInt64(10, object.basalProfile.targetId);
|
||||
fbb.addInt64(11, object.reminderDuration);
|
||||
fbb.finish(fbb.endTable());
|
||||
return object.id;
|
||||
},
|
||||
objectFromFB: (Store store, ByteData fbData) {
|
||||
final buffer = fb.BufferContext(fbData);
|
||||
final rootOffset = buffer.derefObject(0);
|
||||
final endTimeValue =
|
||||
const fb.Int64Reader().vTableGetNullable(buffer, rootOffset, 8);
|
||||
final object = LogEvent(
|
||||
id: const fb.Int64Reader().vTableGet(buffer, rootOffset, 4, 0),
|
||||
deleted: const fb.BoolReader()
|
||||
.vTableGet(buffer, rootOffset, 20, false),
|
||||
time: DateTime.fromMillisecondsSinceEpoch(
|
||||
const fb.Int64Reader().vTableGet(buffer, rootOffset, 6, 0)),
|
||||
endTime: endTimeValue == null
|
||||
? null
|
||||
: DateTime.fromMillisecondsSinceEpoch(endTimeValue),
|
||||
hasEndTime: const fb.BoolReader()
|
||||
.vTableGet(buffer, rootOffset, 10, false),
|
||||
reminderDuration: const fb.Int64Reader()
|
||||
.vTableGetNullable(buffer, rootOffset, 26),
|
||||
notes: const fb.StringReader(asciiOptimization: true)
|
||||
.vTableGetNullable(buffer, rootOffset, 12));
|
||||
object.eventType.targetId =
|
||||
const fb.Int64Reader().vTableGet(buffer, rootOffset, 18, 0);
|
||||
object.eventType.attach(store);
|
||||
object.bolusProfile.targetId =
|
||||
const fb.Int64Reader().vTableGet(buffer, rootOffset, 22, 0);
|
||||
object.bolusProfile.attach(store);
|
||||
object.basalProfile.targetId =
|
||||
const fb.Int64Reader().vTableGet(buffer, rootOffset, 24, 0);
|
||||
object.basalProfile.attach(store);
|
||||
return object;
|
||||
}),
|
||||
LogEventType: EntityDefinition<LogEventType>(
|
||||
model: _entities[6],
|
||||
toOneRelations: (LogEventType object) =>
|
||||
[object.bolusProfile, object.basalProfile],
|
||||
toManyRelations: (LogEventType object) => {},
|
||||
getId: (LogEventType object) => object.id,
|
||||
setId: (LogEventType object, int id) {
|
||||
object.id = id;
|
||||
},
|
||||
objectToFB: (LogEventType object, fb.Builder fbb) {
|
||||
final valueOffset = fbb.writeString(object.value);
|
||||
final notesOffset =
|
||||
object.notes == null ? null : fbb.writeString(object.notes!);
|
||||
fbb.startTable(9);
|
||||
fbb.addInt64(0, object.id);
|
||||
fbb.addOffset(1, valueOffset);
|
||||
fbb.addBool(2, object.hasEndTime);
|
||||
fbb.addInt64(3, object.defaultReminderDuration);
|
||||
fbb.addOffset(4, notesOffset);
|
||||
fbb.addBool(5, object.deleted);
|
||||
fbb.addInt64(6, object.bolusProfile.targetId);
|
||||
fbb.addInt64(7, object.basalProfile.targetId);
|
||||
fbb.finish(fbb.endTable());
|
||||
return object.id;
|
||||
},
|
||||
objectFromFB: (Store store, ByteData fbData) {
|
||||
final buffer = fb.BufferContext(fbData);
|
||||
final rootOffset = buffer.derefObject(0);
|
||||
|
||||
final object = LogEventType(
|
||||
id: const fb.Int64Reader().vTableGet(buffer, rootOffset, 4, 0),
|
||||
deleted: const fb.BoolReader()
|
||||
.vTableGet(buffer, rootOffset, 14, false),
|
||||
value: const fb.StringReader(asciiOptimization: true)
|
||||
.vTableGet(buffer, rootOffset, 6, ''),
|
||||
hasEndTime:
|
||||
const fb.BoolReader().vTableGet(buffer, rootOffset, 8, false),
|
||||
defaultReminderDuration: const fb.Int64Reader()
|
||||
.vTableGetNullable(buffer, rootOffset, 10),
|
||||
notes: const fb.StringReader(asciiOptimization: true)
|
||||
.vTableGetNullable(buffer, rootOffset, 12));
|
||||
object.bolusProfile.targetId =
|
||||
const fb.Int64Reader().vTableGet(buffer, rootOffset, 16, 0);
|
||||
object.bolusProfile.attach(store);
|
||||
@ -1487,6 +1303,353 @@ ModelDefinition getObjectBoxModel() {
|
||||
final rootOffset = buffer.derefObject(0);
|
||||
|
||||
final object = LogMeal(
|
||||
id: const fb.Int64Reader().vTableGet(buffer, rootOffset, 4, 0),
|
||||
deleted: const fb.BoolReader()
|
||||
.vTableGet(buffer, rootOffset, 36, false),
|
||||
value:
|
||||
const fb.StringReader().vTableGet(buffer, rootOffset, 6, ''),
|
||||
amount:
|
||||
const fb.Float64Reader().vTableGet(buffer, rootOffset, 38, 0),
|
||||
carbsRatio: const fb.Float64Reader()
|
||||
.vTableGetNullable(buffer, rootOffset, 8),
|
||||
portionSize: const fb.Float64Reader()
|
||||
.vTableGetNullable(buffer, rootOffset, 10),
|
||||
totalCarbs: const fb.Float64Reader()
|
||||
.vTableGetNullable(buffer, rootOffset, 40),
|
||||
notes: const fb.StringReader()
|
||||
.vTableGetNullable(buffer, rootOffset, 20))
|
||||
..bolus = const fb.Float64Reader()
|
||||
.vTableGetNullable(buffer, rootOffset, 14);
|
||||
object.logEntry.targetId =
|
||||
const fb.Int64Reader().vTableGet(buffer, rootOffset, 22, 0);
|
||||
object.logEntry.attach(store);
|
||||
object.meal.targetId =
|
||||
const fb.Int64Reader().vTableGet(buffer, rootOffset, 24, 0);
|
||||
object.meal.attach(store);
|
||||
object.mealSource.targetId =
|
||||
const fb.Int64Reader().vTableGet(buffer, rootOffset, 26, 0);
|
||||
object.mealSource.attach(store);
|
||||
object.mealCategory.targetId =
|
||||
const fb.Int64Reader().vTableGet(buffer, rootOffset, 28, 0);
|
||||
object.mealCategory.attach(store);
|
||||
object.mealPortionType.targetId =
|
||||
const fb.Int64Reader().vTableGet(buffer, rootOffset, 30, 0);
|
||||
object.mealPortionType.attach(store);
|
||||
object.portionSizeAccuracy.targetId =
|
||||
const fb.Int64Reader().vTableGet(buffer, rootOffset, 32, 0);
|
||||
object.portionSizeAccuracy.attach(store);
|
||||
object.carbsRatioAccuracy.targetId =
|
||||
const fb.Int64Reader().vTableGet(buffer, rootOffset, 34, 0);
|
||||
object.carbsRatioAccuracy.attach(store);
|
||||
return object;
|
||||
}),
|
||||
Meal: EntityDefinition<Meal>(
|
||||
model: _entities[8],
|
||||
toOneRelations: (Meal object) => [
|
||||
object.mealSource,
|
||||
object.mealCategory,
|
||||
object.mealPortionType,
|
||||
object.portionSizeAccuracy,
|
||||
object.carbsRatioAccuracy
|
||||
],
|
||||
toManyRelations: (Meal object) => {},
|
||||
getId: (Meal object) => object.id,
|
||||
setId: (Meal object, int id) {
|
||||
object.id = id;
|
||||
},
|
||||
objectToFB: (Meal object, fb.Builder fbb) {
|
||||
final valueOffset = fbb.writeString(object.value);
|
||||
final notesOffset =
|
||||
object.notes == null ? null : fbb.writeString(object.notes!);
|
||||
fbb.startTable(16);
|
||||
fbb.addInt64(0, object.id);
|
||||
fbb.addOffset(1, valueOffset);
|
||||
fbb.addFloat64(2, object.carbsRatio);
|
||||
fbb.addFloat64(3, object.portionSize);
|
||||
fbb.addFloat64(4, object.carbsPerPortion);
|
||||
fbb.addInt64(5, object.delayedBolusDuration);
|
||||
fbb.addOffset(7, notesOffset);
|
||||
fbb.addInt64(8, object.mealSource.targetId);
|
||||
fbb.addInt64(9, object.mealCategory.targetId);
|
||||
fbb.addInt64(10, object.mealPortionType.targetId);
|
||||
fbb.addInt64(11, object.portionSizeAccuracy.targetId);
|
||||
fbb.addInt64(12, object.carbsRatioAccuracy.targetId);
|
||||
fbb.addBool(13, object.deleted);
|
||||
fbb.addFloat64(14, object.delayedBolusPercentage);
|
||||
fbb.finish(fbb.endTable());
|
||||
return object.id;
|
||||
},
|
||||
objectFromFB: (Store store, ByteData fbData) {
|
||||
final buffer = fb.BufferContext(fbData);
|
||||
final rootOffset = buffer.derefObject(0);
|
||||
|
||||
final object = Meal(
|
||||
id: const fb.Int64Reader().vTableGet(buffer, rootOffset, 4, 0),
|
||||
deleted: const fb.BoolReader()
|
||||
.vTableGet(buffer, rootOffset, 30, false),
|
||||
value:
|
||||
const fb.StringReader().vTableGet(buffer, rootOffset, 6, ''),
|
||||
carbsRatio: const fb.Float64Reader()
|
||||
.vTableGetNullable(buffer, rootOffset, 8),
|
||||
portionSize: const fb.Float64Reader()
|
||||
.vTableGetNullable(buffer, rootOffset, 10),
|
||||
carbsPerPortion: const fb.Float64Reader()
|
||||
.vTableGetNullable(buffer, rootOffset, 12),
|
||||
delayedBolusDuration: const fb.Int64Reader()
|
||||
.vTableGetNullable(buffer, rootOffset, 14),
|
||||
delayedBolusPercentage: const fb.Float64Reader()
|
||||
.vTableGetNullable(buffer, rootOffset, 32),
|
||||
notes: const fb.StringReader()
|
||||
.vTableGetNullable(buffer, rootOffset, 18));
|
||||
object.mealSource.targetId =
|
||||
const fb.Int64Reader().vTableGet(buffer, rootOffset, 20, 0);
|
||||
object.mealSource.attach(store);
|
||||
object.mealCategory.targetId =
|
||||
const fb.Int64Reader().vTableGet(buffer, rootOffset, 22, 0);
|
||||
object.mealCategory.attach(store);
|
||||
object.mealPortionType.targetId =
|
||||
const fb.Int64Reader().vTableGet(buffer, rootOffset, 24, 0);
|
||||
object.mealPortionType.attach(store);
|
||||
object.portionSizeAccuracy.targetId =
|
||||
const fb.Int64Reader().vTableGet(buffer, rootOffset, 26, 0);
|
||||
object.portionSizeAccuracy.attach(store);
|
||||
object.carbsRatioAccuracy.targetId =
|
||||
const fb.Int64Reader().vTableGet(buffer, rootOffset, 28, 0);
|
||||
object.carbsRatioAccuracy.attach(store);
|
||||
return object;
|
||||
}),
|
||||
BolusProfile: EntityDefinition<BolusProfile>(
|
||||
model: _entities[3],
|
||||
toOneRelations: (BolusProfile object) => [],
|
||||
toManyRelations: (BolusProfile object) => {},
|
||||
getId: (BolusProfile object) => object.id,
|
||||
setId: (BolusProfile object, int id) {
|
||||
object.id = id;
|
||||
},
|
||||
objectToFB: (MealCategory object, fb.Builder fbb) {
|
||||
final valueOffset = fbb.writeString(object.value);
|
||||
final notesOffset =
|
||||
object.notes == null ? null : fbb.writeString(object.notes!);
|
||||
fbb.startTable(6);
|
||||
fbb.addInt64(0, object.id);
|
||||
fbb.addOffset(1, nameOffset);
|
||||
fbb.addBool(2, object.active);
|
||||
fbb.addOffset(3, notesOffset);
|
||||
fbb.addBool(4, object.deleted);
|
||||
fbb.finish(fbb.endTable());
|
||||
return object.id;
|
||||
},
|
||||
objectFromFB: (Store store, ByteData fbData) {
|
||||
final buffer = fb.BufferContext(fbData);
|
||||
final rootOffset = buffer.derefObject(0);
|
||||
|
||||
final object = MealCategory(
|
||||
id: const fb.Int64Reader().vTableGet(buffer, rootOffset, 4, 0),
|
||||
deleted: const fb.BoolReader()
|
||||
.vTableGet(buffer, rootOffset, 12, false),
|
||||
name: const fb.StringReader(asciiOptimization: true)
|
||||
.vTableGet(buffer, rootOffset, 6, ''),
|
||||
active:
|
||||
const fb.BoolReader().vTableGet(buffer, rootOffset, 8, false),
|
||||
notes: const fb.StringReader(asciiOptimization: true)
|
||||
.vTableGetNullable(buffer, rootOffset, 10));
|
||||
|
||||
return object;
|
||||
}),
|
||||
LogEntry: EntityDefinition<LogEntry>(
|
||||
model: _entities[4],
|
||||
toOneRelations: (LogEntry object) => [],
|
||||
toManyRelations: (LogEntry object) => {},
|
||||
getId: (LogEntry object) => object.id,
|
||||
setId: (LogEntry object, int id) {
|
||||
object.id = id;
|
||||
},
|
||||
objectToFB: (MealPortionType object, fb.Builder fbb) {
|
||||
final valueOffset = fbb.writeString(object.value);
|
||||
final notesOffset =
|
||||
object.notes == null ? null : fbb.writeString(object.notes!);
|
||||
fbb.startTable(11);
|
||||
fbb.addInt64(0, object.id);
|
||||
fbb.addInt64(1, object.time.millisecondsSinceEpoch);
|
||||
fbb.addInt64(2, object.mgPerDl);
|
||||
fbb.addFloat64(3, object.mmolPerL);
|
||||
fbb.addOffset(7, notesOffset);
|
||||
fbb.addBool(8, object.deleted);
|
||||
fbb.addFloat64(9, object.glucoseTrend);
|
||||
fbb.finish(fbb.endTable());
|
||||
return object.id;
|
||||
},
|
||||
objectFromFB: (Store store, ByteData fbData) {
|
||||
final buffer = fb.BufferContext(fbData);
|
||||
final rootOffset = buffer.derefObject(0);
|
||||
|
||||
final object = MealPortionType(
|
||||
id: const fb.Int64Reader().vTableGet(buffer, rootOffset, 4, 0),
|
||||
deleted: const fb.BoolReader()
|
||||
.vTableGet(buffer, rootOffset, 20, false),
|
||||
time: DateTime.fromMillisecondsSinceEpoch(
|
||||
const fb.Int64Reader().vTableGet(buffer, rootOffset, 6, 0)),
|
||||
mgPerDl: const fb.Int64Reader()
|
||||
.vTableGetNullable(buffer, rootOffset, 8),
|
||||
mmolPerL: const fb.Float64Reader()
|
||||
.vTableGetNullable(buffer, rootOffset, 10),
|
||||
glucoseTrend: const fb.Float64Reader()
|
||||
.vTableGetNullable(buffer, rootOffset, 22),
|
||||
notes: const fb.StringReader(asciiOptimization: true)
|
||||
.vTableGetNullable(buffer, rootOffset, 18));
|
||||
|
||||
return object;
|
||||
}),
|
||||
LogEvent: EntityDefinition<LogEvent>(
|
||||
model: _entities[5],
|
||||
toOneRelations: (LogEvent object) =>
|
||||
[object.eventType, object.bolusProfile, object.basalProfile],
|
||||
toManyRelations: (LogEvent object) => {},
|
||||
getId: (LogEvent object) => object.id,
|
||||
setId: (LogEvent object, int id) {
|
||||
object.id = id;
|
||||
},
|
||||
objectToFB: (MealSource object, fb.Builder fbb) {
|
||||
final valueOffset = fbb.writeString(object.value);
|
||||
final notesOffset =
|
||||
object.notes == null ? null : fbb.writeString(object.notes!);
|
||||
fbb.startTable(13);
|
||||
fbb.addInt64(0, object.id);
|
||||
fbb.addInt64(1, object.time.millisecondsSinceEpoch);
|
||||
fbb.addInt64(2, object.endTime?.millisecondsSinceEpoch);
|
||||
fbb.addBool(3, object.hasEndTime);
|
||||
fbb.addOffset(4, notesOffset);
|
||||
fbb.addInt64(7, object.eventType.targetId);
|
||||
fbb.addBool(8, object.deleted);
|
||||
fbb.addInt64(9, object.bolusProfile.targetId);
|
||||
fbb.addInt64(10, object.basalProfile.targetId);
|
||||
fbb.addInt64(11, object.reminderDuration);
|
||||
fbb.finish(fbb.endTable());
|
||||
return object.id;
|
||||
},
|
||||
objectFromFB: (Store store, ByteData fbData) {
|
||||
final buffer = fb.BufferContext(fbData);
|
||||
final rootOffset = buffer.derefObject(0);
|
||||
|
||||
final object = MealSource(
|
||||
id: const fb.Int64Reader().vTableGet(buffer, rootOffset, 4, 0),
|
||||
deleted: const fb.BoolReader()
|
||||
.vTableGet(buffer, rootOffset, 20, false),
|
||||
time: DateTime.fromMillisecondsSinceEpoch(
|
||||
const fb.Int64Reader().vTableGet(buffer, rootOffset, 6, 0)),
|
||||
endTime: endTimeValue == null
|
||||
? null
|
||||
: DateTime.fromMillisecondsSinceEpoch(endTimeValue),
|
||||
hasEndTime: const fb.BoolReader()
|
||||
.vTableGet(buffer, rootOffset, 10, false),
|
||||
reminderDuration: const fb.Int64Reader()
|
||||
.vTableGetNullable(buffer, rootOffset, 26),
|
||||
notes: const fb.StringReader(asciiOptimization: true)
|
||||
.vTableGetNullable(buffer, rootOffset, 12));
|
||||
object.eventType.targetId =
|
||||
const fb.Int64Reader().vTableGet(buffer, rootOffset, 18, 0);
|
||||
object.eventType.attach(store);
|
||||
object.bolusProfile.targetId =
|
||||
const fb.Int64Reader().vTableGet(buffer, rootOffset, 22, 0);
|
||||
object.bolusProfile.attach(store);
|
||||
object.basalProfile.targetId =
|
||||
const fb.Int64Reader().vTableGet(buffer, rootOffset, 24, 0);
|
||||
object.basalProfile.attach(store);
|
||||
return object;
|
||||
}),
|
||||
LogEventType: EntityDefinition<LogEventType>(
|
||||
model: _entities[6],
|
||||
toOneRelations: (LogEventType object) =>
|
||||
[object.bolusProfile, object.basalProfile],
|
||||
toManyRelations: (LogEventType object) => {},
|
||||
getId: (LogEventType object) => object.id,
|
||||
setId: (LogEventType object, int id) {
|
||||
object.id = id;
|
||||
},
|
||||
objectToFB: (LogBolus object, fb.Builder fbb) {
|
||||
final notesOffset =
|
||||
object.notes == null ? null : fbb.writeString(object.notes!);
|
||||
fbb.startTable(9);
|
||||
fbb.addInt64(0, object.id);
|
||||
fbb.addOffset(1, valueOffset);
|
||||
fbb.addBool(2, object.hasEndTime);
|
||||
fbb.addInt64(3, object.defaultReminderDuration);
|
||||
fbb.addOffset(4, notesOffset);
|
||||
fbb.addBool(5, object.deleted);
|
||||
fbb.addInt64(6, object.bolusProfile.targetId);
|
||||
fbb.addInt64(7, object.basalProfile.targetId);
|
||||
fbb.finish(fbb.endTable());
|
||||
return object.id;
|
||||
},
|
||||
objectFromFB: (Store store, ByteData fbData) {
|
||||
final buffer = fb.BufferContext(fbData);
|
||||
final rootOffset = buffer.derefObject(0);
|
||||
|
||||
final object = LogBolus(
|
||||
id: const fb.Int64Reader().vTableGet(buffer, rootOffset, 4, 0),
|
||||
deleted: const fb.BoolReader()
|
||||
.vTableGet(buffer, rootOffset, 14, false),
|
||||
value: const fb.StringReader(asciiOptimization: true)
|
||||
.vTableGet(buffer, rootOffset, 6, ''),
|
||||
hasEndTime:
|
||||
const fb.BoolReader().vTableGet(buffer, rootOffset, 8, false),
|
||||
defaultReminderDuration: const fb.Int64Reader()
|
||||
.vTableGetNullable(buffer, rootOffset, 10),
|
||||
notes: const fb.StringReader(asciiOptimization: true)
|
||||
.vTableGetNullable(buffer, rootOffset, 12));
|
||||
object.bolusProfile.targetId =
|
||||
const fb.Int64Reader().vTableGet(buffer, rootOffset, 16, 0);
|
||||
object.bolusProfile.attach(store);
|
||||
object.basalProfile.targetId =
|
||||
const fb.Int64Reader().vTableGet(buffer, rootOffset, 18, 0);
|
||||
object.basalProfile.attach(store);
|
||||
return object;
|
||||
}),
|
||||
LogMeal: EntityDefinition<LogMeal>(
|
||||
model: _entities[7],
|
||||
toOneRelations: (LogMeal object) => [
|
||||
object.logEntry,
|
||||
object.meal,
|
||||
object.mealSource,
|
||||
object.mealCategory,
|
||||
object.mealPortionType,
|
||||
object.portionSizeAccuracy,
|
||||
object.carbsRatioAccuracy
|
||||
],
|
||||
toManyRelations: (LogMeal object) => {},
|
||||
getId: (LogMeal object) => object.id,
|
||||
setId: (LogMeal object, int id) {
|
||||
object.id = id;
|
||||
},
|
||||
objectToFB: (Accuracy object, fb.Builder fbb) {
|
||||
final valueOffset = fbb.writeString(object.value);
|
||||
final notesOffset =
|
||||
object.notes == null ? null : fbb.writeString(object.notes!);
|
||||
fbb.startTable(20);
|
||||
fbb.addInt64(0, object.id);
|
||||
fbb.addOffset(1, valueOffset);
|
||||
fbb.addFloat64(2, object.carbsRatio);
|
||||
fbb.addFloat64(3, object.portionSize);
|
||||
fbb.addFloat64(5, object.bolus);
|
||||
fbb.addOffset(8, notesOffset);
|
||||
fbb.addInt64(9, object.logEntry.targetId);
|
||||
fbb.addInt64(10, object.meal.targetId);
|
||||
fbb.addInt64(11, object.mealSource.targetId);
|
||||
fbb.addInt64(12, object.mealCategory.targetId);
|
||||
fbb.addInt64(13, object.mealPortionType.targetId);
|
||||
fbb.addInt64(14, object.portionSizeAccuracy.targetId);
|
||||
fbb.addInt64(15, object.carbsRatioAccuracy.targetId);
|
||||
fbb.addBool(16, object.deleted);
|
||||
fbb.addFloat64(17, object.amount);
|
||||
fbb.addFloat64(18, object.totalCarbs);
|
||||
fbb.finish(fbb.endTable());
|
||||
return object.id;
|
||||
},
|
||||
objectFromFB: (Store store, ByteData fbData) {
|
||||
final buffer = fb.BufferContext(fbData);
|
||||
final rootOffset = buffer.derefObject(0);
|
||||
|
||||
final object = Accuracy(
|
||||
id: const fb.Int64Reader().vTableGet(buffer, rootOffset, 4, 0),
|
||||
deleted: const fb.BoolReader()
|
||||
.vTableGet(buffer, rootOffset, 36, false),
|
||||
|
@ -188,7 +188,7 @@ class _LogEventDetailScreenState extends State<LogEventDetailScreen> {
|
||||
}
|
||||
|
||||
Future<void> checkIfActiveEventOfTypeExistsBeforeSaving() async {
|
||||
if (_isNew && _eventType != null &&
|
||||
if (_eventType != null &&
|
||||
LogEvent.eventTypeExistsForTime(_eventType!.id, _time)) {
|
||||
await showDialog(
|
||||
context: context,
|
366
lib/screens/log/log_event/log_event_list.dart
Normal file
366
lib/screens/log/log_event/log_event_list.dart
Normal file
@ -0,0 +1,366 @@
|
||||
import 'package:diameter/utils/dialog_utils.dart';
|
||||
import 'package:diameter/models/log_event.dart';
|
||||
import 'package:diameter/models/settings.dart';
|
||||
import 'package:diameter/screens/log/log_event/log_event_detail.dart';
|
||||
import 'package:diameter/utils/date_time_utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:diameter/navigation.dart';
|
||||
|
||||
class LogEventListScreen extends StatefulWidget {
|
||||
static const String routeName = '/log-events';
|
||||
const LogEventListScreen({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
_LogEventListScreenState createState() => _LogEventListScreenState();
|
||||
}
|
||||
|
||||
class _LogEventListScreenState extends State<LogEventListScreen> {
|
||||
List<LogEvent> _activeEvents = [];
|
||||
late List<LogEvent> _logEvents;
|
||||
|
||||
final ScrollController _scrollController = ScrollController();
|
||||
|
||||
final TextEditingController _dateController = TextEditingController(text: '');
|
||||
|
||||
late DateTime _date;
|
||||
bool _showActive = true;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
_date = DateTime.now();
|
||||
_dateController.text = DateTimeUtils.displayDate(_date);
|
||||
|
||||
reload();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_scrollController.dispose();
|
||||
_dateController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void reload({String? message}) {
|
||||
setState(() {
|
||||
_activeEvents = LogEvent.getAllActiveForTime(DateTime.now());
|
||||
_logEvents = LogEvent.getAllForDate(_date);
|
||||
});
|
||||
|
||||
setState(() {
|
||||
if (message != null) {
|
||||
var snackBar = SnackBar(
|
||||
content: Text(message),
|
||||
duration: const Duration(seconds: 2),
|
||||
);
|
||||
ScaffoldMessenger.of(context)
|
||||
..removeCurrentSnackBar()
|
||||
..showSnackBar(snackBar);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void handleAddNewEvent() async {
|
||||
final now = DateTime.now();
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) {
|
||||
return LogEventDetailScreen(
|
||||
suggestedDate: _date.isAtSameMomentAs(DateTime(now.year, now.month, now.day)) ? now : _date,
|
||||
);
|
||||
},
|
||||
),
|
||||
).then((result) => reload(message: result?[0]));
|
||||
}
|
||||
|
||||
void handleEditAction(LogEvent event) {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => LogEventDetailScreen(
|
||||
id: event.id,
|
||||
),
|
||||
),
|
||||
).then((result) => reload(message: result?[0]));
|
||||
}
|
||||
|
||||
void onDelete(LogEvent logEvent) {
|
||||
LogEvent.remove(logEvent.id);
|
||||
reload(message: 'Event deleted');
|
||||
}
|
||||
|
||||
void handleDeleteAction(LogEvent logEvent) async {
|
||||
if (Settings.get().showConfirmationDialogOnDelete) {
|
||||
DialogUtils.showConfirmationDialog(
|
||||
context: context,
|
||||
onConfirm: () => onDelete(logEvent),
|
||||
message: 'Are you sure you want to delete this Event?',
|
||||
);
|
||||
} else {
|
||||
onDelete(logEvent);
|
||||
}
|
||||
}
|
||||
|
||||
void onStop(LogEvent event) async {
|
||||
event.endTime = DateTime.now();
|
||||
LogEvent.put(event);
|
||||
reload(message: 'Event ended');
|
||||
}
|
||||
|
||||
void handleStopAction(LogEvent event) async {
|
||||
if (Settings.get().showConfirmationDialogOnStopEvent) {
|
||||
DialogUtils.showConfirmationDialog(
|
||||
context: context,
|
||||
onConfirm: () => onStop(event),
|
||||
message: 'Are you sure you want to end this Event?',
|
||||
confirmationLabel: 'END EVENT',
|
||||
);
|
||||
} else {
|
||||
onStop(event);
|
||||
}
|
||||
}
|
||||
|
||||
void onChangeDate(DateTime? date) {
|
||||
if (date != null) {
|
||||
setState(() {
|
||||
_date = DateTime(date.year, date.month, date.day);
|
||||
_dateController.text = DateTimeUtils.displayDate(date);
|
||||
});
|
||||
reload();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Log Events'),
|
||||
actions: <Widget>[
|
||||
IconButton(
|
||||
onPressed: () => onChangeDate(DateTime.now()),
|
||||
icon: const Icon(Icons.today)),
|
||||
IconButton(onPressed: reload, icon: const Icon(Icons.refresh))
|
||||
],
|
||||
),
|
||||
drawer: const Navigation(currentLocation: LogEventListScreen.routeName),
|
||||
body: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: () => setState(() {
|
||||
_showActive = !_showActive;
|
||||
}),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
'ACTIVE EVENTS',
|
||||
style: Theme.of(context).textTheme.subtitle2,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
Icon(_showActive
|
||||
? Icons.expand_less
|
||||
: Icons.expand_more),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
!_showActive ? Container() :
|
||||
_activeEvents.isNotEmpty
|
||||
? ListView.builder(
|
||||
shrinkWrap: true,
|
||||
padding: const EdgeInsets.all(10.0),
|
||||
itemCount: _activeEvents.length,
|
||||
itemBuilder: (context, index) {
|
||||
LogEvent event = _activeEvents[index];
|
||||
return Card(
|
||||
child: ListTile(
|
||||
onTap: () {
|
||||
handleEditAction(event);
|
||||
},
|
||||
title: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
DateTimeUtils.displayDateTime(event.time),
|
||||
),
|
||||
const SizedBox(width: 24),
|
||||
Expanded(
|
||||
child: Text(
|
||||
(event.title ??
|
||||
event.eventType.target?.value ??
|
||||
'')
|
||||
.toUpperCase(),
|
||||
style: Theme.of(context).textTheme.subtitle2,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
trailing: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
event.hasEndTime && event.endTime == null
|
||||
? IconButton(
|
||||
icon: const Icon(
|
||||
Icons.stop,
|
||||
color: Colors.blue,
|
||||
),
|
||||
onPressed: () => handleStopAction(event),
|
||||
)
|
||||
: const SizedBox(width: 50),
|
||||
IconButton(
|
||||
icon: const Icon(
|
||||
Icons.edit,
|
||||
color: Colors.blue,
|
||||
),
|
||||
onPressed: () => handleEditAction(event),
|
||||
),
|
||||
IconButton(
|
||||
icon: const Icon(
|
||||
Icons.delete,
|
||||
color: Colors.blue,
|
||||
),
|
||||
onPressed: () => handleDeleteAction(event),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
)
|
||||
: const Center(
|
||||
child: Text('There are no Active Events!'),
|
||||
),
|
||||
const Padding(
|
||||
padding: EdgeInsets.all(10.0),
|
||||
child: Divider(),
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
IconButton(
|
||||
onPressed: _date.isAtSameMomentAs(DateTime(2000, 1, 1))
|
||||
? null
|
||||
: () =>
|
||||
onChangeDate(_date.subtract(const Duration(days: 1))),
|
||||
icon: const Icon(Icons.arrow_back),
|
||||
),
|
||||
Expanded(
|
||||
child: GestureDetector(
|
||||
onTap: () async {
|
||||
final newTime = await showDatePicker(
|
||||
context: context,
|
||||
initialDate: _date,
|
||||
firstDate: DateTime(2000, 1, 1),
|
||||
lastDate: DateTime.now().add(const Duration(days: 365)),
|
||||
);
|
||||
onChangeDate(newTime);
|
||||
},
|
||||
child: Expanded(
|
||||
child: Text(
|
||||
DateTimeUtils.displayDate(_date).toUpperCase(),
|
||||
style: Theme.of(context).textTheme.subtitle2,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
IconButton(
|
||||
onPressed:
|
||||
_date.add(const Duration(days: 1)).isBefore(DateTime.now())
|
||||
? () => onChangeDate(_date.add(const Duration(days: 1)))
|
||||
: null,
|
||||
icon: const Icon(Icons.arrow_forward),
|
||||
),
|
||||
],
|
||||
),
|
||||
Expanded(
|
||||
child: _logEvents.isNotEmpty
|
||||
? Scrollbar(
|
||||
controller: _scrollController,
|
||||
child: ListView.builder(
|
||||
controller: _scrollController,
|
||||
shrinkWrap: true,
|
||||
padding: const EdgeInsets.all(10.0),
|
||||
itemCount: _logEvents.length,
|
||||
itemBuilder: (context, index) {
|
||||
LogEvent event = _logEvents[index];
|
||||
return Card(
|
||||
child: ListTile(
|
||||
onTap: () {
|
||||
handleEditAction(event);
|
||||
},
|
||||
title: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
DateTimeUtils.displayTime(event.isEndEvent
|
||||
? event.endTime
|
||||
: event.time),
|
||||
),
|
||||
const SizedBox(width: 24),
|
||||
Expanded(
|
||||
child: Text(
|
||||
(event.title ??
|
||||
event.eventType.target?.value ??
|
||||
'')
|
||||
.toUpperCase(),
|
||||
style:
|
||||
Theme.of(context).textTheme.subtitle2,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
trailing: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
event.hasEndTime && event.endTime == null
|
||||
? IconButton(
|
||||
icon: const Icon(
|
||||
Icons.stop,
|
||||
color: Colors.blue,
|
||||
),
|
||||
onPressed: () =>
|
||||
handleStopAction(event),
|
||||
)
|
||||
: const SizedBox(width: 50),
|
||||
IconButton(
|
||||
icon: const Icon(
|
||||
Icons.edit,
|
||||
color: Colors.blue,
|
||||
),
|
||||
onPressed: () => handleEditAction(event),
|
||||
),
|
||||
IconButton(
|
||||
icon: const Icon(
|
||||
Icons.delete,
|
||||
color: Colors.blue,
|
||||
),
|
||||
onPressed: () => handleDeleteAction(event),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
))
|
||||
: const Center(
|
||||
child: Text('There are no Events for that date!'),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
floatingActionButton: FloatingActionButton(
|
||||
onPressed: handleAddNewEvent,
|
||||
child: const Icon(Icons.add),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
304
lib/screens/log/log_event/log_event_type_detail.dart
Normal file
304
lib/screens/log/log_event/log_event_type_detail.dart
Normal file
@ -0,0 +1,304 @@
|
||||
import 'package:diameter/components/detail.dart';
|
||||
import 'package:diameter/components/forms/boolean_form_field.dart';
|
||||
import 'package:diameter/components/forms/duration_form_field.dart';
|
||||
import 'package:diameter/utils/dialog_utils.dart';
|
||||
import 'package:diameter/components/forms/auto_complete_dropdown_button.dart';
|
||||
import 'package:diameter/components/forms/form_wrapper.dart';
|
||||
import 'package:diameter/models/basal_profile.dart';
|
||||
import 'package:diameter/models/bolus_profile.dart';
|
||||
import 'package:diameter/models/log_event_type.dart';
|
||||
import 'package:diameter/models/settings.dart';
|
||||
import 'package:diameter/navigation.dart';
|
||||
import 'package:diameter/screens/basal/basal_profile_detail.dart';
|
||||
import 'package:diameter/screens/bolus/bolus_profile_detail.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class EventTypeDetailScreen extends StatefulWidget {
|
||||
static const String routeName = '/log-event-type';
|
||||
final int id;
|
||||
const EventTypeDetailScreen({Key? key, this.id = 0}) : super(key: key);
|
||||
|
||||
@override
|
||||
_EventTypeDetailScreenState createState() => _EventTypeDetailScreenState();
|
||||
}
|
||||
|
||||
class _EventTypeDetailScreenState extends State<EventTypeDetailScreen> {
|
||||
LogEventType? _logEventType;
|
||||
bool _isNew = true;
|
||||
bool _isSaving = false;
|
||||
|
||||
List<BolusProfile> _bolusProfiles = [];
|
||||
List<BasalProfile> _basalProfiles = [];
|
||||
|
||||
final GlobalKey<FormState> _logEventTypeForm = GlobalKey<FormState>();
|
||||
final ScrollController _scrollController = ScrollController();
|
||||
|
||||
final _valueController = TextEditingController(text: '');
|
||||
final _notesController = TextEditingController(text: '');
|
||||
|
||||
bool _hasEndTime = false;
|
||||
int _defaultReminderDuration = 0;
|
||||
BolusProfile? _bolusProfile;
|
||||
BasalProfile? _basalProfile;
|
||||
final _bolusProfileController = TextEditingController(text: '');
|
||||
final _basalProfileController = TextEditingController(text: '');
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
reload();
|
||||
|
||||
_bolusProfiles = BolusProfile.getAll();
|
||||
_basalProfiles = BasalProfile.getAll();
|
||||
|
||||
if (_logEventType != null) {
|
||||
_valueController.text = _logEventType!.value;
|
||||
_defaultReminderDuration =
|
||||
_logEventType!.defaultReminderDuration ?? 0;
|
||||
_hasEndTime = _logEventType!.hasEndTime;
|
||||
_notesController.text = _logEventType!.notes ?? '';
|
||||
_basalProfile = _logEventType!.basalProfile.target;
|
||||
_basalProfileController.text = (_basalProfile ?? '').toString();
|
||||
_bolusProfile = _logEventType!.bolusProfile.target;
|
||||
_bolusProfileController.text = (_bolusProfile ?? '').toString();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_scrollController.dispose();
|
||||
_valueController.dispose();
|
||||
_notesController.dispose();
|
||||
_bolusProfileController.dispose();
|
||||
_basalProfileController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void reload({String? message}) {
|
||||
if (widget.id != 0) {
|
||||
setState(() {
|
||||
_logEventType = LogEventType.get(widget.id);
|
||||
});
|
||||
}
|
||||
_isNew = _logEventType == null;
|
||||
|
||||
setState(() {
|
||||
if (message != null) {
|
||||
var snackBar = SnackBar(
|
||||
content: Text(message),
|
||||
duration: const Duration(seconds: 2),
|
||||
);
|
||||
ScaffoldMessenger.of(context)
|
||||
..removeCurrentSnackBar()
|
||||
..showSnackBar(snackBar);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void updateBasalProfile(BasalProfile? value) {
|
||||
setState(() {
|
||||
_basalProfile = value;
|
||||
_basalProfileController.text = (_basalProfile ?? '').toString();
|
||||
});
|
||||
}
|
||||
|
||||
void updateBolusProfile(BolusProfile? value) {
|
||||
setState(() {
|
||||
_bolusProfile = value;
|
||||
_bolusProfileController.text = (_bolusProfile ?? '').toString();
|
||||
});
|
||||
}
|
||||
|
||||
void handleSaveAction() async {
|
||||
setState(() {
|
||||
_isSaving = true;
|
||||
});
|
||||
if (_logEventTypeForm.currentState!.validate()) {
|
||||
LogEventType eventType = LogEventType(
|
||||
id: widget.id,
|
||||
value: _valueController.text,
|
||||
notes: _notesController.text,
|
||||
defaultReminderDuration: _defaultReminderDuration,
|
||||
hasEndTime: _hasEndTime,
|
||||
);
|
||||
eventType.basalProfile.target = _basalProfile;
|
||||
eventType.bolusProfile.target = _bolusProfile;
|
||||
LogEventType.put(eventType);
|
||||
Navigator.pop(
|
||||
context, ['${_isNew ? 'New' : ''} Log Event Type Saved', eventType]);
|
||||
}
|
||||
setState(() {
|
||||
_isSaving = false;
|
||||
});
|
||||
}
|
||||
|
||||
void handleCancelAction() {
|
||||
bool isNew = _logEventType == null;
|
||||
if (Settings.get().showConfirmationDialogOnCancel &&
|
||||
((isNew &&
|
||||
(_valueController.text != '' ||
|
||||
_defaultReminderDuration != 0 ||
|
||||
_notesController.text != '' ||
|
||||
_hasEndTime)) ||
|
||||
(!isNew &&
|
||||
(_valueController.text != _logEventType!.value ||
|
||||
_defaultReminderDuration !=
|
||||
_logEventType!.defaultReminderDuration ||
|
||||
_notesController.text != (_logEventType!.notes ?? '') ||
|
||||
_hasEndTime != _logEventType!.hasEndTime)))) {
|
||||
DialogUtils.showCancelConfirmationDialog(
|
||||
context: context,
|
||||
isNew: isNew,
|
||||
onSave: handleSaveAction,
|
||||
);
|
||||
} else {
|
||||
Navigator.pop(context);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(_isNew ? 'New Log Event Type' : _logEventType!.value),
|
||||
),
|
||||
drawer:
|
||||
const Navigation(currentLocation: EventTypeDetailScreen.routeName),
|
||||
body: Scrollbar(
|
||||
controller: _scrollController,
|
||||
child: SingleChildScrollView(
|
||||
controller: _scrollController,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: <Widget>[
|
||||
FormWrapper(formState: _logEventTypeForm, fields: [
|
||||
TextFormField(
|
||||
controller: _valueController,
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Name',
|
||||
),
|
||||
validator: (value) {
|
||||
if (value!.trim().isEmpty) {
|
||||
return 'Empty name';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
BooleanFormField(
|
||||
value: _hasEndTime,
|
||||
label: 'has end time',
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_hasEndTime = value;
|
||||
});
|
||||
},
|
||||
),
|
||||
Column(
|
||||
children: _hasEndTime
|
||||
? [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 10.0),
|
||||
child: DurationFormField(
|
||||
minutes: _defaultReminderDuration,
|
||||
label: 'Default Reminder Duration',
|
||||
onChanged: (value) => _defaultReminderDuration = value ?? 0,
|
||||
showSteppers: true,
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 10.0),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: AutoCompleteDropdownButton<
|
||||
BolusProfile>(
|
||||
selectedItem: _bolusProfile,
|
||||
controller: _bolusProfileController,
|
||||
label: 'Bolus Profile',
|
||||
items: _bolusProfiles,
|
||||
onChanged: updateBolusProfile,
|
||||
),
|
||||
),
|
||||
IconButton(
|
||||
onPressed: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => _bolusProfile ==
|
||||
null
|
||||
? const BolusProfileDetailScreen()
|
||||
: BolusProfileDetailScreen(
|
||||
id: _bolusProfile!.id),
|
||||
),
|
||||
).then((result) {
|
||||
setState(() {
|
||||
updateBolusProfile(result?[1]);
|
||||
});
|
||||
reload(message: result?[0]);
|
||||
});
|
||||
},
|
||||
icon: Icon(_bolusProfile == null
|
||||
? Icons.add
|
||||
: Icons.edit),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child:
|
||||
AutoCompleteDropdownButton<BasalProfile>(
|
||||
controller: _basalProfileController,
|
||||
selectedItem: _basalProfile,
|
||||
label: 'Basal Profile',
|
||||
items: _basalProfiles,
|
||||
onChanged: updateBasalProfile,
|
||||
),
|
||||
),
|
||||
IconButton(
|
||||
onPressed: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => _basalProfile ==
|
||||
null
|
||||
? const BasalProfileDetailScreen()
|
||||
: BasalProfileDetailScreen(
|
||||
id: _basalProfile!.id),
|
||||
),
|
||||
).then((result) {
|
||||
updateBasalProfile(result?[1]);
|
||||
reload(message: result?[0]);
|
||||
});
|
||||
},
|
||||
icon: Icon(_basalProfile == null
|
||||
? Icons.add
|
||||
: Icons.edit),
|
||||
),
|
||||
],
|
||||
),
|
||||
]
|
||||
: []),
|
||||
TextFormField(
|
||||
controller: _notesController,
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Notes',
|
||||
),
|
||||
keyboardType: TextInputType.multiline,
|
||||
minLines: 2,
|
||||
maxLines: 5,
|
||||
),
|
||||
]),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
bottomNavigationBar: DetailBottomRow(
|
||||
onCancel: handleCancelAction,
|
||||
onAction: _isSaving ? null : handleSaveAction,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
123
lib/screens/log/log_event/log_event_type_list.dart
Normal file
123
lib/screens/log/log_event/log_event_type_list.dart
Normal file
@ -0,0 +1,123 @@
|
||||
import 'package:diameter/models/log_event_type.dart';
|
||||
import 'package:diameter/navigation.dart';
|
||||
import 'package:diameter/screens/log/log_event/log_event_type_detail.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class LogEventTypeListScreen extends StatefulWidget {
|
||||
static const String routeName = '/log-event-types';
|
||||
const LogEventTypeListScreen({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
_LogEventTypeListScreenState createState() => _LogEventTypeListScreenState();
|
||||
}
|
||||
|
||||
class _LogEventTypeListScreenState extends State<LogEventTypeListScreen> {
|
||||
List<LogEventType> _logEventTypes = [];
|
||||
|
||||
final ScrollController _scrollController = ScrollController();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
reload();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_scrollController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void reload({String? message}) {
|
||||
setState(() {
|
||||
_logEventTypes = LogEventType.getAll();
|
||||
});
|
||||
setState(() {
|
||||
if (message != null) {
|
||||
var snackBar = SnackBar(
|
||||
content: Text(message),
|
||||
duration: const Duration(seconds: 2),
|
||||
);
|
||||
ScaffoldMessenger.of(context)
|
||||
..removeCurrentSnackBar()
|
||||
..showSnackBar(snackBar);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: const Text('Log Event Types'), actions: <Widget>[
|
||||
IconButton(onPressed: reload, icon: const Icon(Icons.refresh))
|
||||
]),
|
||||
drawer:
|
||||
const Navigation(currentLocation: LogEventTypeListScreen.routeName),
|
||||
body: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: _logEventTypes.isNotEmpty
|
||||
? Scrollbar(
|
||||
controller: _scrollController,
|
||||
child: ListView.builder(
|
||||
controller: _scrollController,
|
||||
padding: const EdgeInsets.all(10.0),
|
||||
itemCount: _logEventTypes.length,
|
||||
itemBuilder: (context, index) {
|
||||
final logEventType = _logEventTypes[index];
|
||||
return Card(
|
||||
child: ListTile(
|
||||
onTap: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => EventTypeDetailScreen(
|
||||
id: logEventType.id),
|
||||
),
|
||||
).then((result) => reload(message: result?[0]));
|
||||
},
|
||||
title: Text(
|
||||
logEventType.value.toUpperCase(),
|
||||
style: Theme.of(context).textTheme.subtitle2,
|
||||
),
|
||||
subtitle: Text(logEventType.notes ?? ''),
|
||||
trailing: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
IconButton(
|
||||
onPressed: () async {
|
||||
LogEventType.remove(logEventType.id);
|
||||
reload(message: 'Log Event Type deleted');
|
||||
},
|
||||
icon: const Icon(Icons.delete,
|
||||
color: Colors.blue),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
)
|
||||
: const Center(
|
||||
child:
|
||||
Text('You have not created any Log Event Types yet!'),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
floatingActionButton: FloatingActionButton(
|
||||
onPressed: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => const EventTypeDetailScreen(),
|
||||
),
|
||||
).then((result) => reload(message: result?[0]));
|
||||
},
|
||||
child: const Icon(Icons.add),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -1,386 +0,0 @@
|
||||
import 'package:diameter/utils/dialog_utils.dart';
|
||||
import 'package:diameter/models/log_event.dart';
|
||||
import 'package:diameter/models/settings.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/navigation.dart';
|
||||
|
||||
class LogEventListScreen extends StatefulWidget {
|
||||
static const String routeName = '/log-events';
|
||||
const LogEventListScreen({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
_LogEventListScreenState createState() => _LogEventListScreenState();
|
||||
}
|
||||
|
||||
class _LogEventListScreenState extends State<LogEventListScreen> {
|
||||
List<LogEvent> _activeEvents = [];
|
||||
late List<LogEvent> _logEvents;
|
||||
|
||||
final ScrollController _scrollController = ScrollController();
|
||||
|
||||
final TextEditingController _dateController = TextEditingController(text: '');
|
||||
|
||||
late DateTime _date;
|
||||
bool _showActive = true;
|
||||
String? swipeDirection;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
_date = DateTime.now();
|
||||
_dateController.text = DateTimeUtils.displayDate(_date);
|
||||
|
||||
reload();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_scrollController.dispose();
|
||||
_dateController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void reload({String? message}) {
|
||||
setState(() {
|
||||
_activeEvents = LogEvent.getAllActiveForTime(DateTime.now());
|
||||
_logEvents = LogEvent.getAllForDate(_date);
|
||||
});
|
||||
|
||||
setState(() {
|
||||
if (message != null) {
|
||||
var snackBar = SnackBar(
|
||||
content: Text(message),
|
||||
duration: const Duration(seconds: 2),
|
||||
);
|
||||
ScaffoldMessenger.of(context)
|
||||
..removeCurrentSnackBar()
|
||||
..showSnackBar(snackBar);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void handleAddNewEvent() async {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) {
|
||||
return _date.isAtSameMomentAs(DateTimeUtils.today())
|
||||
? const LogEventDetailScreen()
|
||||
: LogEventDetailScreen(
|
||||
suggestedDate: _date,
|
||||
);
|
||||
},
|
||||
),
|
||||
).then((result) => reload(message: result?[0]));
|
||||
}
|
||||
|
||||
void handleEditAction(LogEvent event) {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => LogEventDetailScreen(
|
||||
id: event.id,
|
||||
),
|
||||
),
|
||||
).then((result) => reload(message: result?[0]));
|
||||
}
|
||||
|
||||
void onDelete(LogEvent logEvent) {
|
||||
LogEvent.remove(logEvent.id);
|
||||
reload(message: 'Event deleted');
|
||||
}
|
||||
|
||||
void handleDeleteAction(LogEvent logEvent) async {
|
||||
if (Settings.get().showConfirmationDialogOnDelete) {
|
||||
DialogUtils.showConfirmationDialog(
|
||||
context: context,
|
||||
onConfirm: () => onDelete(logEvent),
|
||||
message: 'Are you sure you want to delete this Event?',
|
||||
);
|
||||
} else {
|
||||
onDelete(logEvent);
|
||||
}
|
||||
}
|
||||
|
||||
void onStop(LogEvent event) async {
|
||||
event.endTime = DateTime.now();
|
||||
LogEvent.put(event);
|
||||
reload(message: 'Event ended');
|
||||
}
|
||||
|
||||
void handleStopAction(LogEvent event) async {
|
||||
if (Settings.get().showConfirmationDialogOnStopEvent) {
|
||||
DialogUtils.showConfirmationDialog(
|
||||
context: context,
|
||||
onConfirm: () => onStop(event),
|
||||
message: 'Are you sure you want to end this Event?',
|
||||
confirmationLabel: 'END EVENT',
|
||||
);
|
||||
} else {
|
||||
onStop(event);
|
||||
}
|
||||
}
|
||||
|
||||
void onChangeDate(DateTime? date) {
|
||||
if (date != null) {
|
||||
setState(() {
|
||||
_date = DateTime(date.year, date.month, date.day);
|
||||
_dateController.text = DateTimeUtils.displayDate(date);
|
||||
});
|
||||
reload();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Log Events'),
|
||||
actions: <Widget>[
|
||||
IconButton(
|
||||
onPressed: () => onChangeDate(DateTime.now()),
|
||||
icon: const Icon(Icons.today)),
|
||||
IconButton(onPressed: reload, icon: const Icon(Icons.refresh))
|
||||
],
|
||||
),
|
||||
drawer: const Navigation(currentLocation: LogEventListScreen.routeName),
|
||||
body: GestureDetector(
|
||||
onPanUpdate: (details) {
|
||||
swipeDirection = details.delta.dx < 0 ? 'left' : 'right';
|
||||
},
|
||||
onPanEnd: (details) {
|
||||
if (swipeDirection == null) {
|
||||
return;
|
||||
}
|
||||
if (swipeDirection == 'right' &&
|
||||
!_date.isAtSameMomentAs(DateTime(2000, 1, 1))) {
|
||||
onChangeDate(_date.subtract(const Duration(days: 1)));
|
||||
}
|
||||
if (swipeDirection == 'left' &&
|
||||
_date.add(const Duration(days: 1)).isBefore(DateTime.now())) {
|
||||
onChangeDate(_date.add(const Duration(days: 1)));
|
||||
}
|
||||
},
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: () => setState(() {
|
||||
_showActive = !_showActive;
|
||||
}),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
'ACTIVE EVENTS',
|
||||
style: Theme.of(context).textTheme.subtitle2,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
Icon(_showActive ? Icons.expand_less : Icons.expand_more),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
!_showActive
|
||||
? Container()
|
||||
: _activeEvents.isNotEmpty
|
||||
? ListView.builder(
|
||||
shrinkWrap: true,
|
||||
padding: const EdgeInsets.all(10.0),
|
||||
itemCount: _activeEvents.length,
|
||||
itemBuilder: (context, index) {
|
||||
LogEvent event = _activeEvents[index];
|
||||
return Card(
|
||||
child: ListTile(
|
||||
onTap: () {
|
||||
handleEditAction(event);
|
||||
},
|
||||
title: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
DateTimeUtils.displayDateTime(event.time),
|
||||
),
|
||||
const SizedBox(width: 24),
|
||||
Expanded(
|
||||
child: Text(
|
||||
(event.title ??
|
||||
event.eventType.target?.value ??
|
||||
'')
|
||||
.toUpperCase(),
|
||||
style:
|
||||
Theme.of(context).textTheme.subtitle2,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
trailing: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
event.hasEndTime && event.endTime == null
|
||||
? IconButton(
|
||||
icon: const Icon(
|
||||
Icons.stop,
|
||||
color: Colors.blue,
|
||||
),
|
||||
onPressed: () =>
|
||||
handleStopAction(event),
|
||||
)
|
||||
: const SizedBox(width: 50),
|
||||
IconButton(
|
||||
icon: const Icon(
|
||||
Icons.edit,
|
||||
color: Colors.blue,
|
||||
),
|
||||
onPressed: () => handleEditAction(event),
|
||||
),
|
||||
IconButton(
|
||||
icon: const Icon(
|
||||
Icons.delete,
|
||||
color: Colors.blue,
|
||||
),
|
||||
onPressed: () => handleDeleteAction(event),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
)
|
||||
: const Center(
|
||||
child: Text('There are no Active Events!'),
|
||||
),
|
||||
const Padding(
|
||||
padding: EdgeInsets.all(10.0),
|
||||
child: Divider(),
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
IconButton(
|
||||
onPressed: _date.isAtSameMomentAs(DateTime(2000, 1, 1))
|
||||
? null
|
||||
: () =>
|
||||
onChangeDate(_date.subtract(const Duration(days: 1))),
|
||||
icon: const Icon(Icons.arrow_back),
|
||||
),
|
||||
Expanded(
|
||||
child: GestureDetector(
|
||||
onTap: () async {
|
||||
final newTime = await showDatePicker(
|
||||
context: context,
|
||||
initialDate: _date,
|
||||
firstDate: DateTime(2000, 1, 1),
|
||||
lastDate: DateTime.now().add(const Duration(days: 365)),
|
||||
);
|
||||
onChangeDate(newTime);
|
||||
},
|
||||
child: Text(
|
||||
DateTimeUtils.displayDate(_date).toUpperCase(),
|
||||
style: Theme.of(context).textTheme.subtitle2,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
),
|
||||
IconButton(
|
||||
onPressed: _date
|
||||
.add(const Duration(days: 1))
|
||||
.isBefore(DateTime.now())
|
||||
? () => onChangeDate(_date.add(const Duration(days: 1)))
|
||||
: null,
|
||||
icon: const Icon(Icons.arrow_forward),
|
||||
),
|
||||
],
|
||||
),
|
||||
Expanded(
|
||||
child: _logEvents.isNotEmpty
|
||||
? Scrollbar(
|
||||
controller: _scrollController,
|
||||
child: ListView.builder(
|
||||
controller: _scrollController,
|
||||
shrinkWrap: true,
|
||||
padding: const EdgeInsets.all(10.0),
|
||||
itemCount: _logEvents.length,
|
||||
itemBuilder: (context, index) {
|
||||
LogEvent event = _logEvents[index];
|
||||
return Card(
|
||||
child: ListTile(
|
||||
onTap: () {
|
||||
handleEditAction(event);
|
||||
},
|
||||
title: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
DateTimeUtils.displayTime(event.isEndEvent
|
||||
? event.endTime
|
||||
: event.time),
|
||||
),
|
||||
const SizedBox(width: 24),
|
||||
Expanded(
|
||||
child: Text(
|
||||
(event.title ??
|
||||
event.eventType.target?.value ??
|
||||
'')
|
||||
.toUpperCase(),
|
||||
style:
|
||||
Theme.of(context).textTheme.subtitle2,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
trailing: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
event.hasEndTime && event.endTime == null
|
||||
? IconButton(
|
||||
icon: const Icon(
|
||||
Icons.stop,
|
||||
color: Colors.blue,
|
||||
),
|
||||
onPressed: () =>
|
||||
handleStopAction(event),
|
||||
)
|
||||
: const SizedBox(width: 50),
|
||||
IconButton(
|
||||
icon: const Icon(
|
||||
Icons.edit,
|
||||
color: Colors.blue,
|
||||
),
|
||||
onPressed: () => handleEditAction(event),
|
||||
),
|
||||
IconButton(
|
||||
icon: const Icon(
|
||||
Icons.delete,
|
||||
color: Colors.blue,
|
||||
),
|
||||
onPressed: () => handleDeleteAction(event),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
))
|
||||
: const Center(
|
||||
child: Text('There are no Events for that date!'),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
floatingActionButton: FloatingActionButton(
|
||||
onPressed: handleAddNewEvent,
|
||||
child: const Icon(Icons.add),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
322
lib/screens/recipe/recipe_detail.dart
Normal file
322
lib/screens/recipe/recipe_detail.dart
Normal file
@ -0,0 +1,322 @@
|
||||
import 'package:diameter/components/detail.dart';
|
||||
import 'package:diameter/utils/dialog_utils.dart';
|
||||
import 'package:diameter/components/forms/auto_complete_dropdown_button.dart';
|
||||
import 'package:diameter/components/forms/form_wrapper.dart';
|
||||
import 'package:diameter/models/ingredient.dart';
|
||||
import 'package:diameter/models/meal.dart';
|
||||
import 'package:diameter/models/recipe.dart';
|
||||
import 'package:diameter/models/settings.dart';
|
||||
import 'package:diameter/navigation.dart';
|
||||
import 'package:diameter/screens/meal/meal_detail.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class RecipeDetailScreen extends StatefulWidget {
|
||||
static const String routeName = '/recipe';
|
||||
final int id;
|
||||
|
||||
const RecipeDetailScreen({Key? key, this.id = 0}) : super(key: key);
|
||||
|
||||
@override
|
||||
_RecipeDetailScreenState createState() => _RecipeDetailScreenState();
|
||||
}
|
||||
|
||||
class _RecipeDetailScreenState extends State<RecipeDetailScreen> {
|
||||
Recipe? _recipe;
|
||||
List<Ingredient> _ingredients = [];
|
||||
|
||||
bool _isNew = true;
|
||||
bool _isSaving = false;
|
||||
|
||||
final GlobalKey<FormState> _recipeForm = GlobalKey<FormState>();
|
||||
final ScrollController _scrollController = ScrollController();
|
||||
|
||||
final _nameController = TextEditingController(text: '');
|
||||
final _notesController = TextEditingController(text: '');
|
||||
|
||||
double _servings = 1;
|
||||
|
||||
final List<TextEditingController> _ingredientControllers = [];
|
||||
|
||||
List<Meal> _meals = [];
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
reload();
|
||||
|
||||
_meals = Meal.getAll();
|
||||
|
||||
if (_recipe != null) {
|
||||
_nameController.text = _recipe!.name;
|
||||
_servings = _recipe!.servings ?? 1;
|
||||
_notesController.text = _recipe!.notes ?? '';
|
||||
|
||||
if (_ingredients.isNotEmpty) {
|
||||
for (Ingredient ingredient in _ingredients) {
|
||||
_ingredientControllers.add(
|
||||
TextEditingController(text: ingredient.ingredient.target?.value));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void reload({String? message}) {
|
||||
if (widget.id != 0) {
|
||||
setState(() {
|
||||
_recipe = Recipe.get(widget.id);
|
||||
_ingredients = Ingredient.getAllForRecipe(widget.id);
|
||||
});
|
||||
}
|
||||
_isNew = _recipe == null;
|
||||
|
||||
setState(() {
|
||||
if (message != null) {
|
||||
var snackBar = SnackBar(
|
||||
content: Text(message),
|
||||
duration: const Duration(seconds: 2),
|
||||
);
|
||||
ScaffoldMessenger.of(context)
|
||||
..removeCurrentSnackBar()
|
||||
..showSnackBar(snackBar);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void onAddIngredient() {
|
||||
final newIngredient = Ingredient(amount: 0);
|
||||
setState(() {
|
||||
newIngredient.recipe.target = _recipe;
|
||||
_ingredients.add(newIngredient);
|
||||
_ingredientControllers.add(TextEditingController(text: ''));
|
||||
});
|
||||
}
|
||||
|
||||
void handleSaveAction({bool close = false}) async {
|
||||
setState(() {
|
||||
_isSaving = true;
|
||||
});
|
||||
if (_recipeForm.currentState!.validate()) {
|
||||
Recipe recipe = Recipe(
|
||||
id: widget.id,
|
||||
name: _nameController.text,
|
||||
servings: _servings,
|
||||
notes: _notesController.text,
|
||||
);
|
||||
Recipe.put(recipe);
|
||||
List<Ingredient> ingredients = _ingredients.map((ingredient) {
|
||||
if (ingredient.id != 0 &&
|
||||
(!ingredient.ingredient.hasValue || ingredient.amount == 0)) {
|
||||
ingredient.deleted = true;
|
||||
}
|
||||
return ingredient;
|
||||
}).toList();
|
||||
ingredients.retainWhere((ingredient) {
|
||||
return ingredient.id != 0 ||
|
||||
(ingredient.amount > 0 && ingredient.ingredient.hasValue);
|
||||
});
|
||||
Ingredient.putMany(ingredients);
|
||||
|
||||
if (close) {
|
||||
Navigator.pop(context, ['${_isNew ? 'New' : ''} Recipe Saved', recipe]);
|
||||
} else {
|
||||
if (_isNew) {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => RecipeDetailScreen(id: recipe.id),
|
||||
),
|
||||
).then((result) => Navigator.pop(context, result));
|
||||
} else {
|
||||
reload(message: 'Recipe saved');
|
||||
}
|
||||
}
|
||||
|
||||
setState(() {
|
||||
_isSaving = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void handleCancelAction() {
|
||||
if (Settings.get().showConfirmationDialogOnCancel &&
|
||||
((_isNew &&
|
||||
(_nameController.text != '' ||
|
||||
_servings != 1 ||
|
||||
_notesController.text != '')) ||
|
||||
(!_isNew &&
|
||||
(_nameController.text != _recipe!.name ||
|
||||
_servings != _recipe!.servings ||
|
||||
_notesController.text != (_recipe!.notes ?? ''))))) {
|
||||
DialogUtils.showCancelConfirmationDialog(
|
||||
context: context,
|
||||
isNew: _isNew,
|
||||
onSave: handleSaveAction,
|
||||
);
|
||||
} else {
|
||||
Navigator.pop(context);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(_isNew ? 'New Recipe' : _recipe!.name),
|
||||
),
|
||||
drawer: const Navigation(currentLocation: RecipeDetailScreen.routeName),
|
||||
body: Scrollbar(
|
||||
controller: _scrollController,
|
||||
child: SingleChildScrollView(
|
||||
controller: _scrollController,
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
FormWrapper(
|
||||
formState: _recipeForm,
|
||||
fields: [
|
||||
TextFormField(
|
||||
controller: _nameController,
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Name',
|
||||
),
|
||||
validator: (value) {
|
||||
if (value!.trim().isEmpty) {
|
||||
return 'Empty title';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
// NumberFormField(
|
||||
// value: _servings,
|
||||
// label: 'Servings',
|
||||
// suffix: ' portions',
|
||||
// min: 0,
|
||||
// onChanged: (value) {
|
||||
// if (value != null && value >= 0) {
|
||||
// setState(() {
|
||||
// _servings = value.toDouble();
|
||||
// });
|
||||
// }
|
||||
// },
|
||||
// ),
|
||||
TextFormField(
|
||||
keyboardType: TextInputType.multiline,
|
||||
controller: _notesController,
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Notes',
|
||||
),
|
||||
minLines: 2,
|
||||
maxLines: 5,
|
||||
),
|
||||
const Divider(),
|
||||
GestureDetector(
|
||||
onTap: onAddIngredient,
|
||||
child: Row(
|
||||
children: [
|
||||
Text(
|
||||
'INGREDIENTS',
|
||||
style: Theme.of(context).textTheme.subtitle2,
|
||||
),
|
||||
const Spacer(),
|
||||
IconButton(
|
||||
onPressed: onAddIngredient,
|
||||
icon: const Icon(Icons.add),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
!_isNew && _ingredients.isNotEmpty
|
||||
? ListBody(
|
||||
children: _ingredients.map((item) {
|
||||
final ingredient = item.ingredient.target;
|
||||
final index = _ingredients.indexOf(item);
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 10.0, vertical: 5.0),
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: AutoCompleteDropdownButton<Meal>(
|
||||
controller: _ingredientControllers[index],
|
||||
selectedItem: ingredient,
|
||||
label: 'Meal Category',
|
||||
items: _meals,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_ingredients[index]
|
||||
.ingredient
|
||||
.target = value;
|
||||
_ingredientControllers[index].text =
|
||||
value?.value ?? '';
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
IconButton(
|
||||
onPressed: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) =>
|
||||
ingredient == null
|
||||
? const MealDetailScreen()
|
||||
: MealDetailScreen(
|
||||
id: ingredient.id),
|
||||
),
|
||||
).then((result) {
|
||||
_ingredients[index].ingredient.target =
|
||||
result?[1];
|
||||
_ingredientControllers[index].text =
|
||||
result?[1].value ?? '';
|
||||
reload(message: result?[0]);
|
||||
});
|
||||
},
|
||||
icon: Icon(ingredient == null
|
||||
? Icons.add
|
||||
: Icons.edit),
|
||||
),
|
||||
],
|
||||
),
|
||||
// Padding(
|
||||
// padding: const EdgeInsets.only(top: 10.0),
|
||||
// child: NumberFormField(
|
||||
// controller:
|
||||
// _ingredients[index].amount,
|
||||
// label: 'Amount',
|
||||
// suffix: Settings.nutritionMeasurementSuffix,
|
||||
// min: 0,
|
||||
// onChanged: (value) {
|
||||
// if (value != null && value >= 0) {
|
||||
// setState(() {
|
||||
// _ingredients[index].amount = value.toDouble();
|
||||
// });
|
||||
// }
|
||||
// },
|
||||
// ),
|
||||
// ),
|
||||
],
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
)
|
||||
: Center(
|
||||
child: Text(_isNew
|
||||
? 'Save the Recipe in order to add ingredients!'
|
||||
: 'You have not added any Ingredients yet!'),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
bottomNavigationBar: DetailBottomRow(
|
||||
onCancel: handleCancelAction,
|
||||
onAction: _isSaving ? null : handleSaveAction,
|
||||
onMiddleAction: _isSaving ? null : () => handleSaveAction(close: true),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
200
lib/screens/recipe/recipe_list.dart
Normal file
200
lib/screens/recipe/recipe_list.dart
Normal file
@ -0,0 +1,200 @@
|
||||
import 'package:diameter/utils/dialog_utils.dart';
|
||||
import 'package:diameter/models/ingredient.dart';
|
||||
import 'package:diameter/models/recipe.dart';
|
||||
import 'package:diameter/models/settings.dart';
|
||||
import 'package:diameter/navigation.dart';
|
||||
import 'package:diameter/screens/recipe/recipe_detail.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class RecipeListScreen extends StatefulWidget {
|
||||
static const String routeName = '/recipes';
|
||||
|
||||
const RecipeListScreen({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
_RecipeListScreenState createState() => _RecipeListScreenState();
|
||||
}
|
||||
|
||||
class _RecipeListScreenState extends State<RecipeListScreen> {
|
||||
List<Recipe> _recipes = [];
|
||||
|
||||
final ScrollController _scrollController = ScrollController();
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
reload();
|
||||
}
|
||||
|
||||
void reload({String? message}) {
|
||||
setState(() {
|
||||
_recipes = Recipe.getAll();
|
||||
});
|
||||
setState(() {
|
||||
if (message != null) {
|
||||
var snackBar = SnackBar(
|
||||
content: Text(message),
|
||||
duration: const Duration(seconds: 2),
|
||||
);
|
||||
ScaffoldMessenger.of(context)
|
||||
..removeCurrentSnackBar()
|
||||
..showSnackBar(snackBar);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void onDelete(Recipe recipe) {
|
||||
Recipe.remove(recipe.id);
|
||||
reload(message: 'Recipe deleted');
|
||||
}
|
||||
|
||||
void handleDeleteAction(Recipe recipe) async {
|
||||
if (Settings.get().showConfirmationDialogOnDelete) {
|
||||
DialogUtils.showConfirmationDialog(
|
||||
context: context,
|
||||
onConfirm: () => onDelete(recipe),
|
||||
message: 'Are you sure you want to delete this Recipe?',
|
||||
);
|
||||
} else {
|
||||
onDelete(recipe);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: const Text('Recipes'), actions: <Widget>[
|
||||
IconButton(onPressed: reload, icon: const Icon(Icons.refresh))
|
||||
]),
|
||||
drawer: const Navigation(currentLocation: RecipeListScreen.routeName),
|
||||
body: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: _recipes.isNotEmpty
|
||||
? Scrollbar(
|
||||
controller: _scrollController,
|
||||
child: ListView.builder(
|
||||
controller: _scrollController,
|
||||
padding: const EdgeInsets.all(10.0),
|
||||
itemCount: _recipes.length,
|
||||
itemBuilder: (context, index) {
|
||||
final recipe = _recipes[index];
|
||||
final carbsRatio =
|
||||
Ingredient.getCarbsRatioForRecipe(recipe.id);
|
||||
final carbsPerPortion = Recipe.getCarbsPerPortion(recipe.id);
|
||||
return Card(
|
||||
child: ListTile(
|
||||
onTap: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) =>
|
||||
RecipeDetailScreen(id: recipe.id),
|
||||
),
|
||||
).then((result) => reload(message: result?[0]));
|
||||
},
|
||||
title: Text(
|
||||
recipe.name.toUpperCase(),
|
||||
style: Theme.of(context).textTheme.subtitle2,
|
||||
),
|
||||
subtitle: Padding(
|
||||
padding:
|
||||
const EdgeInsets.symmetric(vertical: 10.0),
|
||||
child: Row(
|
||||
children: [
|
||||
Column(
|
||||
children: [
|
||||
Text(recipe.notes ?? ''),
|
||||
],
|
||||
),
|
||||
Expanded(
|
||||
child: Column(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.center,
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.center,
|
||||
children:
|
||||
((carbsRatio ?? 0) > 0)
|
||||
? [
|
||||
Text(carbsRatio!.toString()),
|
||||
const Text('% carbs',
|
||||
textScaleFactor: 0.75),
|
||||
]
|
||||
: [],
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Column(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.center,
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.center,
|
||||
children:
|
||||
(recipe.servings != null)
|
||||
? [
|
||||
Text(recipe.servings!
|
||||
.toStringAsPrecision(3)),
|
||||
const Text('servings',
|
||||
textScaleFactor: 0.75),
|
||||
]
|
||||
: [],
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: Column(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.center,
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.center,
|
||||
children:
|
||||
((carbsPerPortion ?? 0) > 0)
|
||||
? [
|
||||
Text(carbsPerPortion!
|
||||
.toStringAsPrecision(3)),
|
||||
Text(
|
||||
'${Settings.nutritionMeasurementSuffix} carbs per serving',
|
||||
textScaleFactor: 0.75),
|
||||
]
|
||||
: [],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
trailing: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
IconButton(
|
||||
onPressed: () => handleDeleteAction(recipe),
|
||||
icon: const Icon(Icons.delete,
|
||||
color: Colors.blue),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
)
|
||||
: const Center(
|
||||
child: Text('You have not created any Recipes yet!'),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
floatingActionButton: FloatingActionButton(
|
||||
onPressed: () {
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => const RecipeDetailScreen(),
|
||||
),
|
||||
).then((result) => reload(message: result?[0]));
|
||||
},
|
||||
child: const Icon(Icons.add),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
12
pubspec.lock
12
pubspec.lock
@ -249,11 +249,6 @@ packages:
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
flutter_web_plugins:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
frontend_server_client:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -275,13 +270,6 @@ packages:
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.1.0"
|
||||
http:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: http
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.13.4"
|
||||
http_multi_server:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
Loading…
Reference in New Issue
Block a user