Merge branch 'main' of https://git.sudo.ca/spinel/diameter into main
This commit is contained in:
		
						commit
						481dc60996
					
				
					 12 changed files with 2773 additions and 1704 deletions
				
			
		|  | @ -68,4 +68,19 @@ class LogEntry { | ||||||
|   String toString() { |   String toString() { | ||||||
|     return DateTimeUtils.displayDateTime(time); |     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 |   // methods | ||||||
|   static LogEvent? get(int id) => box.get(id); |   static LogEvent? get(int id) => box.get(id); | ||||||
|   static List<LogEvent> getAll() => box.getAll(); |  | ||||||
|   static void put(LogEvent logEvent) => box.put(logEvent); |   static void put(LogEvent logEvent) => box.put(logEvent); | ||||||
| 
 | 
 | ||||||
|   static void remove(int id) { |   static void remove(int id) { | ||||||
|  |  | ||||||
|  | @ -1087,44 +1087,19 @@ | ||||||
|     "lastSequenceId": "0:0", |     "lastSequenceId": "0:0", | ||||||
|     "modelVersion": 5, |     "modelVersion": 5, | ||||||
|     "modelVersionParserMinimum": 5, |     "modelVersionParserMinimum": 5, | ||||||
|   "retiredEntityUids": [ |     "retiredEntityUids": [3095978685310268382], | ||||||
|     3095978685310268382 |     "retiredIndexUids": [3670661188280692002, 7379712902406481832], | ||||||
|   ], |  | ||||||
|   "retiredIndexUids": [ |  | ||||||
|     3670661188280692002, |  | ||||||
|     7379712902406481832 |  | ||||||
|   ], |  | ||||||
|     "retiredPropertyUids": [ |     "retiredPropertyUids": [ | ||||||
|     3455702077061719523, |         3455702077061719523, 1048198814030724077, 9003780003858349085, | ||||||
|     1048198814030724077, |         5421422436108145565, 7741631874181070179, 5471636804765937328, | ||||||
|     9003780003858349085, |         6855574218883169324, 5313708456544000157, 3678829169126156351, | ||||||
|     5421422436108145565, |         1568597071506264632, 8795268969829293398, 3247926313599127440, | ||||||
|     7741631874181070179, |         8789440370359282572, 7838546213550447420, 8031421171668506924, | ||||||
|     5471636804765937328, |         1614362036318874174, 1675040259141389754, 7518219134349037920, | ||||||
|     6855574218883169324, |         2172890064639236018, 310032577683835406, 5588897884422150510, | ||||||
|     5313708456544000157, |         7638848982383620744, 3282706593658092097, 596980591281311896, | ||||||
|     3678829169126156351, |         3633551763915044903, 2215708755581938580, 241621230513128588, | ||||||
|     1568597071506264632, |         4678123663117222609, 780211923138281722, 763575433624979013, | ||||||
|     8795268969829293398, |  | ||||||
|     3247926313599127440, |  | ||||||
|     8789440370359282572, |  | ||||||
|     7838546213550447420, |  | ||||||
|     8031421171668506924, |  | ||||||
|     1614362036318874174, |  | ||||||
|     1675040259141389754, |  | ||||||
|     7518219134349037920, |  | ||||||
|     2172890064639236018, |  | ||||||
|     310032577683835406, |  | ||||||
|     5588897884422150510, |  | ||||||
|     7638848982383620744, |  | ||||||
|     3282706593658092097, |  | ||||||
|     596980591281311896, |  | ||||||
|     3633551763915044903, |  | ||||||
|     2215708755581938580, |  | ||||||
|     241621230513128588, |  | ||||||
|     4678123663117222609, |  | ||||||
|     780211923138281722, |  | ||||||
|     763575433624979013, |  | ||||||
|         1225271130099322691 |         1225271130099322691 | ||||||
|     ], |     ], | ||||||
|     "retiredRelationUids": [], |     "retiredRelationUids": [], | ||||||
|  |  | ||||||
|  | @ -1234,7 +1234,7 @@ ModelDefinition getObjectBoxModel() { | ||||||
|           final buffer = fb.BufferContext(fbData); |           final buffer = fb.BufferContext(fbData); | ||||||
|           final rootOffset = buffer.derefObject(0); |           final rootOffset = buffer.derefObject(0); | ||||||
| 
 | 
 | ||||||
|           final object = Bolus( |           final object = LogEventType( | ||||||
|               id: const fb.Int64Reader().vTableGet(buffer, rootOffset, 4, 0), |               id: const fb.Int64Reader().vTableGet(buffer, rootOffset, 4, 0), | ||||||
|               deleted: const fb.BoolReader() |               deleted: const fb.BoolReader() | ||||||
|                   .vTableGet(buffer, rootOffset, 20, false), |                   .vTableGet(buffer, rootOffset, 20, false), | ||||||
|  | @ -1250,190 +1250,6 @@ ModelDefinition getObjectBoxModel() { | ||||||
|                   .vTableGetNullable(buffer, rootOffset, 14), |                   .vTableGetNullable(buffer, rootOffset, 14), | ||||||
|               mmolPerL: const fb.Float64Reader() |               mmolPerL: const fb.Float64Reader() | ||||||
|                   .vTableGetNullable(buffer, rootOffset, 16)); |                   .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 = |           object.bolusProfile.targetId = | ||||||
|               const fb.Int64Reader().vTableGet(buffer, rootOffset, 16, 0); |               const fb.Int64Reader().vTableGet(buffer, rootOffset, 16, 0); | ||||||
|           object.bolusProfile.attach(store); |           object.bolusProfile.attach(store); | ||||||
|  | @ -1487,6 +1303,353 @@ ModelDefinition getObjectBoxModel() { | ||||||
|           final rootOffset = buffer.derefObject(0); |           final rootOffset = buffer.derefObject(0); | ||||||
| 
 | 
 | ||||||
|           final object = LogMeal( |           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), |               id: const fb.Int64Reader().vTableGet(buffer, rootOffset, 4, 0), | ||||||
|               deleted: const fb.BoolReader() |               deleted: const fb.BoolReader() | ||||||
|                   .vTableGet(buffer, rootOffset, 36, false), |                   .vTableGet(buffer, rootOffset, 36, false), | ||||||
|  |  | ||||||
|  | @ -188,7 +188,7 @@ class _LogEventDetailScreenState extends State<LogEventDetailScreen> { | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   Future<void> checkIfActiveEventOfTypeExistsBeforeSaving() async { |   Future<void> checkIfActiveEventOfTypeExistsBeforeSaving() async { | ||||||
|     if (_isNew && _eventType != null && |     if (_eventType != null && | ||||||
|         LogEvent.eventTypeExistsForTime(_eventType!.id, _time)) { |         LogEvent.eventTypeExistsForTime(_eventType!.id, _time)) { | ||||||
|       await showDialog( |       await showDialog( | ||||||
|           context: context, |           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 |     description: flutter | ||||||
|     source: sdk |     source: sdk | ||||||
|     version: "0.0.0" |     version: "0.0.0" | ||||||
|   flutter_web_plugins: |  | ||||||
|     dependency: transitive |  | ||||||
|     description: flutter |  | ||||||
|     source: sdk |  | ||||||
|     version: "0.0.0" |  | ||||||
|   frontend_server_client: |   frontend_server_client: | ||||||
|     dependency: transitive |     dependency: transitive | ||||||
|     description: |     description: | ||||||
|  | @ -275,13 +270,6 @@ packages: | ||||||
|       url: "https://pub.dartlang.org" |       url: "https://pub.dartlang.org" | ||||||
|     source: hosted |     source: hosted | ||||||
|     version: "2.1.0" |     version: "2.1.0" | ||||||
|   http: |  | ||||||
|     dependency: transitive |  | ||||||
|     description: |  | ||||||
|       name: http |  | ||||||
|       url: "https://pub.dartlang.org" |  | ||||||
|     source: hosted |  | ||||||
|     version: "0.13.4" |  | ||||||
|   http_multi_server: |   http_multi_server: | ||||||
|     dependency: transitive |     dependency: transitive | ||||||
|     description: |     description: | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		
		Reference in a new issue