implement dropdown with autocomplete; add bolus tab to log entry
This commit is contained in:
parent
ab0839bff3
commit
13507d14c9
71
TODO
71
TODO
@ -1,37 +1,48 @@
|
|||||||
General/Framework:
|
MAIN TASKS:
|
||||||
☐ add active/deleted flag to all data models
|
General/Framework:
|
||||||
☐ account for deleted/disabled elements in dropdowns
|
☐ add active/deleted flag to all data models
|
||||||
☐ place dropdown items right below their input
|
☐ find a general way to deal with duration fields
|
||||||
✔ use local database instead of back4app @done(21-11-07 18:53)
|
|
||||||
☐ find a general way to deal with duration fields
|
Accuracies:
|
||||||
☐ add explanations to each section
|
☐ implement reordering
|
||||||
☐ use ids instead of passing entities around where possible
|
|
||||||
|
|
||||||
Accuracies:
|
Basal/Bolus:
|
||||||
☐ implement reordering
|
☐ create distinct visual mode for picking the active profile
|
||||||
|
|
||||||
Basal/Bolus:
|
Log Overview:
|
||||||
☐ create distinct visual mode for picking the active profile
|
☐ add active events view (as main menu item?)
|
||||||
|
☐ adjust/debug active events view
|
||||||
|
☐ display more information
|
||||||
|
☐ apply target color settings to glucose
|
||||||
|
☐ total bolus and carbs per entry
|
||||||
|
|
||||||
Meal:
|
Log Entry:
|
||||||
none
|
☐ replace meal and glucose boli with logbolus entities
|
||||||
|
☐ fix logmeals/logboli/logevents (probably use queries for associated items instead of onetomany)
|
||||||
|
|
||||||
Log Overview:
|
Event Types:
|
||||||
☐ add active events view
|
☐ add option to change bolus/basal profile for event duration
|
||||||
☐ adjust/debug active events view
|
|
||||||
☐ display more information
|
|
||||||
☐ apply target color settings to glucose
|
|
||||||
☐ total bolus and carbs per entry
|
|
||||||
|
|
||||||
Log Entry:
|
Settings:
|
||||||
☐ add tab for bolus overview
|
☐ add objectbox class and use instead of shared preferences
|
||||||
☐ calculate bolus suggestions according to active profile
|
☐ add fields for date and time formats
|
||||||
☐ add time picker for entry date/time
|
☐ add fields for glucose target
|
||||||
|
|
||||||
Event Types:
|
FUTURE TASKS:
|
||||||
☐ add option to change bolus/basal profile for event duration
|
General/Framework:
|
||||||
|
☐ add explanations to each section
|
||||||
|
☐ account for deleted/disabled elements in dropdowns
|
||||||
|
☐ hide dropdown overlay on tapping anywhere else (especially menu)
|
||||||
|
☐ add clear button to dropdown (or all text fields?)
|
||||||
|
Log Overview:
|
||||||
|
☐ add pagination
|
||||||
|
|
||||||
|
|
||||||
Settings:
|
ARCHIVE:
|
||||||
☐ add objectbox class and use instead of shared preferences
|
✔ add tab for bolus overview @done(21-11-24 22:05) @project(Log Entry)
|
||||||
☐ add fields for date and time formats
|
✔ calculate bolus suggestions according to active profile @done(21-11-24 22:05) @project(Log Entry)
|
||||||
☐ add fields for glucose target
|
✔ place dropdown items right below their input @done(21-11-23 20:33) @project(General/Framework)
|
||||||
|
✔ add autocomplete function to dropdowns @done(21-11-23 20:33) @project(General/Framework)
|
||||||
|
✔ use local database instead of back4app @done(21-11-07 18:53) @project(General/Framework)
|
||||||
|
✔ use ids instead of passing entities around where possible @done(21-11-10 00:06) @project(General/Framework)
|
||||||
|
✔ add time picker for entry date/time @done(21-11-10 00:06) @project(Log Entry)
|
||||||
|
@ -5,6 +5,7 @@ class AppTheme {
|
|||||||
AppTheme._();
|
AppTheme._();
|
||||||
|
|
||||||
static ThemeData lightTheme = FlexColorScheme.light(
|
static ThemeData lightTheme = FlexColorScheme.light(
|
||||||
|
surfaceStyle: FlexSurface.medium,
|
||||||
scheme: FlexScheme.mandyRed,
|
scheme: FlexScheme.mandyRed,
|
||||||
fontFamily: 'RobotoCondensed',
|
fontFamily: 'RobotoCondensed',
|
||||||
).toTheme;
|
).toTheme;
|
||||||
@ -16,8 +17,7 @@ class AppTheme {
|
|||||||
|
|
||||||
static ThemeData makeTheme(ThemeData baseThemeData) {
|
static ThemeData makeTheme(ThemeData baseThemeData) {
|
||||||
return baseThemeData.copyWith(
|
return baseThemeData.copyWith(
|
||||||
visualDensity: VisualDensity.compact,
|
bottomNavigationBarTheme: BottomNavigationBarThemeData(
|
||||||
bottomNavigationBarTheme: BottomNavigationBarThemeData(
|
backgroundColor: baseThemeData.primaryColor));
|
||||||
backgroundColor: baseThemeData.primaryColor));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
199
lib/components/dropdown.dart
Normal file
199
lib/components/dropdown.dart
Normal file
@ -0,0 +1,199 @@
|
|||||||
|
import 'dart:math';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class AutoCompleteDropdownButton<T> extends StatefulWidget {
|
||||||
|
final String label;
|
||||||
|
final T? selectedItem;
|
||||||
|
final List<T> items;
|
||||||
|
final String Function(T item) renderItem;
|
||||||
|
final void Function(T? value) onChanged;
|
||||||
|
final List<T> Function(String? value)? applyQuery;
|
||||||
|
|
||||||
|
const AutoCompleteDropdownButton(
|
||||||
|
{Key? key,
|
||||||
|
this.selectedItem,
|
||||||
|
required this.label,
|
||||||
|
required this.items,
|
||||||
|
required this.renderItem,
|
||||||
|
required this.onChanged,
|
||||||
|
this.applyQuery})
|
||||||
|
: super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
_AutoCompleteDropdownButtonState<T> createState() =>
|
||||||
|
_AutoCompleteDropdownButtonState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _AutoCompleteDropdownButtonState<T>
|
||||||
|
extends State<AutoCompleteDropdownButton<T>> {
|
||||||
|
TextEditingController controller = TextEditingController(text: '');
|
||||||
|
late List<T> options;
|
||||||
|
late List<T> suggestions;
|
||||||
|
|
||||||
|
final LayerLink layerLink = LayerLink();
|
||||||
|
OverlayEntry? entry;
|
||||||
|
bool isOpen = false;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
setState(() {
|
||||||
|
controller.text = widget.selectedItem == null
|
||||||
|
? ''
|
||||||
|
: widget.renderItem(widget.selectedItem!);
|
||||||
|
options = widget.items;
|
||||||
|
suggestions = [];
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void toggleOverlay() {
|
||||||
|
isOpen ? hideOverlay() : showOverlay();
|
||||||
|
}
|
||||||
|
|
||||||
|
void showOverlay() {
|
||||||
|
hideOverlay();
|
||||||
|
|
||||||
|
List<Widget> items = [];
|
||||||
|
Divider? divider;
|
||||||
|
|
||||||
|
final overlay = Overlay.of(context)!;
|
||||||
|
final renderBox = context.findRenderObject() as RenderBox;
|
||||||
|
|
||||||
|
if (widget.selectedItem != null) {
|
||||||
|
items.add(buildListTile(widget.selectedItem!));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (suggestions.isNotEmpty) {
|
||||||
|
items.addAll(suggestions
|
||||||
|
.where((item) => item != widget.selectedItem)
|
||||||
|
.map((item) => buildListTile(item)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((widget.selectedItem != null || suggestions.isNotEmpty) &&
|
||||||
|
(options.length -
|
||||||
|
suggestions.length -
|
||||||
|
(widget.selectedItem == null ? 0 : 1) >
|
||||||
|
0)) {
|
||||||
|
divider = const Divider(height: 10);
|
||||||
|
items.add(divider);
|
||||||
|
}
|
||||||
|
|
||||||
|
items.addAll(options
|
||||||
|
.where((item) =>
|
||||||
|
!(widget.selectedItem != null &&
|
||||||
|
widget.renderItem(item) ==
|
||||||
|
widget.renderItem(widget.selectedItem!)) &&
|
||||||
|
!suggestions.contains(item))
|
||||||
|
.map((item) => buildListTile(item))
|
||||||
|
.toList());
|
||||||
|
|
||||||
|
final screenHeight = MediaQuery.of(context).size.height;
|
||||||
|
final neededHeight =
|
||||||
|
renderBox.size.height * (items.length - 1) + (divider?.height ??
|
||||||
|
renderBox.size.height);
|
||||||
|
final availableHeight = screenHeight -
|
||||||
|
(renderBox.localToGlobal(Offset.zero).dy + renderBox.size.height);
|
||||||
|
bool displayAbove = neededHeight > availableHeight &&
|
||||||
|
screenHeight - availableHeight > availableHeight;
|
||||||
|
final height = min(neededHeight,
|
||||||
|
max(availableHeight, screenHeight - availableHeight - 55) - 55);
|
||||||
|
|
||||||
|
entry = OverlayEntry(
|
||||||
|
builder: (context) => Positioned(
|
||||||
|
width: renderBox.size.width,
|
||||||
|
height: height,
|
||||||
|
child: CompositedTransformFollower(
|
||||||
|
link: layerLink,
|
||||||
|
targetAnchor: displayAbove ? Alignment.bottomLeft : Alignment.topLeft,
|
||||||
|
followerAnchor:
|
||||||
|
displayAbove ? Alignment.bottomLeft : Alignment.topLeft,
|
||||||
|
offset: Offset(0, renderBox.size.height * (displayAbove ? -1 : 1)),
|
||||||
|
showWhenUnlinked: false,
|
||||||
|
child: Material(
|
||||||
|
elevation: 8,
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
child: Column(
|
||||||
|
children: items,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
overlay.insert(entry!);
|
||||||
|
isOpen = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
ListTile buildListTile(T item) {
|
||||||
|
return ListTile(
|
||||||
|
onTap: () => handleChanged(item),
|
||||||
|
selected: item == widget.selectedItem,
|
||||||
|
title: Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: Text(widget.renderItem(item)),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void hideOverlay() {
|
||||||
|
entry?.remove();
|
||||||
|
entry = null;
|
||||||
|
isOpen = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleChanged(item) {
|
||||||
|
widget.onChanged(item);
|
||||||
|
controller.text = widget.renderItem(item);
|
||||||
|
hideOverlay();
|
||||||
|
}
|
||||||
|
|
||||||
|
void onChangeQuery(String value) {
|
||||||
|
if (value.trim() == '' ||
|
||||||
|
(widget.selectedItem != null &&
|
||||||
|
value == widget.renderItem(widget.selectedItem!))) {
|
||||||
|
setState(() {
|
||||||
|
suggestions = [];
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
if (widget.applyQuery == null) {
|
||||||
|
setState(() {
|
||||||
|
suggestions = widget.items.where((item) {
|
||||||
|
String itemString = widget.renderItem(item).toLowerCase();
|
||||||
|
String valueLowercase = value.toLowerCase();
|
||||||
|
return itemString.contains(valueLowercase);
|
||||||
|
}).toList();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
setState(() {
|
||||||
|
suggestions = widget.applyQuery!(value)
|
||||||
|
.where((item) => item != widget.selectedItem)
|
||||||
|
.toList();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
showOverlay();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return CompositedTransformTarget(
|
||||||
|
link: layerLink,
|
||||||
|
child: TextFormField(
|
||||||
|
onChanged: onChangeQuery,
|
||||||
|
onTap: toggleOverlay,
|
||||||
|
controller: controller,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: widget.label,
|
||||||
|
suffixIcon: IconButton(
|
||||||
|
onPressed: toggleOverlay,
|
||||||
|
icon: const Icon(Icons.arrow_drop_down),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -65,7 +65,7 @@ class BooleanFormField extends StatefulWidget {
|
|||||||
class _BooleanFormFieldState extends State<BooleanFormField> {
|
class _BooleanFormFieldState extends State<BooleanFormField> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return FormField<bool>(builder: (context) {
|
return FormField<bool>(builder: (state) {
|
||||||
return ListTile(
|
return ListTile(
|
||||||
onTap: () => widget.onChanged(!widget.value),
|
onTap: () => widget.onChanged(!widget.value),
|
||||||
trailing: Switch(
|
trailing: Switch(
|
||||||
@ -98,8 +98,7 @@ class DateTimeFormField extends StatefulWidget {
|
|||||||
: super(key: key);
|
: super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_DateTimeFormFieldState createState() =>
|
_DateTimeFormFieldState createState() => _DateTimeFormFieldState();
|
||||||
_DateTimeFormFieldState();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class _DateTimeFormFieldState extends State<DateTimeFormField> {
|
class _DateTimeFormFieldState extends State<DateTimeFormField> {
|
||||||
@ -115,7 +114,7 @@ class _DateTimeFormFieldState extends State<DateTimeFormField> {
|
|||||||
final newTime = await showDatePicker(
|
final newTime = await showDatePicker(
|
||||||
context: context,
|
context: context,
|
||||||
initialDate: widget.date,
|
initialDate: widget.date,
|
||||||
firstDate: widget.minDate ?? DateTime(2000,1,1),
|
firstDate: widget.minDate ?? DateTime(2000, 1, 1),
|
||||||
lastDate: widget.maxDate ?? DateTime.now(),
|
lastDate: widget.maxDate ?? DateTime.now(),
|
||||||
);
|
);
|
||||||
widget.onChanged(newTime);
|
widget.onChanged(newTime);
|
||||||
@ -139,8 +138,7 @@ class TimeOfDayFormField extends StatefulWidget {
|
|||||||
: super(key: key);
|
: super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_TimeOfDayFormFieldState createState() =>
|
_TimeOfDayFormFieldState createState() => _TimeOfDayFormFieldState();
|
||||||
_TimeOfDayFormFieldState();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class _TimeOfDayFormFieldState extends State<TimeOfDayFormField> {
|
class _TimeOfDayFormFieldState extends State<TimeOfDayFormField> {
|
||||||
@ -163,42 +161,3 @@ class _TimeOfDayFormFieldState extends State<TimeOfDayFormField> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class LabeledDropdownButton<T> extends StatefulWidget {
|
|
||||||
final String label;
|
|
||||||
final T? selectedItem;
|
|
||||||
final List<T> items;
|
|
||||||
final Widget Function(T item) renderItem;
|
|
||||||
final void Function(T? value) onChanged;
|
|
||||||
|
|
||||||
const LabeledDropdownButton(
|
|
||||||
{Key? key,
|
|
||||||
this.selectedItem,
|
|
||||||
required this.label,
|
|
||||||
required this.items,
|
|
||||||
required this.renderItem,
|
|
||||||
required this.onChanged})
|
|
||||||
: super(key: key);
|
|
||||||
|
|
||||||
@override
|
|
||||||
_LabeledDropdownButtonState<T> createState() => _LabeledDropdownButtonState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _LabeledDropdownButtonState<T> extends State<LabeledDropdownButton<T>> {
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return DropdownButtonFormField<T>(
|
|
||||||
decoration: InputDecoration(
|
|
||||||
labelText: widget.label,
|
|
||||||
),
|
|
||||||
value: widget.selectedItem,
|
|
||||||
onChanged: widget.onChanged,
|
|
||||||
items: widget.items
|
|
||||||
.map((item) => DropdownMenuItem<T>(
|
|
||||||
value: item,
|
|
||||||
child: widget.renderItem(item),
|
|
||||||
))
|
|
||||||
.toList(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
@ -4,8 +4,8 @@ import 'package:diameter/screens/accuracy_detail.dart';
|
|||||||
import 'package:diameter/screens/basal/basal_profile_detail.dart';
|
import 'package:diameter/screens/basal/basal_profile_detail.dart';
|
||||||
import 'package:diameter/screens/bolus/bolus_profile_detail.dart';
|
import 'package:diameter/screens/bolus/bolus_profile_detail.dart';
|
||||||
import 'package:diameter/screens/log/log.dart';
|
import 'package:diameter/screens/log/log.dart';
|
||||||
import 'package:diameter/screens/log/log_entry.dart';
|
import 'package:diameter/screens/log/log_entry/log_entry.dart';
|
||||||
import 'package:diameter/screens/log/log_event_detail.dart';
|
import 'package:diameter/screens/log/log_entry/log_event_detail.dart';
|
||||||
import 'package:diameter/screens/log/log_event_type_detail.dart';
|
import 'package:diameter/screens/log/log_event_type_detail.dart';
|
||||||
import 'package:diameter/screens/log/log_event_type_list.dart';
|
import 'package:diameter/screens/log/log_event_type_list.dart';
|
||||||
import 'package:diameter/screens/meal/meal_category_detail.dart';
|
import 'package:diameter/screens/meal/meal_category_detail.dart';
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
import 'package:diameter/main.dart';
|
import 'package:diameter/main.dart';
|
||||||
import 'package:diameter/models/bolus_profile.dart';
|
import 'package:diameter/models/bolus_profile.dart';
|
||||||
import 'package:diameter/objectbox.g.dart';
|
import 'package:diameter/objectbox.g.dart';
|
||||||
|
import 'package:diameter/utils/date_time_utils.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
@Entity(uid: 3417770529060202389)
|
@Entity(uid: 3417770529060202389)
|
||||||
class Bolus {
|
class Bolus {
|
||||||
@ -37,4 +39,22 @@ class Bolus {
|
|||||||
builder.link(Bolus_.bolusProfile, BolusProfile_.id.equals(id));
|
builder.link(Bolus_.bolusProfile, BolusProfile_.id.equals(id));
|
||||||
return builder.build().find();
|
return builder.build().find();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Bolus? getRateForTime(DateTime? dateTime) {
|
||||||
|
if (dateTime != null) {
|
||||||
|
// ignore: todo
|
||||||
|
// TODO: check if an event is active that would change the active profile
|
||||||
|
final bolusProfile = BolusProfile.getActive();
|
||||||
|
final time = DateTimeUtils.convertTimeOfDayToDateTime(TimeOfDay.fromDateTime(dateTime));
|
||||||
|
if (bolusProfile != null) {
|
||||||
|
final rates = Bolus.getAllForProfile(bolusProfile.id);
|
||||||
|
final result = rates.where((rate) =>
|
||||||
|
(time.isAfter(rate.startTime) ||
|
||||||
|
time.isAtSameMomentAs(rate.startTime)) &&
|
||||||
|
time.isBefore(rate.endTime));
|
||||||
|
return result.length != 1 ? null : result.single;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,4 +34,11 @@ class BolusProfile {
|
|||||||
return element;
|
return element;
|
||||||
}).toList());
|
}).toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static BolusProfile? getActive() {
|
||||||
|
Query<BolusProfile> query =
|
||||||
|
box.query(BolusProfile_.active.equals(true)).build();
|
||||||
|
final result = query.find();
|
||||||
|
return result.length != 1 ? null : result.single;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
38
lib/models/log_bolus.dart
Normal file
38
lib/models/log_bolus.dart
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import 'package:diameter/main.dart';
|
||||||
|
import 'package:diameter/models/bolus.dart';
|
||||||
|
import 'package:diameter/models/log_entry.dart';
|
||||||
|
import 'package:diameter/models/log_meal.dart';
|
||||||
|
import 'package:diameter/objectbox.g.dart';
|
||||||
|
|
||||||
|
@Entity(uid: 0)
|
||||||
|
class LogBolus {
|
||||||
|
static final Box<LogBolus> box = objectBox.store.box<LogBolus>();
|
||||||
|
|
||||||
|
int id;
|
||||||
|
double units;
|
||||||
|
double? carbs;
|
||||||
|
int? delay;
|
||||||
|
int? mgPerDl;
|
||||||
|
double? mmolPerL;
|
||||||
|
bool manuallyAdjusted;
|
||||||
|
String? notes;
|
||||||
|
|
||||||
|
final logEntry = ToOne<LogEntry>();
|
||||||
|
final rate = ToOne<Bolus>();
|
||||||
|
final meal = ToOne<LogMeal?>();
|
||||||
|
|
||||||
|
LogBolus({
|
||||||
|
this.id = 0,
|
||||||
|
this.units = 0,
|
||||||
|
this.carbs,
|
||||||
|
this.delay,
|
||||||
|
this.mgPerDl,
|
||||||
|
this.mmolPerL,
|
||||||
|
this.manuallyAdjusted = false,
|
||||||
|
this.notes,
|
||||||
|
});
|
||||||
|
|
||||||
|
static LogBolus? get(int id) => box.get(id);
|
||||||
|
static void put(LogBolus logBolus) => box.put(logBolus);
|
||||||
|
static void remove(int id) => box.remove(id);
|
||||||
|
}
|
@ -1,4 +1,5 @@
|
|||||||
import 'package:diameter/main.dart';
|
import 'package:diameter/main.dart';
|
||||||
|
import 'package:diameter/models/log_bolus.dart';
|
||||||
import 'package:diameter/models/log_event.dart';
|
import 'package:diameter/models/log_event.dart';
|
||||||
import 'package:diameter/models/log_meal.dart';
|
import 'package:diameter/models/log_meal.dart';
|
||||||
import 'package:diameter/objectbox.g.dart';
|
import 'package:diameter/objectbox.g.dart';
|
||||||
@ -28,6 +29,9 @@ class LogEntry {
|
|||||||
@Backlink('logEntry')
|
@Backlink('logEntry')
|
||||||
final meals = ToMany<LogMeal>();
|
final meals = ToMany<LogMeal>();
|
||||||
|
|
||||||
|
@Backlink('logEntry')
|
||||||
|
final boli = ToMany<LogBolus>();
|
||||||
|
|
||||||
LogEntry({
|
LogEntry({
|
||||||
this.id = 0,
|
this.id = 0,
|
||||||
required this.time,
|
required this.time,
|
||||||
@ -47,7 +51,8 @@ class LogEntry {
|
|||||||
static Map<DateTime, List<LogEntry>> getDailyEntryMap() {
|
static Map<DateTime, List<LogEntry>> getDailyEntryMap() {
|
||||||
Map<DateTime, List<LogEntry>> dateMap = <DateTime, List<LogEntry>>{};
|
Map<DateTime, List<LogEntry>> dateMap = <DateTime, List<LogEntry>>{};
|
||||||
|
|
||||||
QueryBuilder<LogEntry> allByDate = box.query()..order(LogEntry_.time, flags: Order.descending);
|
QueryBuilder<LogEntry> allByDate = box.query()
|
||||||
|
..order(LogEntry_.time, flags: Order.descending);
|
||||||
List<LogEntry> entries = allByDate.build().find();
|
List<LogEntry> entries = allByDate.build().find();
|
||||||
DateTime? date;
|
DateTime? date;
|
||||||
|
|
||||||
|
@ -7,11 +7,11 @@ import 'package:diameter/screens/bolus/bolus_detail.dart';
|
|||||||
import 'package:diameter/screens/bolus/bolus_profile_detail.dart';
|
import 'package:diameter/screens/bolus/bolus_profile_detail.dart';
|
||||||
import 'package:diameter/screens/bolus/bolus_profile_list.dart';
|
import 'package:diameter/screens/bolus/bolus_profile_list.dart';
|
||||||
import 'package:diameter/screens/log/log.dart';
|
import 'package:diameter/screens/log/log.dart';
|
||||||
import 'package:diameter/screens/log/log_entry.dart';
|
import 'package:diameter/screens/log/log_entry/log_entry.dart';
|
||||||
import 'package:diameter/screens/log/log_event_detail.dart';
|
import 'package:diameter/screens/log/log_entry/log_event_detail.dart';
|
||||||
import 'package:diameter/screens/log/log_event_type_detail.dart';
|
import 'package:diameter/screens/log/log_event_type_detail.dart';
|
||||||
import 'package:diameter/screens/log/log_event_type_list.dart';
|
import 'package:diameter/screens/log/log_event_type_list.dart';
|
||||||
import 'package:diameter/screens/log/log_meal_detail.dart';
|
import 'package:diameter/screens/log/log_entry/log_meal_detail.dart';
|
||||||
import 'package:diameter/screens/meal/meal_category_detail.dart';
|
import 'package:diameter/screens/meal/meal_category_detail.dart';
|
||||||
import 'package:diameter/screens/meal/meal_category_list.dart';
|
import 'package:diameter/screens/meal/meal_category_list.dart';
|
||||||
import 'package:diameter/screens/meal/meal_detail.dart';
|
import 'package:diameter/screens/meal/meal_detail.dart';
|
||||||
|
@ -2,7 +2,6 @@ import 'package:diameter/components/dialogs.dart';
|
|||||||
import 'package:diameter/config.dart';
|
import 'package:diameter/config.dart';
|
||||||
import 'package:diameter/navigation.dart';
|
import 'package:diameter/navigation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
// import 'package:diameter/components/progress_indicator.dart';
|
|
||||||
import 'package:diameter/models/basal_profile.dart';
|
import 'package:diameter/models/basal_profile.dart';
|
||||||
import 'package:diameter/screens/basal/basal_profile_detail.dart';
|
import 'package:diameter/screens/basal/basal_profile_detail.dart';
|
||||||
|
|
||||||
@ -24,7 +23,6 @@ class _BasalProfileListScreenState extends State<BasalProfileListScreen> {
|
|||||||
pickActiveProfileMode = false;
|
pickActiveProfileMode = false;
|
||||||
_basalProfiles = BasalProfile.getAll();
|
_basalProfiles = BasalProfile.getAll();
|
||||||
});
|
});
|
||||||
// _basalProfiles.then((list) =>
|
|
||||||
updateBanner();
|
updateBanner();
|
||||||
setState(() {
|
setState(() {
|
||||||
if (message != null) {
|
if (message != null) {
|
||||||
@ -94,7 +92,6 @@ class _BasalProfileListScreenState extends State<BasalProfileListScreen> {
|
|||||||
BasalProfile.setAllInactive;
|
BasalProfile.setAllInactive;
|
||||||
basalProfile.active = true;
|
basalProfile.active = true;
|
||||||
BasalProfile.put(basalProfile);
|
BasalProfile.put(basalProfile);
|
||||||
// (exception: basalProfile.objectId!).then((_) =>
|
|
||||||
refresh(message: '${basalProfile.name} has been set as your active Profile');
|
refresh(message: '${basalProfile.name} has been set as your active Profile');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,20 +2,17 @@ import 'package:diameter/components/dialogs.dart';
|
|||||||
import 'package:diameter/config.dart';
|
import 'package:diameter/config.dart';
|
||||||
import 'package:diameter/models/log_entry.dart';
|
import 'package:diameter/models/log_entry.dart';
|
||||||
import 'package:diameter/models/log_event.dart';
|
import 'package:diameter/models/log_event.dart';
|
||||||
// import 'package:diameter/models/log_event_type.dart';
|
|
||||||
import 'package:diameter/screens/log/log_event_detail.dart';
|
|
||||||
import 'package:diameter/utils/date_time_utils.dart';
|
import 'package:diameter/utils/date_time_utils.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
// import 'package:diameter/components/progress_indicator.dart';
|
|
||||||
|
|
||||||
class ActiveLogEventListScreen extends StatefulWidget {
|
class ActiveLogEventListScreen extends StatefulWidget {
|
||||||
static const String routeName = '/active-log-events';
|
static const String routeName = '/active-log-events';
|
||||||
|
|
||||||
final LogEntry? endLogEntry;
|
final int endLogEntryId;
|
||||||
final Function()? onSetEndTime;
|
final Function()? onSetEndTime;
|
||||||
|
|
||||||
const ActiveLogEventListScreen(
|
const ActiveLogEventListScreen(
|
||||||
{Key? key, this.endLogEntry, this.onSetEndTime})
|
{Key? key, this.endLogEntryId = 0, this.onSetEndTime})
|
||||||
: super(key: key);
|
: super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -26,7 +23,13 @@ class ActiveLogEventListScreen extends StatefulWidget {
|
|||||||
class _ActiveLogEventListScreenState extends State<ActiveLogEventListScreen> {
|
class _ActiveLogEventListScreenState extends State<ActiveLogEventListScreen> {
|
||||||
List<LogEvent> _activeLogEvents = [];
|
List<LogEvent> _activeLogEvents = [];
|
||||||
|
|
||||||
void refresh({String? message}) {
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
void reload({String? message}) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_activeLogEvents = LogEvent.getAllOngoing();
|
_activeLogEvents = LogEvent.getAllOngoing();
|
||||||
});
|
});
|
||||||
@ -47,9 +50,9 @@ class _ActiveLogEventListScreenState extends State<ActiveLogEventListScreen> {
|
|||||||
void onStop(LogEvent event) async {
|
void onStop(LogEvent event) async {
|
||||||
event.endTime = DateTime.now();
|
event.endTime = DateTime.now();
|
||||||
event.endLogEntry.target =
|
event.endLogEntry.target =
|
||||||
widget.endLogEntry ?? LogEntry(time: DateTime.now());
|
LogEntry.get(widget.endLogEntryId) ?? LogEntry(time: DateTime.now());
|
||||||
LogEvent.put(event);
|
LogEvent.put(event);
|
||||||
refresh();
|
reload();
|
||||||
if (widget.onSetEndTime != null) {
|
if (widget.onSetEndTime != null) {
|
||||||
widget.onSetEndTime!();
|
widget.onSetEndTime!();
|
||||||
}
|
}
|
||||||
@ -69,7 +72,7 @@ class _ActiveLogEventListScreenState extends State<ActiveLogEventListScreen> {
|
|||||||
|
|
||||||
void onDelete(LogEvent event) {
|
void onDelete(LogEvent event) {
|
||||||
LogEvent.remove(event.id);
|
LogEvent.remove(event.id);
|
||||||
refresh(message: 'Event deleted');
|
reload(message: 'Event deleted');
|
||||||
}
|
}
|
||||||
|
|
||||||
void handleDeleteAction(LogEvent event) async {
|
void handleDeleteAction(LogEvent event) async {
|
||||||
@ -84,11 +87,6 @@ class _ActiveLogEventListScreenState extends State<ActiveLogEventListScreen> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
refresh();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@ -101,20 +99,7 @@ class _ActiveLogEventListScreenState extends State<ActiveLogEventListScreen> {
|
|||||||
primary: false,
|
primary: false,
|
||||||
automaticallyImplyLeading: false,
|
automaticallyImplyLeading: false,
|
||||||
actions: [
|
actions: [
|
||||||
IconButton(
|
IconButton(icon: const Icon(Icons.refresh), onPressed: reload),
|
||||||
icon: const Icon(Icons.add),
|
|
||||||
onPressed: () {
|
|
||||||
Navigator.push(
|
|
||||||
context,
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) => LogEventDetailScreen(
|
|
||||||
logEntry: widget.endLogEntry!,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
).then((message) => refresh(message: message));
|
|
||||||
},
|
|
||||||
),
|
|
||||||
IconButton(icon: const Icon(Icons.refresh), onPressed: refresh),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
_activeLogEvents.isNotEmpty ?
|
_activeLogEvents.isNotEmpty ?
|
||||||
|
@ -2,7 +2,7 @@ import 'package:diameter/components/dialogs.dart';
|
|||||||
import 'package:diameter/config.dart';
|
import 'package:diameter/config.dart';
|
||||||
import 'package:diameter/models/log_entry.dart';
|
import 'package:diameter/models/log_entry.dart';
|
||||||
import 'package:diameter/navigation.dart';
|
import 'package:diameter/navigation.dart';
|
||||||
import 'package:diameter/screens/log/log_entry.dart';
|
import 'package:diameter/screens/log/log_entry/log_entry.dart';
|
||||||
import 'package:diameter/utils/date_time_utils.dart';
|
import 'package:diameter/utils/date_time_utils.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
@ -17,7 +17,13 @@ class LogScreen extends StatefulWidget {
|
|||||||
class _LogScreenState extends State<LogScreen> {
|
class _LogScreenState extends State<LogScreen> {
|
||||||
late Map<DateTime, List<LogEntry>> _logEntryDailyMap;
|
late Map<DateTime, List<LogEntry>> _logEntryDailyMap;
|
||||||
|
|
||||||
void refresh({String? message}) {
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
void reload({String? message}) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_logEntryDailyMap = LogEntry.getDailyEntryMap();
|
_logEntryDailyMap = LogEntry.getDailyEntryMap();
|
||||||
});
|
});
|
||||||
@ -36,7 +42,7 @@ class _LogScreenState extends State<LogScreen> {
|
|||||||
|
|
||||||
void onDelete(LogEntry logEntry) {
|
void onDelete(LogEntry logEntry) {
|
||||||
LogEntry.remove(logEntry.id);
|
LogEntry.remove(logEntry.id);
|
||||||
refresh(message: 'Log Entry deleted');
|
reload(message: 'Log Entry deleted');
|
||||||
}
|
}
|
||||||
|
|
||||||
void handleDeleteAction(LogEntry logEntry) async {
|
void handleDeleteAction(LogEntry logEntry) async {
|
||||||
@ -51,12 +57,6 @@ class _LogScreenState extends State<LogScreen> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
refresh();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
@ -64,7 +64,7 @@ class _LogScreenState extends State<LogScreen> {
|
|||||||
title: const Text('Log Entries'),
|
title: const Text('Log Entries'),
|
||||||
actions: <Widget>[
|
actions: <Widget>[
|
||||||
IconButton(
|
IconButton(
|
||||||
onPressed: refresh,
|
onPressed: reload,
|
||||||
icon: const Icon(Icons.refresh)
|
icon: const Icon(Icons.refresh)
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -101,7 +101,7 @@ class _LogScreenState extends State<LogScreen> {
|
|||||||
LogEntryScreen(
|
LogEntryScreen(
|
||||||
id: logEntry.id),
|
id: logEntry.id),
|
||||||
),
|
),
|
||||||
).then((message) => refresh(
|
).then((message) => reload(
|
||||||
message: message));
|
message: message));
|
||||||
},
|
},
|
||||||
title: Text(
|
title: Text(
|
||||||
@ -150,7 +150,7 @@ class _LogScreenState extends State<LogScreen> {
|
|||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (context) => const LogEntryScreen(),
|
builder: (context) => const LogEntryScreen(),
|
||||||
),
|
),
|
||||||
).then((message) => refresh(message: message));
|
).then((message) => reload(message: message));
|
||||||
},
|
},
|
||||||
child: const Icon(Icons.add),
|
child: const Icon(Icons.add),
|
||||||
),
|
),
|
||||||
|
226
lib/screens/log/log_entry/log_bolus_detail.dart
Normal file
226
lib/screens/log/log_entry/log_bolus_detail.dart
Normal file
@ -0,0 +1,226 @@
|
|||||||
|
import 'package:diameter/components/detail.dart';
|
||||||
|
import 'package:diameter/components/dialogs.dart';
|
||||||
|
import 'package:diameter/components/dropdown.dart';
|
||||||
|
import 'package:diameter/components/forms.dart';
|
||||||
|
import 'package:diameter/config.dart';
|
||||||
|
import 'package:diameter/models/bolus.dart';
|
||||||
|
import 'package:diameter/models/log_bolus.dart';
|
||||||
|
import 'package:diameter/models/log_entry.dart';
|
||||||
|
import 'package:diameter/models/log_meal.dart';
|
||||||
|
import 'package:diameter/navigation.dart';
|
||||||
|
import 'package:diameter/settings.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class LogBolusDetailScreen extends StatefulWidget {
|
||||||
|
static const String routeName = '/log-bolus';
|
||||||
|
|
||||||
|
final int logEntryId;
|
||||||
|
final int id;
|
||||||
|
|
||||||
|
const LogBolusDetailScreen({Key? key, this.logEntryId = 0, this.id = 0})
|
||||||
|
: super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
_LogBolusDetailScreenState createState() => _LogBolusDetailScreenState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _LogBolusDetailScreenState extends State<LogBolusDetailScreen> {
|
||||||
|
LogEntry? _logEntry;
|
||||||
|
LogBolus? _logBolus;
|
||||||
|
|
||||||
|
bool _isNew = true;
|
||||||
|
bool _isSaving = false;
|
||||||
|
|
||||||
|
final GlobalKey<FormState> _logBolusForm = GlobalKey<FormState>();
|
||||||
|
|
||||||
|
final _unitsController = TextEditingController(text: '');
|
||||||
|
final _carbsController = TextEditingController(text: '');
|
||||||
|
final _mgPerDlController = TextEditingController(text: '');
|
||||||
|
final _mmolPerLController = TextEditingController(text: '');
|
||||||
|
final _delayController = TextEditingController(text: '');
|
||||||
|
final _notesController = TextEditingController(text: '');
|
||||||
|
|
||||||
|
bool _manuallyAdjusted = false;
|
||||||
|
LogMeal? _meal;
|
||||||
|
Bolus? _rate;
|
||||||
|
|
||||||
|
List<LogMeal> _logMeals = [];
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
reload();
|
||||||
|
|
||||||
|
_logEntry = LogEntry.get(widget.logEntryId);
|
||||||
|
_logMeals = _logEntry?.meals ?? [];
|
||||||
|
|
||||||
|
if (widget.id != 0) {
|
||||||
|
_unitsController.text = _logBolus!.units.toString();
|
||||||
|
_carbsController.text = (_logBolus!.carbs ?? '').toString();
|
||||||
|
_mgPerDlController.text = (_logBolus!.mgPerDl ?? '').toString();
|
||||||
|
_mmolPerLController.text = (_logBolus!.mmolPerL ?? '').toString();
|
||||||
|
_delayController.text = (_logBolus!.delay ?? '').toString();
|
||||||
|
_notesController.text = _logBolus!.notes ?? '';
|
||||||
|
_manuallyAdjusted = _logBolus!.manuallyAdjusted;
|
||||||
|
_meal = _logBolus!.meal.target;
|
||||||
|
_rate = _logBolus!.rate.target ?? Bolus.getRateForTime(_logEntry?.time);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void reload() {
|
||||||
|
if (widget.id != 0) {
|
||||||
|
setState(() {
|
||||||
|
_logBolus = LogBolus.get(widget.id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
_isNew = _logBolus == null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> onSelectMeal(LogMeal meal) async {
|
||||||
|
setState(() {
|
||||||
|
_meal = meal;
|
||||||
|
if (meal.carbsPerPortion != null) {
|
||||||
|
_carbsController.text = meal.carbsPerPortion.toString();
|
||||||
|
|
||||||
|
if (_rate != null) {
|
||||||
|
_unitsController.text =
|
||||||
|
(meal.carbsPerPortion ?? 0 / (_rate!.carbs / _rate!.units))
|
||||||
|
.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleSaveAction() async {
|
||||||
|
setState(() {
|
||||||
|
_isSaving = true;
|
||||||
|
});
|
||||||
|
if (_logBolusForm.currentState!.validate()) {
|
||||||
|
LogBolus logBolus = LogBolus(
|
||||||
|
id: widget.id,
|
||||||
|
units: double.tryParse(_unitsController.text) ?? 0,
|
||||||
|
carbs: double.tryParse(_carbsController.text),
|
||||||
|
mgPerDl: int.tryParse(_mgPerDlController.text),
|
||||||
|
mmolPerL: double.tryParse(_mmolPerLController.text),
|
||||||
|
delay: int.tryParse(_delayController.text),
|
||||||
|
manuallyAdjusted: _manuallyAdjusted,
|
||||||
|
notes: _notesController.text,
|
||||||
|
);
|
||||||
|
logBolus.logEntry.target = _logEntry;
|
||||||
|
logBolus.meal.target = _meal;
|
||||||
|
logBolus.rate.target = _rate;
|
||||||
|
LogBolus.put(logBolus);
|
||||||
|
Navigator.pop(context, '${_isNew ? 'New' : ''} Bolus Saved');
|
||||||
|
}
|
||||||
|
setState(() {
|
||||||
|
_isSaving = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleCancelAction() {
|
||||||
|
if (showConfirmationDialogOnCancel &&
|
||||||
|
((_isNew &&
|
||||||
|
(_unitsController.text != '' ||
|
||||||
|
_carbsController.text != '' ||
|
||||||
|
_mgPerDlController.text != '' ||
|
||||||
|
_mmolPerLController.text != '' ||
|
||||||
|
_delayController.text != '' ||
|
||||||
|
_manuallyAdjusted ||
|
||||||
|
_notesController.text != '')) ||
|
||||||
|
(!_isNew &&
|
||||||
|
(double.tryParse(_unitsController.text) != _logBolus!.units ||
|
||||||
|
double.tryParse(_carbsController.text) != _logBolus!.carbs ||
|
||||||
|
int.tryParse(_mgPerDlController.text) != _logBolus!.mgPerDl ||
|
||||||
|
double.tryParse(_mmolPerLController.text) != _logBolus!.mmolPerL ||
|
||||||
|
int.tryParse(_delayController.text) != _logBolus!.delay ||
|
||||||
|
_manuallyAdjusted != _logBolus!.manuallyAdjusted ||
|
||||||
|
_notesController.text != (_logBolus!.notes ?? ''))))) {
|
||||||
|
Dialogs.showCancelConfirmationDialog(
|
||||||
|
context: context,
|
||||||
|
isNew: _isNew,
|
||||||
|
onSave: handleSaveAction,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
Navigator.pop(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: Text(_isNew ? 'New Bolus' : 'Edit Bolus'),
|
||||||
|
),
|
||||||
|
drawer: const Navigation(currentLocation: LogBolusDetailScreen.routeName),
|
||||||
|
body: SingleChildScrollView(
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
|
children: <Widget>[
|
||||||
|
FormWrapper(
|
||||||
|
formState: _logBolusForm,
|
||||||
|
fields: [
|
||||||
|
TextFormField(
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
labelText: 'Bolus Units',
|
||||||
|
suffixText: ' U',
|
||||||
|
),
|
||||||
|
controller: _unitsController,
|
||||||
|
keyboardType:
|
||||||
|
const TextInputType.numberWithOptions(decimal: true),
|
||||||
|
),
|
||||||
|
AutoCompleteDropdownButton<LogMeal>(
|
||||||
|
selectedItem: _meal,
|
||||||
|
label: 'Meal',
|
||||||
|
items: _logMeals,
|
||||||
|
renderItem: (item) => item.value,
|
||||||
|
onChanged: (value) {
|
||||||
|
if (value != null) {
|
||||||
|
onSelectMeal(value);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: TextFormField(
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: 'Carbs',
|
||||||
|
suffixText:
|
||||||
|
nutritionMeasurement == NutritionMeasurement.grams
|
||||||
|
? 'g'
|
||||||
|
: nutritionMeasurement ==
|
||||||
|
NutritionMeasurement.ounces
|
||||||
|
? 'oz'
|
||||||
|
: '',
|
||||||
|
),
|
||||||
|
controller: _carbsController,
|
||||||
|
keyboardType: const TextInputType.numberWithOptions(
|
||||||
|
decimal: true),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
TextFormField(
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
labelText: 'Delayed Bolus Duration',
|
||||||
|
suffixText: ' min',
|
||||||
|
),
|
||||||
|
controller: _delayController,
|
||||||
|
keyboardType: const TextInputType.numberWithOptions(),
|
||||||
|
),
|
||||||
|
TextFormField(
|
||||||
|
controller: _notesController,
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
labelText: 'Notes',
|
||||||
|
alignLabelWithHint: true,
|
||||||
|
),
|
||||||
|
keyboardType: TextInputType.multiline,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
bottomNavigationBar: DetailBottomRow(
|
||||||
|
onCancel: handleCancelAction,
|
||||||
|
onSave: _isSaving ? null : handleSaveAction,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
127
lib/screens/log/log_entry/log_bolus_list.dart
Normal file
127
lib/screens/log/log_entry/log_bolus_list.dart
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
import 'package:diameter/components/dialogs.dart';
|
||||||
|
import 'package:diameter/config.dart';
|
||||||
|
import 'package:diameter/models/log_bolus.dart';
|
||||||
|
import 'package:diameter/models/log_entry.dart';
|
||||||
|
import 'package:diameter/screens/log/log_entry/log_bolus_detail.dart';
|
||||||
|
import 'package:diameter/screens/log/log_entry/log_meal_detail.dart';
|
||||||
|
import 'package:diameter/settings.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class LogBolusListScreen extends StatefulWidget {
|
||||||
|
final LogEntry logEntry;
|
||||||
|
final List<LogBolus> logBoli;
|
||||||
|
final Function() reload;
|
||||||
|
|
||||||
|
const LogBolusListScreen(
|
||||||
|
{Key? key,
|
||||||
|
required this.logEntry,
|
||||||
|
this.logBoli = const [],
|
||||||
|
required this.reload})
|
||||||
|
: super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
_LogBolusListScreenState createState() => _LogBolusListScreenState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _LogBolusListScreenState extends State<LogBolusListScreen> {
|
||||||
|
void reload({String? message}) {
|
||||||
|
widget.reload();
|
||||||
|
|
||||||
|
setState(() {
|
||||||
|
if (message != null) {
|
||||||
|
var snackBar = SnackBar(
|
||||||
|
content: Text(message),
|
||||||
|
duration: const Duration(seconds: 2),
|
||||||
|
);
|
||||||
|
ScaffoldMessenger.of(context)
|
||||||
|
..removeCurrentSnackBar()
|
||||||
|
..showSnackBar(snackBar);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleEditAction(LogBolus logBolus) {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => LogBolusDetailScreen(
|
||||||
|
logEntryId: widget.logEntry.id,
|
||||||
|
id: logBolus.id,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
).then((message) => reload(message: message));
|
||||||
|
}
|
||||||
|
|
||||||
|
void onDelete(LogBolus logBolus) {
|
||||||
|
LogBolus.remove(logBolus.id);
|
||||||
|
reload(message: 'Meal deleted');
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleDeleteAction(LogBolus logBolus) async {
|
||||||
|
if (showConfirmationDialogOnDelete) {
|
||||||
|
Dialogs.showConfirmationDialog(
|
||||||
|
context: context,
|
||||||
|
onConfirm: () => onDelete(logBolus),
|
||||||
|
message: 'Are you sure you want to delete this Bolus?',
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
onDelete(logBolus);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleEditMealAction(int mealId) {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => LogMealDetailScreen(
|
||||||
|
logEntryId: widget.logEntry.id,
|
||||||
|
id: mealId,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
).then((message) => reload(message: message));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Column(
|
||||||
|
children: <Widget>[
|
||||||
|
Expanded(
|
||||||
|
child: widget.logEntry.boli.isNotEmpty
|
||||||
|
? ListView.builder(
|
||||||
|
shrinkWrap: true,
|
||||||
|
itemCount: widget.logEntry.boli.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final bolus = widget.logEntry.boli[index];
|
||||||
|
return ListTile(
|
||||||
|
onTap: () => handleEditAction(bolus),
|
||||||
|
title:
|
||||||
|
Text(
|
||||||
|
'${bolus.units} U per ${bolus.carbs}${nutritionMeasurement == NutritionMeasurement.grams ? ' g' : ' oz'} carbs/${glucoseMeasurement == GlucoseMeasurement.mgPerDl ? bolus.mgPerDl : bolus.mmolPerL} ${glucoseMeasurement == GlucoseMeasurement.mgPerDl ? 'mg/dl' : 'mmol/l'}'),
|
||||||
|
trailing: Row(
|
||||||
|
children: [
|
||||||
|
bolus.meal.target != null ? IconButton(
|
||||||
|
icon: const Icon(Icons.restaurant),
|
||||||
|
onPressed: () => handleEditMealAction(bolus.meal.targetId),
|
||||||
|
) : Container(),
|
||||||
|
const SizedBox(width: 24),
|
||||||
|
IconButton(
|
||||||
|
icon: const Icon(
|
||||||
|
Icons.delete,
|
||||||
|
color: Colors.blue,
|
||||||
|
),
|
||||||
|
onPressed: () => handleDeleteAction(bolus),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
)
|
||||||
|
: const Center(
|
||||||
|
child: Text(
|
||||||
|
'You have not added any Boli to this Log Entry yet!'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -2,12 +2,17 @@ import 'package:diameter/components/detail.dart';
|
|||||||
import 'package:diameter/components/dialogs.dart';
|
import 'package:diameter/components/dialogs.dart';
|
||||||
import 'package:diameter/components/forms.dart';
|
import 'package:diameter/components/forms.dart';
|
||||||
import 'package:diameter/config.dart';
|
import 'package:diameter/config.dart';
|
||||||
|
import 'package:diameter/models/log_bolus.dart';
|
||||||
import 'package:diameter/models/log_entry.dart';
|
import 'package:diameter/models/log_entry.dart';
|
||||||
|
import 'package:diameter/models/log_event.dart';
|
||||||
|
import 'package:diameter/models/log_meal.dart';
|
||||||
import 'package:diameter/navigation.dart';
|
import 'package:diameter/navigation.dart';
|
||||||
import 'package:diameter/screens/log/log_event_detail.dart';
|
import 'package:diameter/screens/log/log_entry/log_bolus_detail.dart';
|
||||||
import 'package:diameter/screens/log/log_event_list.dart';
|
import 'package:diameter/screens/log/log_entry/log_bolus_list.dart';
|
||||||
import 'package:diameter/screens/log/log_meal_detail.dart';
|
import 'package:diameter/screens/log/log_entry/log_event_list.dart';
|
||||||
import 'package:diameter/screens/log/log_meal_list.dart';
|
import 'package:diameter/screens/log/log_entry/log_event_detail.dart';
|
||||||
|
import 'package:diameter/screens/log/log_entry/log_meal_detail.dart';
|
||||||
|
import 'package:diameter/screens/log/log_entry/log_meal_list.dart';
|
||||||
import 'package:diameter/settings.dart';
|
import 'package:diameter/settings.dart';
|
||||||
import 'package:diameter/utils/date_time_utils.dart';
|
import 'package:diameter/utils/date_time_utils.dart';
|
||||||
import 'package:diameter/utils/utils.dart';
|
import 'package:diameter/utils/utils.dart';
|
||||||
@ -25,6 +30,9 @@ class LogEntryScreen extends StatefulWidget {
|
|||||||
|
|
||||||
class _LogEntryScreenState extends State<LogEntryScreen> {
|
class _LogEntryScreenState extends State<LogEntryScreen> {
|
||||||
LogEntry? _logEntry;
|
LogEntry? _logEntry;
|
||||||
|
List<LogMeal> _logMeals = [];
|
||||||
|
List<LogEvent> _logEvents = [];
|
||||||
|
List<LogBolus> _logBoli = [];
|
||||||
bool _isNew = true;
|
bool _isNew = true;
|
||||||
bool _isSaving = false;
|
bool _isSaving = false;
|
||||||
|
|
||||||
@ -42,6 +50,7 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
|
|||||||
final _notesController = TextEditingController(text: '');
|
final _notesController = TextEditingController(text: '');
|
||||||
|
|
||||||
late FloatingActionButton addMealButton;
|
late FloatingActionButton addMealButton;
|
||||||
|
late FloatingActionButton addBolusButton;
|
||||||
late FloatingActionButton addEventButton;
|
late FloatingActionButton addEventButton;
|
||||||
late IconButton refreshButton;
|
late IconButton refreshButton;
|
||||||
late IconButton closeButton;
|
late IconButton closeButton;
|
||||||
@ -62,6 +71,11 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
|
|||||||
child: const Icon(Icons.add),
|
child: const Icon(Icons.add),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
addBolusButton = FloatingActionButton(
|
||||||
|
onPressed: handleAddNewBolus,
|
||||||
|
child: const Icon(Icons.add),
|
||||||
|
);
|
||||||
|
|
||||||
addEventButton = FloatingActionButton(
|
addEventButton = FloatingActionButton(
|
||||||
onPressed: handleAddNewEvent,
|
onPressed: handleAddNewEvent,
|
||||||
child: const Icon(Icons.add),
|
child: const Icon(Icons.add),
|
||||||
@ -105,6 +119,9 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
|
|||||||
if (widget.id != 0) {
|
if (widget.id != 0) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_logEntry = LogEntry.get(widget.id);
|
_logEntry = LogEntry.get(widget.id);
|
||||||
|
_logMeals = _logEntry?.meals ?? [];
|
||||||
|
_logEvents = _logEntry?.events ?? [];
|
||||||
|
_logBoli = _logEntry?.boli ?? [];
|
||||||
});
|
});
|
||||||
_isNew = _logEntry == null;
|
_isNew = _logEntry == null;
|
||||||
}
|
}
|
||||||
@ -216,7 +233,18 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
|
|||||||
context,
|
context,
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
return LogMealDetailScreen(logEntry: _logEntry!);
|
return LogMealDetailScreen(logEntryId: _logEntry!.id);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
).then((message) => reload(message: message));
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleAddNewBolus() async {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) {
|
||||||
|
return LogBolusDetailScreen(logEntryId: _logEntry!.id);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
).then((message) => reload(message: message));
|
).then((message) => reload(message: message));
|
||||||
@ -227,7 +255,7 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
|
|||||||
context,
|
context,
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
return LogEventDetailScreen(logEntry: _logEntry!);
|
return LogEventDetailScreen(logEntryId: _logEntry!.id);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
).then((message) => reload(message: message));
|
).then((message) => reload(message: message));
|
||||||
@ -243,6 +271,11 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
|
|||||||
bottomNav = null;
|
bottomNav = null;
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
|
actionButton = addBolusButton;
|
||||||
|
appBarActions = [refreshButton, closeButton];
|
||||||
|
bottomNav = null;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
actionButton = addEventButton;
|
actionButton = addEventButton;
|
||||||
appBarActions = [refreshButton, closeButton];
|
appBarActions = [refreshButton, closeButton];
|
||||||
bottomNav = null;
|
bottomNav = null;
|
||||||
@ -259,7 +292,7 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return DefaultTabController(
|
return DefaultTabController(
|
||||||
length: _isNew ? 1 : 3,
|
length: _isNew ? 1 : 4,
|
||||||
child: Builder(builder: (BuildContext context) {
|
child: Builder(builder: (BuildContext context) {
|
||||||
final TabController tabController = DefaultTabController.of(context)!;
|
final TabController tabController = DefaultTabController.of(context)!;
|
||||||
tabController.addListener(() {
|
tabController.addListener(() {
|
||||||
@ -452,8 +485,12 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
|
|||||||
];
|
];
|
||||||
|
|
||||||
if (!_isNew) {
|
if (!_isNew) {
|
||||||
tabs.add(LogMealListScreen(logEntry: _logEntry!, reload: reload));
|
tabs.add(LogMealListScreen(
|
||||||
tabs.add(LogEventListScreen(logEntry: _logEntry!, reload: reload));
|
logEntry: _logEntry!, logMeals: _logMeals, reload: reload));
|
||||||
|
tabs.add(LogBolusListScreen(
|
||||||
|
logEntry: _logEntry!, logBoli: _logBoli, reload: reload));
|
||||||
|
tabs.add(LogEventListScreen(
|
||||||
|
logEntry: _logEntry!, logEvents: _logEvents, reload: reload));
|
||||||
}
|
}
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
@ -465,6 +502,7 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
|
|||||||
tabs: [
|
tabs: [
|
||||||
Tab(text: 'GENERAL'),
|
Tab(text: 'GENERAL'),
|
||||||
Tab(text: 'MEALS'),
|
Tab(text: 'MEALS'),
|
||||||
|
Tab(text: 'BOLI'),
|
||||||
Tab(text: 'EVENTS'),
|
Tab(text: 'EVENTS'),
|
||||||
],
|
],
|
||||||
),
|
),
|
@ -1,5 +1,6 @@
|
|||||||
import 'package:diameter/components/detail.dart';
|
import 'package:diameter/components/detail.dart';
|
||||||
import 'package:diameter/components/dialogs.dart';
|
import 'package:diameter/components/dialogs.dart';
|
||||||
|
import 'package:diameter/components/dropdown.dart';
|
||||||
import 'package:diameter/components/forms.dart';
|
import 'package:diameter/components/forms.dart';
|
||||||
import 'package:diameter/config.dart';
|
import 'package:diameter/config.dart';
|
||||||
import 'package:diameter/models/log_entry.dart';
|
import 'package:diameter/models/log_entry.dart';
|
||||||
@ -10,11 +11,13 @@ import 'package:flutter/material.dart';
|
|||||||
|
|
||||||
class LogEventDetailScreen extends StatefulWidget {
|
class LogEventDetailScreen extends StatefulWidget {
|
||||||
static const String routeName = '/log-event';
|
static const String routeName = '/log-event';
|
||||||
final LogEntry? logEntry;
|
|
||||||
final LogEntry? endLogEntry;
|
final int logEntryId;
|
||||||
final LogEvent? logEvent;
|
final int endLogEntryId;
|
||||||
|
final int id;
|
||||||
|
|
||||||
const LogEventDetailScreen(
|
const LogEventDetailScreen(
|
||||||
{Key? key, this.logEntry, this.endLogEntry, this.logEvent})
|
{Key? key, this.logEntryId = 0, this.endLogEntryId = 0, this.id = 0})
|
||||||
: super(key: key);
|
: super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -22,59 +25,55 @@ class LogEventDetailScreen extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _LogEventDetailScreenState extends State<LogEventDetailScreen> {
|
class _LogEventDetailScreenState extends State<LogEventDetailScreen> {
|
||||||
|
LogEvent? _logEvent;
|
||||||
|
bool _isNew = true;
|
||||||
|
bool _isSaving = false;
|
||||||
|
|
||||||
final GlobalKey<FormState> _logEventForm = GlobalKey<FormState>();
|
final GlobalKey<FormState> _logEventForm = GlobalKey<FormState>();
|
||||||
|
|
||||||
final _notesController = TextEditingController(text: '');
|
|
||||||
LogEventType? _eventType;
|
LogEventType? _eventType;
|
||||||
bool _hasEndTime = false;
|
bool _hasEndTime = false;
|
||||||
|
final _notesController = TextEditingController(text: '');
|
||||||
|
|
||||||
List<LogEventType> _logEventTypes = [];
|
List<LogEventType> _logEventTypes = [];
|
||||||
|
|
||||||
bool _isSaving = false;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
reload();
|
||||||
|
|
||||||
if (widget.logEvent != null) {
|
if (_logEvent != null) {
|
||||||
_notesController.text = widget.logEvent!.notes ?? '';
|
_notesController.text = _logEvent!.notes ?? '';
|
||||||
_eventType = widget.logEvent!.eventType.target;
|
_eventType = _logEvent!.eventType.target;
|
||||||
_hasEndTime = widget.logEvent!.hasEndTime;
|
_hasEndTime = _logEvent!.hasEndTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
_logEventTypes = LogEventType.getAll();
|
_logEventTypes = LogEventType.getAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void reload() {
|
||||||
|
if (widget.id != 0) {
|
||||||
|
setState(() {
|
||||||
|
_logEvent = LogEvent.get(widget.id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
_isNew = _logEvent == null;
|
||||||
|
}
|
||||||
|
|
||||||
void handleSaveAction() async {
|
void handleSaveAction() async {
|
||||||
setState(() {
|
setState(() {
|
||||||
_isSaving = true;
|
_isSaving = true;
|
||||||
});
|
});
|
||||||
if (_logEventForm.currentState!.validate()) {
|
if (_logEventForm.currentState!.validate()) {
|
||||||
bool isNew = widget.logEvent == null;
|
|
||||||
// isNew
|
|
||||||
// ? await LogEvent.save(
|
|
||||||
// logEntry: widget.logEntry!.objectId!,
|
|
||||||
// eventType: _eventType!,
|
|
||||||
// time: widget.logEntry!.time,
|
|
||||||
// hasEndTime: _hasEndTime,
|
|
||||||
// notes: _notesController.text,
|
|
||||||
// )
|
|
||||||
// : await LogEvent.update(
|
|
||||||
// widget.logEvent!.objectId!,
|
|
||||||
// eventType: _eventType!,
|
|
||||||
// time: widget.logEntry!.time,
|
|
||||||
// hasEndTime: _hasEndTime,
|
|
||||||
// notes: _notesController.text,
|
|
||||||
// );
|
|
||||||
LogEvent event = LogEvent(
|
LogEvent event = LogEvent(
|
||||||
id: widget.logEvent?.id ?? 0,
|
id: widget.id,
|
||||||
time: widget.logEntry!.time,
|
time: LogEntry.get(widget.logEntryId)!.time,
|
||||||
hasEndTime: _hasEndTime,
|
hasEndTime: _hasEndTime,
|
||||||
notes: _notesController.text,
|
notes: _notesController.text,
|
||||||
);
|
);
|
||||||
event.eventType.target = _eventType;
|
event.eventType.target = _eventType;
|
||||||
LogEvent.put(event);
|
LogEvent.put(event);
|
||||||
Navigator.pop(context, '${isNew ? 'New' : ''} Event Saved');
|
Navigator.pop(context, '${_isNew ? 'New' : ''} Event Saved');
|
||||||
}
|
}
|
||||||
setState(() {
|
setState(() {
|
||||||
_isSaving = false;
|
_isSaving = false;
|
||||||
@ -82,19 +81,18 @@ class _LogEventDetailScreenState extends State<LogEventDetailScreen> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void handleCancelAction() {
|
void handleCancelAction() {
|
||||||
bool isNew = widget.logEvent == null;
|
|
||||||
if (showConfirmationDialogOnCancel &&
|
if (showConfirmationDialogOnCancel &&
|
||||||
((isNew &&
|
((_isNew &&
|
||||||
(_notesController.text != '' ||
|
(_notesController.text != '' ||
|
||||||
_eventType != null ||
|
_eventType != null ||
|
||||||
_hasEndTime)) ||
|
_hasEndTime)) ||
|
||||||
(!isNew &&
|
(!_isNew &&
|
||||||
(_notesController.text != (widget.logEvent!.notes ?? '') ||
|
(_notesController.text != (_logEvent!.notes ?? '') ||
|
||||||
_eventType != widget.logEvent!.eventType.target ||
|
_eventType != _logEvent!.eventType.target ||
|
||||||
_hasEndTime != widget.logEvent!.hasEndTime)))) {
|
_hasEndTime != _logEvent!.hasEndTime)))) {
|
||||||
Dialogs.showCancelConfirmationDialog(
|
Dialogs.showCancelConfirmationDialog(
|
||||||
context: context,
|
context: context,
|
||||||
isNew: isNew,
|
isNew: _isNew,
|
||||||
onSave: handleSaveAction,
|
onSave: handleSaveAction,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
@ -104,10 +102,9 @@ class _LogEventDetailScreenState extends State<LogEventDetailScreen> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
bool isNew = widget.logEvent == null;
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text(isNew ? 'New Event' : 'Edit Event'),
|
title: Text(_isNew ? 'New Event' : 'Edit Event'),
|
||||||
),
|
),
|
||||||
drawer: const Navigation(currentLocation: LogEventDetailScreen.routeName),
|
drawer: const Navigation(currentLocation: LogEventDetailScreen.routeName),
|
||||||
body: SingleChildScrollView(
|
body: SingleChildScrollView(
|
||||||
@ -117,11 +114,11 @@ class _LogEventDetailScreenState extends State<LogEventDetailScreen> {
|
|||||||
FormWrapper(
|
FormWrapper(
|
||||||
formState: _logEventForm,
|
formState: _logEventForm,
|
||||||
fields: [
|
fields: [
|
||||||
LabeledDropdownButton<LogEventType>(
|
AutoCompleteDropdownButton<LogEventType>(
|
||||||
selectedItem: _eventType,
|
selectedItem: _eventType,
|
||||||
label: 'Event Type',
|
label: 'Event Type',
|
||||||
items: _logEventTypes,
|
items: _logEventTypes,
|
||||||
renderItem: (item) => Text(item.value),
|
renderItem: (item) => item.value,
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_eventType = value;
|
_eventType = value;
|
||||||
@ -147,7 +144,6 @@ class _LogEventDetailScreenState extends State<LogEventDetailScreen> {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
// ActiveLogEventListScreen(onSetEndTime: onSetEndTime)
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
@ -2,15 +2,17 @@ import 'package:diameter/components/dialogs.dart';
|
|||||||
import 'package:diameter/config.dart';
|
import 'package:diameter/config.dart';
|
||||||
import 'package:diameter/models/log_entry.dart';
|
import 'package:diameter/models/log_entry.dart';
|
||||||
import 'package:diameter/models/log_event.dart';
|
import 'package:diameter/models/log_event.dart';
|
||||||
import 'package:diameter/screens/log/log_event_detail.dart';
|
import 'package:diameter/screens/log/log_entry/log_event_detail.dart';
|
||||||
import 'package:diameter/utils/date_time_utils.dart';
|
import 'package:diameter/utils/date_time_utils.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class LogEventListScreen extends StatefulWidget {
|
class LogEventListScreen extends StatefulWidget {
|
||||||
final LogEntry logEntry;
|
final LogEntry logEntry;
|
||||||
|
final List<LogEvent> logEvents;
|
||||||
final Function() reload;
|
final Function() reload;
|
||||||
|
|
||||||
const LogEventListScreen({Key? key, required this.logEntry, required this.reload})
|
const LogEventListScreen(
|
||||||
|
{Key? key, required this.logEntry, this.logEvents = const [], required this.reload})
|
||||||
: super(key: key);
|
: super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -39,8 +41,8 @@ class _LogEventListScreenState extends State<LogEventListScreen> {
|
|||||||
context,
|
context,
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (context) => LogEventDetailScreen(
|
builder: (context) => LogEventDetailScreen(
|
||||||
endLogEntry: widget.logEntry,
|
logEntryId: widget.logEntry.id,
|
||||||
logEvent: event,
|
id: event.id,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
).then((message) => reload(message: message));
|
).then((message) => reload(message: message));
|
||||||
@ -66,15 +68,18 @@ class _LogEventListScreenState extends State<LogEventListScreen> {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: (widget.logEntry.events.isNotEmpty || widget.logEntry.endedEvents.isNotEmpty)
|
child: (widget.logEntry.events.isNotEmpty ||
|
||||||
|
widget.logEntry.endedEvents.isNotEmpty)
|
||||||
? ListView.builder(
|
? ListView.builder(
|
||||||
shrinkWrap: true,
|
shrinkWrap: true,
|
||||||
itemCount: widget.logEntry.events.length + widget.logEntry.endedEvents.length,
|
itemCount: widget.logEntry.events.length +
|
||||||
|
widget.logEntry.endedEvents.length,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
final event = (widget.logEntry.events + widget.logEntry.endedEvents)[index];
|
final event = (widget.logEntry.events +
|
||||||
|
widget.logEntry.endedEvents)[index];
|
||||||
return ListTile(
|
return ListTile(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
handleEditAction(event);
|
handleEditAction(event);
|
||||||
@ -103,10 +108,11 @@ class _LogEventListScreenState extends State<LogEventListScreen> {
|
|||||||
);
|
);
|
||||||
})
|
})
|
||||||
: const Center(
|
: const Center(
|
||||||
child: Text('You have not added any Events to this Log Entry yet!'),
|
child: Text(
|
||||||
),
|
'You have not added any Events to this Log Entry yet!'),
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,9 +1,9 @@
|
|||||||
import 'package:diameter/components/detail.dart';
|
import 'package:diameter/components/detail.dart';
|
||||||
import 'package:diameter/components/dialogs.dart';
|
import 'package:diameter/components/dialogs.dart';
|
||||||
|
import 'package:diameter/components/dropdown.dart';
|
||||||
import 'package:diameter/components/forms.dart';
|
import 'package:diameter/components/forms.dart';
|
||||||
import 'package:diameter/config.dart';
|
import 'package:diameter/config.dart';
|
||||||
import 'package:diameter/models/accuracy.dart';
|
import 'package:diameter/models/accuracy.dart';
|
||||||
import 'package:diameter/models/log_entry.dart';
|
|
||||||
import 'package:diameter/models/log_meal.dart';
|
import 'package:diameter/models/log_meal.dart';
|
||||||
import 'package:diameter/models/meal.dart';
|
import 'package:diameter/models/meal.dart';
|
||||||
import 'package:diameter/models/meal_category.dart';
|
import 'package:diameter/models/meal_category.dart';
|
||||||
@ -16,9 +16,11 @@ import 'package:flutter/material.dart';
|
|||||||
|
|
||||||
class LogMealDetailScreen extends StatefulWidget {
|
class LogMealDetailScreen extends StatefulWidget {
|
||||||
static const String routeName = '/log-meal';
|
static const String routeName = '/log-meal';
|
||||||
final LogEntry logEntry;
|
|
||||||
final LogMeal? logMeal;
|
final int logEntryId;
|
||||||
const LogMealDetailScreen({Key? key, required this.logEntry, this.logMeal})
|
final int id;
|
||||||
|
|
||||||
|
const LogMealDetailScreen({Key? key, this.logEntryId = 0, this.id = 0})
|
||||||
: super(key: key);
|
: super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -26,6 +28,10 @@ class LogMealDetailScreen extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
|
class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
|
||||||
|
LogMeal? _logMeal;
|
||||||
|
bool _isNew = true;
|
||||||
|
bool _isSaving = false;
|
||||||
|
|
||||||
final GlobalKey<FormState> _logMealForm = GlobalKey<FormState>();
|
final GlobalKey<FormState> _logMealForm = GlobalKey<FormState>();
|
||||||
|
|
||||||
final _valueController = TextEditingController(text: '');
|
final _valueController = TextEditingController(text: '');
|
||||||
@ -36,6 +42,7 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
|
|||||||
final _delayedBolusRateController = TextEditingController(text: '');
|
final _delayedBolusRateController = TextEditingController(text: '');
|
||||||
final _delayedBolusDurationController = TextEditingController(text: '');
|
final _delayedBolusDurationController = TextEditingController(text: '');
|
||||||
final _notesController = TextEditingController(text: '');
|
final _notesController = TextEditingController(text: '');
|
||||||
|
|
||||||
Meal? _meal;
|
Meal? _meal;
|
||||||
MealSource? _mealSource;
|
MealSource? _mealSource;
|
||||||
MealCategory? _mealCategory;
|
MealCategory? _mealCategory;
|
||||||
@ -50,11 +57,10 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
|
|||||||
List<Accuracy> _portionSizeAccuracies = [];
|
List<Accuracy> _portionSizeAccuracies = [];
|
||||||
List<Accuracy> _carbsRatioAccuracies = [];
|
List<Accuracy> _carbsRatioAccuracies = [];
|
||||||
|
|
||||||
bool _isSaving = false;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
reload();
|
||||||
|
|
||||||
_portionSizeAccuracies = Accuracy.getAllForPortionSize();
|
_portionSizeAccuracies = Accuracy.getAllForPortionSize();
|
||||||
_carbsRatioAccuracies = Accuracy.getAllForCarbsRatio();
|
_carbsRatioAccuracies = Accuracy.getAllForCarbsRatio();
|
||||||
@ -63,35 +69,30 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
|
|||||||
_mealPortionTypes = MealPortionType.getAll();
|
_mealPortionTypes = MealPortionType.getAll();
|
||||||
_mealSources = MealSource.getAll();
|
_mealSources = MealSource.getAll();
|
||||||
|
|
||||||
if (widget.logMeal != null) {
|
if (widget.id != 0) {
|
||||||
_valueController.text = widget.logMeal!.value;
|
_valueController.text = _logMeal!.value;
|
||||||
_carbsRatioController.text =
|
_carbsRatioController.text = (_logMeal!.carbsRatio ?? '').toString();
|
||||||
(widget.logMeal!.carbsRatio ?? '').toString();
|
_portionSizeController.text = (_logMeal!.portionSize ?? '').toString();
|
||||||
_portionSizeController.text =
|
|
||||||
(widget.logMeal!.portionSize ?? '').toString();
|
|
||||||
_carbsPerPortionController.text =
|
_carbsPerPortionController.text =
|
||||||
(widget.logMeal!.carbsPerPortion ?? '').toString();
|
(_logMeal!.carbsPerPortion ?? '').toString();
|
||||||
_bolusController.text = (widget.logMeal!.bolus ?? '').toString();
|
_bolusController.text = (_logMeal!.bolus ?? '').toString();
|
||||||
_delayedBolusRateController.text =
|
_delayedBolusRateController.text =
|
||||||
(widget.logMeal!.delayedBolusRate ?? '').toString();
|
(_logMeal!.delayedBolusRate ?? '').toString();
|
||||||
_delayedBolusDurationController.text =
|
_delayedBolusDurationController.text =
|
||||||
(widget.logMeal!.delayedBolusDuration ?? '').toString();
|
(_logMeal!.delayedBolusDuration ?? '').toString();
|
||||||
_notesController.text = widget.logMeal!.notes ?? '';
|
_notesController.text = _logMeal!.notes ?? '';
|
||||||
|
|
||||||
// _meal = widget.logMeal!.meal;
|
|
||||||
// _source = widget.logMeal!.source;
|
|
||||||
// _category = widget.logMeal!.category;
|
|
||||||
// _portionType = widget.logMeal!.portionType;
|
|
||||||
// _portionSizeAccuracy = _portionSizeAccuracies.firstWhere((element) =>
|
|
||||||
// element.id ==
|
|
||||||
// int.tryParse(widget.logMeal!.portionSizeAccuracy ?? ''));
|
|
||||||
// _carbsRatioAccuracy = _carbsRatioAccuracies.firstWhere((element) =>
|
|
||||||
// element.id == int.tryParse(widget.logMeal!.carbsRatioAccuracy ?? ''));
|
|
||||||
// _portionSizeAccuracy = widget.meal!.portionSizeAccuracy;
|
|
||||||
// _carbsRatioAccuracy = widget.meal!.carbsRatioAccuracy;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void reload() {
|
||||||
|
if (widget.id != 0) {
|
||||||
|
setState(() {
|
||||||
|
_logMeal = LogMeal.get(widget.id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
_isNew = _logMeal == null;
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> onSelectMeal(Meal meal) async {
|
Future<void> onSelectMeal(Meal meal) async {
|
||||||
setState(() {
|
setState(() {
|
||||||
_meal = meal;
|
_meal = meal;
|
||||||
@ -135,52 +136,8 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
|
|||||||
_isSaving = true;
|
_isSaving = true;
|
||||||
});
|
});
|
||||||
if (_logMealForm.currentState!.validate()) {
|
if (_logMealForm.currentState!.validate()) {
|
||||||
bool isNew = widget.logMeal == null;
|
|
||||||
// isNew
|
|
||||||
// ? await LogMeal.save(
|
|
||||||
// logEntry: widget.logEntry.objectId!,
|
|
||||||
// meal: _meal,
|
|
||||||
// value: _valueController.text,
|
|
||||||
// source: _mealSource,
|
|
||||||
// category: _category,
|
|
||||||
// portionType: _portionType,
|
|
||||||
// carbsRatio: double.tryParse(_carbsRatioController.text),
|
|
||||||
// portionSize: double.tryParse(_portionSizeController.text),
|
|
||||||
// carbsPerPortion: double.tryParse(_carbsPerPortionController.text),
|
|
||||||
// // portionSizeAccuracy: _portionSizeAccuracy,
|
|
||||||
// // carbsRatioAccuracy: _carbsRatioAccuracy,
|
|
||||||
// portionSizeAccuracy: _portionSizeAccuracy?.id.toString(),
|
|
||||||
// carbsRatioAccuracy: _carbsRatioAccuracy?.id.toString(),
|
|
||||||
// bolus: double.tryParse(_bolusController.text),
|
|
||||||
// delayedBolusDuration:
|
|
||||||
// int.tryParse(_delayedBolusDurationController.text),
|
|
||||||
// delayedBolusRate:
|
|
||||||
// double.tryParse(_delayedBolusRateController.text),
|
|
||||||
// notes: _notesController.text,
|
|
||||||
// )
|
|
||||||
// : await LogMeal.update(
|
|
||||||
// widget.logMeal!.objectId!,
|
|
||||||
// meal: _meal,
|
|
||||||
// value: _valueController.text,
|
|
||||||
// source: _mealSource,
|
|
||||||
// category: _category,
|
|
||||||
// portionType: _portionType,
|
|
||||||
// carbsRatio: double.tryParse(_carbsRatioController.text),
|
|
||||||
// portionSize: double.tryParse(_portionSizeController.text),
|
|
||||||
// carbsPerPortion: double.tryParse(_carbsPerPortionController.text),
|
|
||||||
// // portionSizeAccuracy: _portionSizeAccuracy,
|
|
||||||
// // carbsRatioAccuracy: _carbsRatioAccuracy,
|
|
||||||
// portionSizeAccuracy: _portionSizeAccuracy?.id.toString(),
|
|
||||||
// carbsRatioAccuracy: _carbsRatioAccuracy?.id.toString(),
|
|
||||||
// bolus: double.tryParse(_bolusController.text),
|
|
||||||
// delayedBolusDuration:
|
|
||||||
// int.tryParse(_delayedBolusDurationController.text),
|
|
||||||
// delayedBolusRate:
|
|
||||||
// double.tryParse(_delayedBolusRateController.text),
|
|
||||||
// notes: _notesController.text,
|
|
||||||
// );
|
|
||||||
LogMeal logMeal = LogMeal(
|
LogMeal logMeal = LogMeal(
|
||||||
id: widget.logMeal?.id ?? 0,
|
id: widget.id,
|
||||||
value: _valueController.text,
|
value: _valueController.text,
|
||||||
carbsRatio: double.tryParse(_carbsRatioController.text),
|
carbsRatio: double.tryParse(_carbsRatioController.text),
|
||||||
portionSize: double.tryParse(_portionSizeController.text),
|
portionSize: double.tryParse(_portionSizeController.text),
|
||||||
@ -197,9 +154,9 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
|
|||||||
logMeal.mealPortionType.target = _mealPortionType;
|
logMeal.mealPortionType.target = _mealPortionType;
|
||||||
logMeal.portionSizeAccuracy.target = _portionSizeAccuracy;
|
logMeal.portionSizeAccuracy.target = _portionSizeAccuracy;
|
||||||
logMeal.carbsRatioAccuracy.target = _carbsRatioAccuracy;
|
logMeal.carbsRatioAccuracy.target = _carbsRatioAccuracy;
|
||||||
|
|
||||||
LogMeal.put(logMeal);
|
LogMeal.put(logMeal);
|
||||||
Navigator.pop(context, '${isNew ? 'New' : ''} Meal Saved');
|
Navigator.pop(context, '${_isNew ? 'New' : ''} Meal Saved');
|
||||||
}
|
}
|
||||||
setState(() {
|
setState(() {
|
||||||
_isSaving = false;
|
_isSaving = false;
|
||||||
@ -207,9 +164,8 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void handleCancelAction() {
|
void handleCancelAction() {
|
||||||
bool isNew = widget.logMeal == null;
|
|
||||||
if (showConfirmationDialogOnCancel &&
|
if (showConfirmationDialogOnCancel &&
|
||||||
((isNew &&
|
((_isNew &&
|
||||||
(_valueController.text != '' ||
|
(_valueController.text != '' ||
|
||||||
_meal != null ||
|
_meal != null ||
|
||||||
_mealSource != null ||
|
_mealSource != null ||
|
||||||
@ -225,35 +181,32 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
|
|||||||
null ||
|
null ||
|
||||||
double.tryParse(_delayedBolusRateController.text) != null ||
|
double.tryParse(_delayedBolusRateController.text) != null ||
|
||||||
_notesController.text != '')) ||
|
_notesController.text != '')) ||
|
||||||
(!isNew &&
|
(!_isNew &&
|
||||||
(_valueController.text != widget.logMeal!.value ||
|
(_valueController.text != _logMeal!.value ||
|
||||||
_meal != widget.logMeal!.meal.target ||
|
_meal != _logMeal!.meal.target ||
|
||||||
_mealSource != widget.logMeal!.mealSource.target ||
|
_mealSource != _logMeal!.mealSource.target ||
|
||||||
_mealCategory != widget.logMeal!.mealCategory.target ||
|
_mealCategory != _logMeal!.mealCategory.target ||
|
||||||
_mealPortionType != widget.logMeal!.mealPortionType.target ||
|
_mealPortionType != _logMeal!.mealPortionType.target ||
|
||||||
double.tryParse(_carbsRatioController.text) !=
|
double.tryParse(_carbsRatioController.text) !=
|
||||||
widget.logMeal!.carbsRatio ||
|
_logMeal!.carbsRatio ||
|
||||||
double.tryParse(_portionSizeController.text) !=
|
double.tryParse(_portionSizeController.text) !=
|
||||||
widget.logMeal!.portionSize ||
|
_logMeal!.portionSize ||
|
||||||
double.tryParse(_carbsPerPortionController.text) !=
|
double.tryParse(_carbsPerPortionController.text) !=
|
||||||
widget.logMeal!.carbsPerPortion ||
|
_logMeal!.carbsPerPortion ||
|
||||||
// _carbsRatioAccuracy != widget.logMeal!.carbsRatioAccuracy ||
|
|
||||||
// _portionSizeAccuracy !=
|
|
||||||
// widget.logMeal!.portionSizeAccuracy ||
|
|
||||||
_carbsRatioAccuracy !=
|
_carbsRatioAccuracy !=
|
||||||
widget.logMeal!.carbsRatioAccuracy.target ||
|
_logMeal!.carbsRatioAccuracy.target ||
|
||||||
_portionSizeAccuracy !=
|
_portionSizeAccuracy !=
|
||||||
widget.logMeal!.portionSizeAccuracy.target ||
|
_logMeal!.portionSizeAccuracy.target ||
|
||||||
double.tryParse(_bolusController.text) !=
|
double.tryParse(_bolusController.text) !=
|
||||||
widget.logMeal!.bolus ||
|
_logMeal!.bolus ||
|
||||||
int.tryParse(_delayedBolusDurationController.text) !=
|
int.tryParse(_delayedBolusDurationController.text) !=
|
||||||
widget.logMeal!.delayedBolusDuration ||
|
_logMeal!.delayedBolusDuration ||
|
||||||
double.tryParse(_delayedBolusRateController.text) !=
|
double.tryParse(_delayedBolusRateController.text) !=
|
||||||
widget.logMeal!.delayedBolusRate ||
|
_logMeal!.delayedBolusRate ||
|
||||||
_notesController.text != (widget.logMeal!.notes ?? ''))))) {
|
_notesController.text != (_logMeal!.notes ?? ''))))) {
|
||||||
Dialogs.showCancelConfirmationDialog(
|
Dialogs.showCancelConfirmationDialog(
|
||||||
context: context,
|
context: context,
|
||||||
isNew: isNew,
|
isNew: _isNew,
|
||||||
onSave: handleSaveAction,
|
onSave: handleSaveAction,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
@ -305,10 +258,9 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
bool isNew = widget.logMeal == null;
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text(isNew ? 'New Meal' : widget.logMeal!.value),
|
title: Text(_isNew ? 'New Meal' : _logMeal!.value),
|
||||||
),
|
),
|
||||||
drawer: const Navigation(currentLocation: LogMealDetailScreen.routeName),
|
drawer: const Navigation(currentLocation: LogMealDetailScreen.routeName),
|
||||||
body: SingleChildScrollView(
|
body: SingleChildScrollView(
|
||||||
@ -330,48 +282,44 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
|
|||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
LabeledDropdownButton<Meal>(
|
AutoCompleteDropdownButton<Meal>(
|
||||||
selectedItem: _meal,
|
selectedItem: _meal,
|
||||||
label: 'Meal',
|
label: 'Meal',
|
||||||
items: _meals,
|
items: _meals,
|
||||||
// getItemValue: (item) => item.objectId,
|
renderItem: (item) => item.value,
|
||||||
renderItem: (item) => Text(item.value),
|
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
onSelectMeal(value);
|
onSelectMeal(value);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
LabeledDropdownButton<MealSource>(
|
AutoCompleteDropdownButton<MealSource>(
|
||||||
selectedItem: _mealSource,
|
selectedItem: _mealSource,
|
||||||
label: 'Meal Source',
|
label: 'Meal Source',
|
||||||
items: _mealSources,
|
items: _mealSources,
|
||||||
// getItemValue: (item) => item.objectId,
|
renderItem: (item) => item.value,
|
||||||
renderItem: (item) => Text(item.value),
|
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_mealSource = value;
|
_mealSource = value;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
LabeledDropdownButton<MealCategory>(
|
AutoCompleteDropdownButton<MealCategory>(
|
||||||
selectedItem: _mealCategory,
|
selectedItem: _mealCategory,
|
||||||
label: 'Meal Category',
|
label: 'Meal Category',
|
||||||
items: _mealCategories,
|
items: _mealCategories,
|
||||||
// getItemValue: (item) => item.objectId,
|
renderItem: (item) => item.value,
|
||||||
renderItem: (item) => Text(item.value),
|
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_mealCategory = value;
|
_mealCategory = value;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
LabeledDropdownButton<MealPortionType>(
|
AutoCompleteDropdownButton<MealPortionType>(
|
||||||
selectedItem: _mealPortionType,
|
selectedItem: _mealPortionType,
|
||||||
label: 'Meal Portion Type',
|
label: 'Meal Portion Type',
|
||||||
items: _mealPortionTypes,
|
items: _mealPortionTypes,
|
||||||
// getItemValue: (item) => item.objectId,
|
renderItem: (item) => item.value,
|
||||||
renderItem: (item) => Text(item.value),
|
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_mealPortionType = value;
|
_mealPortionType = value;
|
||||||
@ -434,30 +382,18 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
LabeledDropdownButton<Accuracy>(
|
AutoCompleteDropdownButton<Accuracy>(
|
||||||
selectedItem: _portionSizeAccuracy,
|
selectedItem: _portionSizeAccuracy,
|
||||||
label: 'Portion Size Accuracy',
|
label: 'Portion Size Accuracy',
|
||||||
items: _portionSizeAccuracies,
|
items: _portionSizeAccuracies,
|
||||||
// getItemValue: (item) => item.objectId,
|
// getItemValue: (item) => item.objectId,
|
||||||
renderItem: (item) => Text(item.value),
|
renderItem: (item) => item.value,
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_portionSizeAccuracy = value;
|
_portionSizeAccuracy = value;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
// StyledFutureDropdownButton<Accuracy>(
|
|
||||||
// selectedItem: _portionSizeAccuracy,
|
|
||||||
// label: 'Portion Size Accuracy',
|
|
||||||
// items: _portionSizeAccuracies,
|
|
||||||
// getItemValue: (item) => item.objectId,
|
|
||||||
// renderItem: (item) => Text(item.value),
|
|
||||||
// onChanged: (value) {
|
|
||||||
// setState(() {
|
|
||||||
// _portionSizeAccuracy = value;
|
|
||||||
// });
|
|
||||||
// },
|
|
||||||
// ),
|
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
@ -488,30 +424,17 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
LabeledDropdownButton<Accuracy>(
|
AutoCompleteDropdownButton<Accuracy>(
|
||||||
selectedItem: _carbsRatioAccuracy,
|
selectedItem: _carbsRatioAccuracy,
|
||||||
label: 'Carbs Ratio Accuracy',
|
label: 'Carbs Ratio Accuracy',
|
||||||
items: _carbsRatioAccuracies,
|
items: _carbsRatioAccuracies,
|
||||||
// getItemValue: (item) => item.objectId,
|
renderItem: (item) => item.value,
|
||||||
renderItem: (item) => Text(item.value),
|
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_carbsRatioAccuracy = value;
|
_carbsRatioAccuracy = value;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
// StyledFutureDropdownButton<Accuracy>(
|
|
||||||
// selectedItem: _carbsRatioAccuracy,
|
|
||||||
// label: 'Carbs Ratio Accuracy',
|
|
||||||
// items: _carbsRatioAccuracies,
|
|
||||||
// getItemValue: (item) => item.objectId,
|
|
||||||
// renderItem: (item) => Text(item.value),
|
|
||||||
// onChanged: (value) {
|
|
||||||
// setState(() {
|
|
||||||
// _carbsRatioAccuracy = value;
|
|
||||||
// });
|
|
||||||
// },
|
|
||||||
// ),
|
|
||||||
TextFormField(
|
TextFormField(
|
||||||
decoration: const InputDecoration(
|
decoration: const InputDecoration(
|
||||||
labelText: 'Bolus Units',
|
labelText: 'Bolus Units',
|
110
lib/screens/log/log_entry/log_meal_list.dart
Normal file
110
lib/screens/log/log_entry/log_meal_list.dart
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
import 'package:diameter/components/dialogs.dart';
|
||||||
|
import 'package:diameter/config.dart';
|
||||||
|
import 'package:diameter/models/log_entry.dart';
|
||||||
|
import 'package:diameter/models/log_meal.dart';
|
||||||
|
import 'package:diameter/screens/log/log_entry/log_meal_detail.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class LogMealListScreen extends StatefulWidget {
|
||||||
|
final LogEntry logEntry;
|
||||||
|
final List<LogMeal> logMeals;
|
||||||
|
final Function() reload;
|
||||||
|
|
||||||
|
const LogMealListScreen(
|
||||||
|
{Key? key, required this.logEntry, this.logMeals = const [], required this.reload})
|
||||||
|
: super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
_LogMealListScreenState createState() => _LogMealListScreenState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _LogMealListScreenState extends State<LogMealListScreen> {
|
||||||
|
void reload({String? message}) {
|
||||||
|
widget.reload();
|
||||||
|
|
||||||
|
setState(() {
|
||||||
|
if (message != null) {
|
||||||
|
var snackBar = SnackBar(
|
||||||
|
content: Text(message),
|
||||||
|
duration: const Duration(seconds: 2),
|
||||||
|
);
|
||||||
|
ScaffoldMessenger.of(context)
|
||||||
|
..removeCurrentSnackBar()
|
||||||
|
..showSnackBar(snackBar);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleEditAction(LogMeal meal) {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => LogMealDetailScreen(
|
||||||
|
logEntryId: widget.logEntry.id,
|
||||||
|
id: meal.id,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
).then((message) => reload(message: message));
|
||||||
|
}
|
||||||
|
|
||||||
|
void onDelete(LogMeal logMeal) {
|
||||||
|
LogMeal.remove(logMeal.id);
|
||||||
|
reload(message: 'Meal deleted');
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleDeleteAction(LogMeal meal) async {
|
||||||
|
if (showConfirmationDialogOnDelete) {
|
||||||
|
Dialogs.showConfirmationDialog(
|
||||||
|
context: context,
|
||||||
|
onConfirm: () => onDelete(meal),
|
||||||
|
message: 'Are you sure you want to delete this Meal?',
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
onDelete(meal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Column(
|
||||||
|
children: <Widget>[
|
||||||
|
Expanded(
|
||||||
|
child: widget.logEntry.meals.isNotEmpty
|
||||||
|
? ListView.builder(
|
||||||
|
shrinkWrap: true,
|
||||||
|
itemCount: widget.logEntry.meals.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final meal = widget.logEntry.meals[index];
|
||||||
|
return ListTile(
|
||||||
|
onTap: () => handleEditAction(meal),
|
||||||
|
title: Row(
|
||||||
|
children: [
|
||||||
|
Expanded(child: Text(meal.value)),
|
||||||
|
Expanded(
|
||||||
|
child: Text(meal.carbsPerPortion != null
|
||||||
|
? '${meal.carbsPerPortion} g carbs'
|
||||||
|
: '')),
|
||||||
|
Expanded(
|
||||||
|
child: Text(
|
||||||
|
meal.bolus != null ? '${meal.bolus} U' : ''))
|
||||||
|
],
|
||||||
|
),
|
||||||
|
trailing: IconButton(
|
||||||
|
icon: const Icon(
|
||||||
|
Icons.delete,
|
||||||
|
color: Colors.blue,
|
||||||
|
),
|
||||||
|
onPressed: () => handleDeleteAction(meal),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
)
|
||||||
|
: const Center(
|
||||||
|
child: Text(
|
||||||
|
'You have not added any Meals to this Log Entry yet!'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -8,9 +8,8 @@ import 'package:flutter/material.dart';
|
|||||||
|
|
||||||
class LogEventTypeDetailScreen extends StatefulWidget {
|
class LogEventTypeDetailScreen extends StatefulWidget {
|
||||||
static const String routeName = '/log-event-type';
|
static const String routeName = '/log-event-type';
|
||||||
final LogEventType? logEventType;
|
final int id;
|
||||||
const LogEventTypeDetailScreen({Key? key, this.logEventType})
|
const LogEventTypeDetailScreen({Key? key, this.id = 0}) : super(key: key);
|
||||||
: super(key: key);
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_LogEventTypeDetailScreenState createState() =>
|
_LogEventTypeDetailScreenState createState() =>
|
||||||
@ -18,35 +17,49 @@ class LogEventTypeDetailScreen extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _LogEventTypeDetailScreenState extends State<LogEventTypeDetailScreen> {
|
class _LogEventTypeDetailScreenState extends State<LogEventTypeDetailScreen> {
|
||||||
|
LogEventType? _logEventType;
|
||||||
|
bool _isNew = true;
|
||||||
|
bool _isSaving = false;
|
||||||
|
|
||||||
final GlobalKey<FormState> _logEventTypeForm = GlobalKey<FormState>();
|
final GlobalKey<FormState> _logEventTypeForm = GlobalKey<FormState>();
|
||||||
|
|
||||||
final _valueController = TextEditingController(text: '');
|
final _valueController = TextEditingController(text: '');
|
||||||
final _defaultReminderDurationController = TextEditingController(text: '');
|
final _defaultReminderDurationController = TextEditingController(text: '');
|
||||||
final _notesController = TextEditingController(text: '');
|
final _notesController = TextEditingController(text: '');
|
||||||
bool _hasEndTime = false;
|
bool _hasEndTime = false;
|
||||||
|
|
||||||
bool _isSaving = false;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
|
||||||
if (widget.logEventType != null) {
|
reload();
|
||||||
_valueController.text = widget.logEventType!.value;
|
|
||||||
|
if (_logEventType != null) {
|
||||||
|
_valueController.text = _logEventType!.value;
|
||||||
_defaultReminderDurationController.text =
|
_defaultReminderDurationController.text =
|
||||||
(widget.logEventType!.defaultReminderDuration ?? '').toString();
|
(_logEventType!.defaultReminderDuration ?? '').toString();
|
||||||
_notesController.text = widget.logEventType!.notes ?? '';
|
_notesController.text = _logEventType!.notes ?? '';
|
||||||
_hasEndTime = widget.logEventType!.hasEndTime;
|
_hasEndTime = _logEventType!.hasEndTime;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void reload() {
|
||||||
|
if (widget.id != 0) {
|
||||||
|
setState(() {
|
||||||
|
_logEventType = LogEventType.get(widget.id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
_isNew = _logEventType == null;
|
||||||
|
}
|
||||||
|
|
||||||
void handleSaveAction() async {
|
void handleSaveAction() async {
|
||||||
setState(() {
|
setState(() {
|
||||||
_isSaving = true;
|
_isSaving = true;
|
||||||
});
|
});
|
||||||
if (_logEventTypeForm.currentState!.validate()) {
|
if (_logEventTypeForm.currentState!.validate()) {
|
||||||
bool isNew = widget.logEventType == null;
|
bool isNew = _logEventType == null;
|
||||||
LogEventType.put(LogEventType(
|
LogEventType.put(LogEventType(
|
||||||
id: widget.logEventType?.id ?? 0,
|
id: widget.id,
|
||||||
value: _valueController.text,
|
value: _valueController.text,
|
||||||
notes: _notesController.text,
|
notes: _notesController.text,
|
||||||
defaultReminderDuration:
|
defaultReminderDuration:
|
||||||
@ -61,7 +74,7 @@ class _LogEventTypeDetailScreenState extends State<LogEventTypeDetailScreen> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void handleCancelAction() {
|
void handleCancelAction() {
|
||||||
bool isNew = widget.logEventType == null;
|
bool isNew = _logEventType == null;
|
||||||
if (showConfirmationDialogOnCancel &&
|
if (showConfirmationDialogOnCancel &&
|
||||||
((isNew &&
|
((isNew &&
|
||||||
(_valueController.text != '' ||
|
(_valueController.text != '' ||
|
||||||
@ -70,12 +83,11 @@ class _LogEventTypeDetailScreenState extends State<LogEventTypeDetailScreen> {
|
|||||||
_notesController.text != '' ||
|
_notesController.text != '' ||
|
||||||
_hasEndTime)) ||
|
_hasEndTime)) ||
|
||||||
(!isNew &&
|
(!isNew &&
|
||||||
(_valueController.text != widget.logEventType!.value ||
|
(_valueController.text != _logEventType!.value ||
|
||||||
int.tryParse(_defaultReminderDurationController.text) !=
|
int.tryParse(_defaultReminderDurationController.text) !=
|
||||||
widget.logEventType!.defaultReminderDuration ||
|
_logEventType!.defaultReminderDuration ||
|
||||||
_notesController.text !=
|
_notesController.text != (_logEventType!.notes ?? '') ||
|
||||||
(widget.logEventType!.notes ?? '') ||
|
_hasEndTime != _logEventType!.hasEndTime)))) {
|
||||||
_hasEndTime != widget.logEventType!.hasEndTime)))) {
|
|
||||||
Dialogs.showCancelConfirmationDialog(
|
Dialogs.showCancelConfirmationDialog(
|
||||||
context: context,
|
context: context,
|
||||||
isNew: isNew,
|
isNew: isNew,
|
||||||
@ -88,10 +100,9 @@ class _LogEventTypeDetailScreenState extends State<LogEventTypeDetailScreen> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
bool isNew = widget.logEventType == null;
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text(isNew ? 'New Log Event Type' : widget.logEventType!.value),
|
title: Text(_isNew ? 'New Log Event Type' : _logEventType!.value),
|
||||||
),
|
),
|
||||||
drawer:
|
drawer:
|
||||||
const Navigation(currentLocation: LogEventTypeDetailScreen.routeName),
|
const Navigation(currentLocation: LogEventTypeDetailScreen.routeName),
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
// import 'package:diameter/components/progress_indicator.dart';
|
|
||||||
import 'package:diameter/models/log_event_type.dart';
|
import 'package:diameter/models/log_event_type.dart';
|
||||||
import 'package:diameter/navigation.dart';
|
import 'package:diameter/navigation.dart';
|
||||||
import 'package:diameter/screens/log/log_event_type_detail.dart';
|
import 'package:diameter/screens/log/log_event_type_detail.dart';
|
||||||
@ -15,7 +14,13 @@ class LogEventTypeListScreen extends StatefulWidget {
|
|||||||
class _LogEventTypeListScreenState extends State<LogEventTypeListScreen> {
|
class _LogEventTypeListScreenState extends State<LogEventTypeListScreen> {
|
||||||
List<LogEventType> _logEventTypes = [];
|
List<LogEventType> _logEventTypes = [];
|
||||||
|
|
||||||
void refresh({String? message}) {
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
void reload({String? message}) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_logEventTypes = LogEventType.getAll();
|
_logEventTypes = LogEventType.getAll();
|
||||||
});
|
});
|
||||||
@ -32,17 +37,11 @@ class _LogEventTypeListScreenState extends State<LogEventTypeListScreen> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
refresh();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(title: const Text('Log Event Types'), actions: <Widget>[
|
appBar: AppBar(title: const Text('Log Event Types'), actions: <Widget>[
|
||||||
IconButton(onPressed: refresh, icon: const Icon(Icons.refresh))
|
IconButton(onPressed: reload, icon: const Icon(Icons.refresh))
|
||||||
]),
|
]),
|
||||||
drawer:
|
drawer:
|
||||||
const Navigation(currentLocation: LogEventTypeListScreen.routeName),
|
const Navigation(currentLocation: LogEventTypeListScreen.routeName),
|
||||||
@ -54,7 +53,6 @@ class _LogEventTypeListScreenState extends State<LogEventTypeListScreen> {
|
|||||||
padding: const EdgeInsets.all(10.0),
|
padding: const EdgeInsets.all(10.0),
|
||||||
itemCount: _logEventTypes.length,
|
itemCount: _logEventTypes.length,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
// final logEventType = snapshot.data![index];
|
|
||||||
final logEventType = _logEventTypes[index];
|
final logEventType = _logEventTypes[index];
|
||||||
return ListTile(
|
return ListTile(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
@ -63,9 +61,9 @@ class _LogEventTypeListScreenState extends State<LogEventTypeListScreen> {
|
|||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (context) =>
|
builder: (context) =>
|
||||||
LogEventTypeDetailScreen(
|
LogEventTypeDetailScreen(
|
||||||
logEventType: logEventType),
|
id: logEventType.id),
|
||||||
),
|
),
|
||||||
).then((message) => refresh(message: message));
|
).then((message) => reload(message: message));
|
||||||
},
|
},
|
||||||
title: Text(logEventType.value),
|
title: Text(logEventType.value),
|
||||||
subtitle: Text(logEventType.notes ?? ''),
|
subtitle: Text(logEventType.notes ?? ''),
|
||||||
@ -76,7 +74,7 @@ class _LogEventTypeListScreenState extends State<LogEventTypeListScreen> {
|
|||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
LogEventType.remove(logEventType.id);
|
LogEventType.remove(logEventType.id);
|
||||||
// await logEventType.delete().then((_) {
|
// await logEventType.delete().then((_) {
|
||||||
refresh(
|
reload(
|
||||||
message: 'Log Event Type deleted');
|
message: 'Log Event Type deleted');
|
||||||
// });
|
// });
|
||||||
},
|
},
|
||||||
@ -100,7 +98,7 @@ class _LogEventTypeListScreenState extends State<LogEventTypeListScreen> {
|
|||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (context) => const LogEventTypeDetailScreen(),
|
builder: (context) => const LogEventTypeDetailScreen(),
|
||||||
),
|
),
|
||||||
).then((message) => refresh(message: message));
|
).then((message) => reload(message: message));
|
||||||
},
|
},
|
||||||
child: const Icon(Icons.add),
|
child: const Icon(Icons.add),
|
||||||
),
|
),
|
||||||
|
@ -1,107 +0,0 @@
|
|||||||
import 'package:diameter/components/dialogs.dart';
|
|
||||||
import 'package:diameter/config.dart';
|
|
||||||
import 'package:diameter/models/log_entry.dart';
|
|
||||||
import 'package:diameter/models/log_meal.dart';
|
|
||||||
import 'package:diameter/screens/log/log_meal_detail.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
|
|
||||||
class LogMealListScreen extends StatefulWidget {
|
|
||||||
final LogEntry logEntry;
|
|
||||||
final Function() reload;
|
|
||||||
|
|
||||||
const LogMealListScreen({Key? key, required this.logEntry, required this.reload})
|
|
||||||
: super(key: key);
|
|
||||||
|
|
||||||
@override
|
|
||||||
_LogMealListScreenState createState() => _LogMealListScreenState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _LogMealListScreenState extends State<LogMealListScreen> {
|
|
||||||
void reload({String? message}) {
|
|
||||||
widget.reload();
|
|
||||||
|
|
||||||
setState(() {
|
|
||||||
if (message != null) {
|
|
||||||
var snackBar = SnackBar(
|
|
||||||
content: Text(message),
|
|
||||||
duration: const Duration(seconds: 2),
|
|
||||||
);
|
|
||||||
ScaffoldMessenger.of(context)
|
|
||||||
..removeCurrentSnackBar()
|
|
||||||
..showSnackBar(snackBar);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void handleEditAction(LogMeal meal) {
|
|
||||||
Navigator.push(
|
|
||||||
context,
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) => LogMealDetailScreen(
|
|
||||||
logEntry: widget.logEntry,
|
|
||||||
logMeal: meal,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
).then((message) => reload(message: message));
|
|
||||||
}
|
|
||||||
|
|
||||||
void onDelete(LogMeal logMeal) {
|
|
||||||
LogMeal.remove(logMeal.id);
|
|
||||||
reload(message: 'Meal deleted');
|
|
||||||
}
|
|
||||||
|
|
||||||
void handleDeleteAction(LogMeal meal) async {
|
|
||||||
if (showConfirmationDialogOnDelete) {
|
|
||||||
Dialogs.showConfirmationDialog(
|
|
||||||
context: context,
|
|
||||||
onConfirm: () => onDelete(meal),
|
|
||||||
message: 'Are you sure you want to delete this Meal?',
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
onDelete(meal);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Column(
|
|
||||||
children: <Widget>[
|
|
||||||
Expanded(
|
|
||||||
child: widget.logEntry.meals.isNotEmpty ? ListView.builder(
|
|
||||||
shrinkWrap: true,
|
|
||||||
itemCount: widget.logEntry.meals.length,
|
|
||||||
itemBuilder: (context, index) {
|
|
||||||
final meal = widget.logEntry.meals[index];
|
|
||||||
return ListTile(
|
|
||||||
onTap: () => handleEditAction(meal),
|
|
||||||
title: Row(
|
|
||||||
children: [
|
|
||||||
Expanded(child: Text(meal.value)),
|
|
||||||
Expanded(
|
|
||||||
child: Text(meal.carbsPerPortion != null
|
|
||||||
? '${meal.carbsPerPortion} g carbs'
|
|
||||||
: '')),
|
|
||||||
Expanded(
|
|
||||||
child: Text(meal.bolus != null
|
|
||||||
? '${meal.bolus} U'
|
|
||||||
: ''))
|
|
||||||
],
|
|
||||||
),
|
|
||||||
trailing: IconButton(
|
|
||||||
icon: const Icon(
|
|
||||||
Icons.delete,
|
|
||||||
color: Colors.blue,
|
|
||||||
),
|
|
||||||
onPressed: () => handleDeleteAction(meal),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
) : const Center(
|
|
||||||
child: Text(
|
|
||||||
'You have not added any Meals to this Log Entry yet!'),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -8,10 +8,9 @@ import 'package:diameter/models/meal_category.dart';
|
|||||||
|
|
||||||
class MealCategoryDetailScreen extends StatefulWidget {
|
class MealCategoryDetailScreen extends StatefulWidget {
|
||||||
static const String routeName = '/meal-category';
|
static const String routeName = '/meal-category';
|
||||||
final MealCategory? mealCategory;
|
final int id;
|
||||||
|
|
||||||
const MealCategoryDetailScreen({Key? key, this.mealCategory})
|
const MealCategoryDetailScreen({Key? key, this.id = 0}) : super(key: key);
|
||||||
: super(key: key);
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_MealCategoryDetailScreenState createState() =>
|
_MealCategoryDetailScreenState createState() =>
|
||||||
@ -19,45 +18,54 @@ class MealCategoryDetailScreen extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _MealCategoryDetailScreenState extends State<MealCategoryDetailScreen> {
|
class _MealCategoryDetailScreenState extends State<MealCategoryDetailScreen> {
|
||||||
|
MealCategory? _mealCategory;
|
||||||
|
bool _isNew = true;
|
||||||
|
|
||||||
final GlobalKey<FormState> _mealCategoryForm = GlobalKey<FormState>();
|
final GlobalKey<FormState> _mealCategoryForm = GlobalKey<FormState>();
|
||||||
|
|
||||||
final _valueController = TextEditingController(text: '');
|
final _valueController = TextEditingController(text: '');
|
||||||
final _notesController = TextEditingController(text: '');
|
final _notesController = TextEditingController(text: '');
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
if (widget.mealCategory != null) {
|
reload();
|
||||||
_valueController.text = widget.mealCategory!.value;
|
|
||||||
_notesController.text = widget.mealCategory!.notes ?? '';
|
if (_mealCategory != null) {
|
||||||
|
_valueController.text = _mealCategory!.value;
|
||||||
|
_notesController.text = _mealCategory!.notes ?? '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void reload() {
|
||||||
|
if (widget.id != 0) {
|
||||||
|
setState(() {
|
||||||
|
_mealCategory = MealCategory.get(widget.id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
_isNew = _mealCategory == null;
|
||||||
|
}
|
||||||
|
|
||||||
void handleSaveAction() async {
|
void handleSaveAction() async {
|
||||||
if (_mealCategoryForm.currentState!.validate()) {
|
if (_mealCategoryForm.currentState!.validate()) {
|
||||||
bool isNew = widget.mealCategory == null;
|
MealCategory.put(MealCategory(
|
||||||
// isNew
|
id: widget.id,
|
||||||
// ? await MealCategory.save(
|
value: _valueController.text,
|
||||||
// value: _valueController.text, notes: _notesController.text)
|
notes: _notesController.text));
|
||||||
// : await MealCategory.update(widget.mealCategory!.objectId!,
|
Navigator.pop(context, '${_isNew ? 'New' : ''} Meal Category saved');
|
||||||
// value: _valueController.text, notes: _notesController.text);
|
|
||||||
MealCategory.put(MealCategory(id: widget.mealCategory?.id ?? 0,
|
|
||||||
value: _valueController.text, notes: _notesController.text));
|
|
||||||
Navigator.pop(context, '${isNew ? 'New' : ''} Meal Category saved');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void handleCancelAction() {
|
void handleCancelAction() {
|
||||||
bool isNew = widget.mealCategory == null;
|
|
||||||
|
|
||||||
if (showConfirmationDialogOnCancel &&
|
if (showConfirmationDialogOnCancel &&
|
||||||
(isNew &&
|
(_isNew &&
|
||||||
(_valueController.text != '' || _notesController.text != '')) ||
|
(_valueController.text != '' || _notesController.text != '')) ||
|
||||||
(!isNew &&
|
(!_isNew &&
|
||||||
(widget.mealCategory!.value != _valueController.text ||
|
(_mealCategory!.value != _valueController.text ||
|
||||||
(widget.mealCategory!.notes ?? '') != _notesController.text))) {
|
(_mealCategory!.notes ?? '') != _notesController.text))) {
|
||||||
Dialogs.showCancelConfirmationDialog(
|
Dialogs.showCancelConfirmationDialog(
|
||||||
context: context,
|
context: context,
|
||||||
isNew: isNew,
|
isNew: _isNew,
|
||||||
onSave: handleSaveAction,
|
onSave: handleSaveAction,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
@ -67,10 +75,9 @@ class _MealCategoryDetailScreenState extends State<MealCategoryDetailScreen> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
bool isNew = widget.mealCategory == null;
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text(isNew ? 'New Meal Category' : widget.mealCategory!.value),
|
title: Text(_isNew ? 'New Meal Category' : _mealCategory!.value),
|
||||||
),
|
),
|
||||||
drawer:
|
drawer:
|
||||||
const Navigation(currentLocation: MealCategoryDetailScreen.routeName),
|
const Navigation(currentLocation: MealCategoryDetailScreen.routeName),
|
||||||
|
@ -18,7 +18,13 @@ class MealCategoryListScreen extends StatefulWidget {
|
|||||||
class _MealCategoryListScreenState extends State<MealCategoryListScreen> {
|
class _MealCategoryListScreenState extends State<MealCategoryListScreen> {
|
||||||
List<MealCategory> _mealCategories = [];
|
List<MealCategory> _mealCategories = [];
|
||||||
|
|
||||||
void refresh({String? message}) {
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
void reload({String? message}) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_mealCategories = MealCategory.getAll();
|
_mealCategories = MealCategory.getAll();
|
||||||
});
|
});
|
||||||
@ -37,7 +43,7 @@ class _MealCategoryListScreenState extends State<MealCategoryListScreen> {
|
|||||||
|
|
||||||
void onDelete(MealCategory mealCategory) {
|
void onDelete(MealCategory mealCategory) {
|
||||||
MealCategory.remove(mealCategory.id);
|
MealCategory.remove(mealCategory.id);
|
||||||
refresh(message: 'Meal Category deleted');
|
reload(message: 'Meal Category deleted');
|
||||||
}
|
}
|
||||||
|
|
||||||
void handleDeleteAction(MealCategory mealCategory) async {
|
void handleDeleteAction(MealCategory mealCategory) async {
|
||||||
@ -52,12 +58,6 @@ class _MealCategoryListScreenState extends State<MealCategoryListScreen> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
refresh();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
@ -65,7 +65,7 @@ class _MealCategoryListScreenState extends State<MealCategoryListScreen> {
|
|||||||
title: const Text('Meal Categories'),
|
title: const Text('Meal Categories'),
|
||||||
actions: <Widget>[
|
actions: <Widget>[
|
||||||
IconButton(
|
IconButton(
|
||||||
onPressed: refresh,
|
onPressed: reload,
|
||||||
icon: const Icon(Icons.refresh),
|
icon: const Icon(Icons.refresh),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -88,11 +88,11 @@ class _MealCategoryListScreenState extends State<MealCategoryListScreen> {
|
|||||||
context,
|
context,
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (context) =>
|
builder: (context) =>
|
||||||
MealCategoryDetailScreen(
|
MealCategoryDetailScreen(
|
||||||
mealCategory: mealCategory,
|
id: mealCategory.id,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
).then((message) => refresh(message: message));
|
).then((message) => reload(message: message));
|
||||||
},
|
},
|
||||||
title: Text(mealCategory.value),
|
title: Text(mealCategory.value),
|
||||||
subtitle: Text(mealCategory.notes ?? ''),
|
subtitle: Text(mealCategory.notes ?? ''),
|
||||||
@ -124,7 +124,7 @@ class _MealCategoryListScreenState extends State<MealCategoryListScreen> {
|
|||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (context) => const MealCategoryDetailScreen(),
|
builder: (context) => const MealCategoryDetailScreen(),
|
||||||
),
|
),
|
||||||
).then((message) => refresh(message: message));
|
).then((message) => reload(message: message));
|
||||||
},
|
},
|
||||||
child: const Icon(Icons.add),
|
child: const Icon(Icons.add),
|
||||||
),
|
),
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import 'package:diameter/components/detail.dart';
|
import 'package:diameter/components/detail.dart';
|
||||||
import 'package:diameter/components/dialogs.dart';
|
import 'package:diameter/components/dialogs.dart';
|
||||||
|
import 'package:diameter/components/dropdown.dart';
|
||||||
import 'package:diameter/components/forms.dart';
|
import 'package:diameter/components/forms.dart';
|
||||||
import 'package:diameter/config.dart';
|
import 'package:diameter/config.dart';
|
||||||
import 'package:diameter/models/accuracy.dart';
|
import 'package:diameter/models/accuracy.dart';
|
||||||
@ -8,22 +9,25 @@ import 'package:diameter/models/meal_category.dart';
|
|||||||
import 'package:diameter/models/meal_portion_type.dart';
|
import 'package:diameter/models/meal_portion_type.dart';
|
||||||
import 'package:diameter/models/meal_source.dart';
|
import 'package:diameter/models/meal_source.dart';
|
||||||
import 'package:diameter/navigation.dart';
|
import 'package:diameter/navigation.dart';
|
||||||
// import 'package:diameter/objectbox.g.dart';
|
|
||||||
import 'package:diameter/settings.dart';
|
import 'package:diameter/settings.dart';
|
||||||
import 'package:diameter/utils/utils.dart';
|
import 'package:diameter/utils/utils.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class MealDetailScreen extends StatefulWidget {
|
class MealDetailScreen extends StatefulWidget {
|
||||||
static const String routeName = '/meal';
|
static const String routeName = '/meal';
|
||||||
|
final int id;
|
||||||
|
|
||||||
final Meal? meal;
|
const MealDetailScreen({Key? key, this.id = 0}) : super(key: key);
|
||||||
const MealDetailScreen({Key? key, this.meal}) : super(key: key);
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_MealDetailScreenState createState() => _MealDetailScreenState();
|
_MealDetailScreenState createState() => _MealDetailScreenState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _MealDetailScreenState extends State<MealDetailScreen> {
|
class _MealDetailScreenState extends State<MealDetailScreen> {
|
||||||
|
Meal? _meal;
|
||||||
|
bool _isNew = true;
|
||||||
|
bool _isSaving = false;
|
||||||
|
|
||||||
final GlobalKey<FormState> _mealForm = GlobalKey<FormState>();
|
final GlobalKey<FormState> _mealForm = GlobalKey<FormState>();
|
||||||
|
|
||||||
final _valueController = TextEditingController(text: '');
|
final _valueController = TextEditingController(text: '');
|
||||||
@ -46,84 +50,54 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
|
|||||||
List<Accuracy> _portionSizeAccuracies = [];
|
List<Accuracy> _portionSizeAccuracies = [];
|
||||||
List<Accuracy> _carbsRatioAccuracies = [];
|
List<Accuracy> _carbsRatioAccuracies = [];
|
||||||
|
|
||||||
bool isSaving = false;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
|
||||||
|
reload();
|
||||||
|
|
||||||
_portionSizeAccuracies = Accuracy.getAllForPortionSize();
|
_portionSizeAccuracies = Accuracy.getAllForPortionSize();
|
||||||
_carbsRatioAccuracies = Accuracy.getAllForCarbsRatio();
|
_carbsRatioAccuracies = Accuracy.getAllForCarbsRatio();
|
||||||
_mealCategories = MealCategory.getAll();
|
_mealCategories = MealCategory.getAll();
|
||||||
_mealPortionTypes = MealPortionType.getAll();
|
_mealPortionTypes = MealPortionType.getAll();
|
||||||
_mealSources = MealSource.getAll();
|
_mealSources = MealSource.getAll();
|
||||||
|
|
||||||
if (widget.meal != null) {
|
if (_meal != null) {
|
||||||
_valueController.text = widget.meal!.value;
|
_valueController.text = _meal!.value;
|
||||||
_carbsRatioController.text = (widget.meal!.carbsRatio ?? '').toString();
|
_carbsRatioController.text = (_meal!.carbsRatio ?? '').toString();
|
||||||
_portionSizeController.text = (widget.meal!.portionSize ?? '').toString();
|
_portionSizeController.text = (_meal!.portionSize ?? '').toString();
|
||||||
_carbsPerPortionController.text =
|
_carbsPerPortionController.text =
|
||||||
(widget.meal!.carbsPerPortion ?? '').toString();
|
(_meal!.carbsPerPortion ?? '').toString();
|
||||||
_delayedBolusRateController.text =
|
_delayedBolusRateController.text =
|
||||||
(widget.meal!.delayedBolusRate ?? '').toString();
|
(_meal!.delayedBolusRate ?? '').toString();
|
||||||
_delayedBolusDurationController.text =
|
_delayedBolusDurationController.text =
|
||||||
(widget.meal!.delayedBolusDuration ?? '').toString();
|
(_meal!.delayedBolusDuration ?? '').toString();
|
||||||
_notesController.text = widget.meal!.notes ?? '';
|
_notesController.text = _meal!.notes ?? '';
|
||||||
|
|
||||||
_mealSource = widget.meal!.mealSource.target;
|
_mealSource = _meal!.mealSource.target;
|
||||||
_mealCategory = widget.meal!.mealCategory.target;
|
_mealCategory = _meal!.mealCategory.target;
|
||||||
_mealPortionType = widget.meal!.mealPortionType.target;
|
_mealPortionType = _meal!.mealPortionType.target;
|
||||||
_portionSizeAccuracy = widget.meal!.portionSizeAccuracy.target;
|
_portionSizeAccuracy = _meal!.portionSizeAccuracy.target;
|
||||||
_carbsRatioAccuracy = widget.meal!.carbsRatioAccuracy.target;
|
_carbsRatioAccuracy = _meal!.carbsRatioAccuracy.target;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void reload() {
|
||||||
|
if (widget.id != 0) {
|
||||||
|
setState(() {
|
||||||
|
_meal = Meal.get(widget.id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
_isNew = _meal == null;
|
||||||
|
}
|
||||||
|
|
||||||
void handleSaveAction() async {
|
void handleSaveAction() async {
|
||||||
setState(() {
|
setState(() {
|
||||||
isSaving = true;
|
_isSaving = true;
|
||||||
});
|
});
|
||||||
if (_mealForm.currentState!.validate()) {
|
if (_mealForm.currentState!.validate()) {
|
||||||
bool isNew = widget.meal == null;
|
|
||||||
// isNew
|
|
||||||
// ? await Meal.save(
|
|
||||||
// value: _valueController.text,
|
|
||||||
// source: _mealSource,
|
|
||||||
// category: _mealCategory,
|
|
||||||
// portionType: _mealPortionType,
|
|
||||||
// carbsRatio: double.tryParse(_carbsRatioController.text),
|
|
||||||
// portionSize: double.tryParse(_portionSizeController.text),
|
|
||||||
// carbsPerPortion: double.tryParse(_carbsPerPortionController.text),
|
|
||||||
// // portionSizeAccuracy: _portionSizeAccuracy,
|
|
||||||
// // carbsRatioAccuracy: _carbsRatioAccuracy,
|
|
||||||
// portionSizeAccuracy: _portionSizeAccuracy?.id.toString(),
|
|
||||||
// carbsRatioAccuracy: _carbsRatioAccuracy?.id.toString(),
|
|
||||||
// delayedBolusDuration:
|
|
||||||
// int.tryParse(_delayedBolusDurationController.text),
|
|
||||||
// delayedBolusRate:
|
|
||||||
// double.tryParse(_delayedBolusRateController.text),
|
|
||||||
// notes: _notesController.text,
|
|
||||||
// )
|
|
||||||
// : await Meal.update(
|
|
||||||
// widget.meal!.objectId!,
|
|
||||||
// value: _valueController.text,
|
|
||||||
// source: _mealSource,
|
|
||||||
// category: _mealCategory,
|
|
||||||
// portionType: _mealPortionType,
|
|
||||||
// carbsRatio: double.tryParse(_carbsRatioController.text),
|
|
||||||
// portionSize: double.tryParse(_portionSizeController.text),
|
|
||||||
// carbsPerPortion: double.tryParse(_carbsPerPortionController.text),
|
|
||||||
// // portionSizeAccuracy: _portionSizeAccuracy,
|
|
||||||
// // carbsRatioAccuracy: _carbsRatioAccuracy,
|
|
||||||
// portionSizeAccuracy: _portionSizeAccuracy?.id.toString(),
|
|
||||||
// carbsRatioAccuracy: _carbsRatioAccuracy?.id.toString(),
|
|
||||||
// delayedBolusDuration:
|
|
||||||
// int.tryParse(_delayedBolusDurationController.text),
|
|
||||||
// delayedBolusRate:
|
|
||||||
// double.tryParse(_delayedBolusRateController.text),
|
|
||||||
// notes: _notesController.text,
|
|
||||||
// );
|
|
||||||
Meal meal = Meal(
|
Meal meal = Meal(
|
||||||
id: widget.meal?.id ?? 0,
|
id: widget.id,
|
||||||
value: _valueController.text,
|
value: _valueController.text,
|
||||||
carbsRatio: double.tryParse(_carbsRatioController.text),
|
carbsRatio: double.tryParse(_carbsRatioController.text),
|
||||||
portionSize: double.tryParse(_portionSizeController.text),
|
portionSize: double.tryParse(_portionSizeController.text),
|
||||||
@ -140,17 +114,16 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
|
|||||||
meal.carbsRatioAccuracy.target = _carbsRatioAccuracy;
|
meal.carbsRatioAccuracy.target = _carbsRatioAccuracy;
|
||||||
|
|
||||||
Meal.put(meal);
|
Meal.put(meal);
|
||||||
Navigator.pop(context, '${isNew ? 'New' : ''} Meal Saved');
|
Navigator.pop(context, '${_isNew ? 'New' : ''} Meal Saved');
|
||||||
}
|
}
|
||||||
setState(() {
|
setState(() {
|
||||||
isSaving = false;
|
_isSaving = false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void handleCancelAction() {
|
void handleCancelAction() {
|
||||||
bool isNew = widget.meal == null;
|
|
||||||
if (showConfirmationDialogOnCancel &&
|
if (showConfirmationDialogOnCancel &&
|
||||||
((isNew &&
|
((_isNew &&
|
||||||
(_valueController.text != '' ||
|
(_valueController.text != '' ||
|
||||||
_mealSource != null ||
|
_mealSource != null ||
|
||||||
_mealCategory != null ||
|
_mealCategory != null ||
|
||||||
@ -164,27 +137,27 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
|
|||||||
null ||
|
null ||
|
||||||
double.tryParse(_delayedBolusRateController.text) != null ||
|
double.tryParse(_delayedBolusRateController.text) != null ||
|
||||||
_notesController.text != '')) ||
|
_notesController.text != '')) ||
|
||||||
(!isNew &&
|
(!_isNew &&
|
||||||
(_valueController.text != widget.meal!.value ||
|
(_valueController.text != _meal!.value ||
|
||||||
_mealSource != widget.meal!.mealSource.target ||
|
_mealSource != _meal!.mealSource.target ||
|
||||||
_mealCategory != widget.meal!.mealCategory.target ||
|
_mealCategory != _meal!.mealCategory.target ||
|
||||||
_mealPortionType != widget.meal!.mealPortionType.target ||
|
_mealPortionType != _meal!.mealPortionType.target ||
|
||||||
double.tryParse(_carbsRatioController.text) !=
|
double.tryParse(_carbsRatioController.text) !=
|
||||||
widget.meal!.carbsRatio ||
|
_meal!.carbsRatio ||
|
||||||
double.tryParse(_portionSizeController.text) !=
|
double.tryParse(_portionSizeController.text) !=
|
||||||
widget.meal!.portionSize ||
|
_meal!.portionSize ||
|
||||||
double.tryParse(_carbsPerPortionController.text) !=
|
double.tryParse(_carbsPerPortionController.text) !=
|
||||||
widget.meal!.carbsPerPortion ||
|
_meal!.carbsPerPortion ||
|
||||||
_carbsRatioAccuracy != widget.meal!.carbsRatioAccuracy.target ||
|
_carbsRatioAccuracy != _meal!.carbsRatioAccuracy.target ||
|
||||||
_portionSizeAccuracy != widget.meal!.portionSizeAccuracy.target ||
|
_portionSizeAccuracy != _meal!.portionSizeAccuracy.target ||
|
||||||
int.tryParse(_delayedBolusDurationController.text) !=
|
int.tryParse(_delayedBolusDurationController.text) !=
|
||||||
widget.meal!.delayedBolusDuration ||
|
_meal!.delayedBolusDuration ||
|
||||||
double.tryParse(_delayedBolusRateController.text) !=
|
double.tryParse(_delayedBolusRateController.text) !=
|
||||||
widget.meal!.delayedBolusRate ||
|
_meal!.delayedBolusRate ||
|
||||||
_notesController.text != (widget.meal!.notes ?? ''))))) {
|
_notesController.text != (_meal!.notes ?? ''))))) {
|
||||||
Dialogs.showCancelConfirmationDialog(
|
Dialogs.showCancelConfirmationDialog(
|
||||||
context: context,
|
context: context,
|
||||||
isNew: isNew,
|
isNew: _isNew,
|
||||||
onSave: handleSaveAction,
|
onSave: handleSaveAction,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
@ -196,12 +169,10 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
|
|||||||
setState(() {
|
setState(() {
|
||||||
_mealSource = mealSource;
|
_mealSource = mealSource;
|
||||||
if (mealSource.defaultCarbsRatioAccuracy.hasValue) {
|
if (mealSource.defaultCarbsRatioAccuracy.hasValue) {
|
||||||
_carbsRatioAccuracy =
|
_carbsRatioAccuracy = mealSource.defaultCarbsRatioAccuracy.target;
|
||||||
mealSource.defaultCarbsRatioAccuracy.target;
|
|
||||||
}
|
}
|
||||||
if (mealSource.defaultPortionSizeAccuracy.hasValue) {
|
if (mealSource.defaultPortionSizeAccuracy.hasValue) {
|
||||||
_portionSizeAccuracy =
|
_portionSizeAccuracy = mealSource.defaultPortionSizeAccuracy.target;
|
||||||
mealSource.defaultPortionSizeAccuracy.target;
|
|
||||||
}
|
}
|
||||||
if (mealSource.defaultMealCategory.hasValue) {
|
if (mealSource.defaultMealCategory.hasValue) {
|
||||||
_mealCategory = mealSource.defaultMealCategory.target;
|
_mealCategory = mealSource.defaultMealCategory.target;
|
||||||
@ -256,10 +227,9 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
bool isNew = widget.meal == null;
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text(isNew ? 'New Meal' : widget.meal!.value),
|
title: Text(_isNew ? 'New Meal' : _meal!.value),
|
||||||
),
|
),
|
||||||
drawer: const Navigation(currentLocation: MealDetailScreen.routeName),
|
drawer: const Navigation(currentLocation: MealDetailScreen.routeName),
|
||||||
body: SingleChildScrollView(
|
body: SingleChildScrollView(
|
||||||
@ -281,36 +251,33 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
|
|||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
LabeledDropdownButton<MealSource>(
|
AutoCompleteDropdownButton<MealSource>(
|
||||||
selectedItem: _mealSource,
|
selectedItem: _mealSource,
|
||||||
label: 'Meal Source',
|
label: 'Meal Source',
|
||||||
items: _mealSources,
|
items: _mealSources,
|
||||||
// getItemValue: (item) => item.objectId,
|
renderItem: (item) => item.value,
|
||||||
renderItem: (item) => Text(item.value),
|
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
onSelectMealSource(value);
|
onSelectMealSource(value);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
LabeledDropdownButton<MealCategory>(
|
AutoCompleteDropdownButton<MealCategory>(
|
||||||
selectedItem: _mealCategory,
|
selectedItem: _mealCategory,
|
||||||
label: 'Meal Category',
|
label: 'Meal Category',
|
||||||
items: _mealCategories,
|
items: _mealCategories,
|
||||||
// getItemValue: (item) => item.objectId,
|
renderItem: (item) => item.value,
|
||||||
renderItem: (item) => Text(item.value),
|
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_mealCategory = value;
|
_mealCategory = value;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
LabeledDropdownButton<MealPortionType>(
|
AutoCompleteDropdownButton<MealPortionType>(
|
||||||
selectedItem: _mealPortionType,
|
selectedItem: _mealPortionType,
|
||||||
label: 'Meal Portion Type',
|
label: 'Meal Portion Type',
|
||||||
items: _mealPortionTypes,
|
items: _mealPortionTypes,
|
||||||
// getItemValue: (item) => item.objectId,
|
renderItem: (item) => item.value,
|
||||||
renderItem: (item) => Text(item.value),
|
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_mealPortionType = value;
|
_mealPortionType = value;
|
||||||
@ -373,12 +340,11 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
LabeledDropdownButton<Accuracy>(
|
AutoCompleteDropdownButton<Accuracy>(
|
||||||
selectedItem: _portionSizeAccuracy,
|
selectedItem: _portionSizeAccuracy,
|
||||||
label: 'Portion Size Accuracy',
|
label: 'Portion Size Accuracy',
|
||||||
items: _portionSizeAccuracies,
|
items: _portionSizeAccuracies,
|
||||||
// getItemValue: (item) => item.objectId,
|
renderItem: (item) => item.value,
|
||||||
renderItem: (item) => Text(item.value),
|
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_portionSizeAccuracy = value;
|
_portionSizeAccuracy = value;
|
||||||
@ -415,12 +381,11 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
LabeledDropdownButton<Accuracy>(
|
AutoCompleteDropdownButton<Accuracy>(
|
||||||
selectedItem: _carbsRatioAccuracy,
|
selectedItem: _carbsRatioAccuracy,
|
||||||
label: 'Carbs Ratio Accuracy',
|
label: 'Carbs Ratio Accuracy',
|
||||||
items: _carbsRatioAccuracies,
|
items: _carbsRatioAccuracies,
|
||||||
// getItemValue: (item) => item.objectId,
|
renderItem: (item) => item.value,
|
||||||
renderItem: (item) => Text(item.value),
|
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_carbsRatioAccuracy = value;
|
_carbsRatioAccuracy = value;
|
||||||
@ -461,7 +426,7 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
|
|||||||
),
|
),
|
||||||
bottomNavigationBar: DetailBottomRow(
|
bottomNavigationBar: DetailBottomRow(
|
||||||
onCancel: handleCancelAction,
|
onCancel: handleCancelAction,
|
||||||
onSave: isSaving ? null : handleSaveAction,
|
onSave: _isSaving ? null : handleSaveAction,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import 'package:diameter/components/dialogs.dart';
|
import 'package:diameter/components/dialogs.dart';
|
||||||
// import 'package:diameter/components/progress_indicator.dart';
|
|
||||||
import 'package:diameter/config.dart';
|
import 'package:diameter/config.dart';
|
||||||
import 'package:diameter/models/meal.dart';
|
import 'package:diameter/models/meal.dart';
|
||||||
import 'package:diameter/navigation.dart';
|
import 'package:diameter/navigation.dart';
|
||||||
@ -18,7 +17,13 @@ class MealListScreen extends StatefulWidget {
|
|||||||
class _MealListScreenState extends State<MealListScreen> {
|
class _MealListScreenState extends State<MealListScreen> {
|
||||||
List<Meal> _meals = [];
|
List<Meal> _meals = [];
|
||||||
|
|
||||||
void refresh({String? message}) {
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
void reload({String? message}) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_meals = Meal.getAll();
|
_meals = Meal.getAll();
|
||||||
});
|
});
|
||||||
@ -37,7 +42,7 @@ class _MealListScreenState extends State<MealListScreen> {
|
|||||||
|
|
||||||
void onDelete(Meal meal) {
|
void onDelete(Meal meal) {
|
||||||
Meal.remove(meal.id);
|
Meal.remove(meal.id);
|
||||||
refresh(message: 'Meal deleted');
|
reload(message: 'Meal deleted');
|
||||||
}
|
}
|
||||||
|
|
||||||
void handleDeleteAction(Meal meal) async {
|
void handleDeleteAction(Meal meal) async {
|
||||||
@ -52,17 +57,11 @@ class _MealListScreenState extends State<MealListScreen> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
refresh();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(title: const Text('Meals'), actions: <Widget>[
|
appBar: AppBar(title: const Text('Meals'), actions: <Widget>[
|
||||||
IconButton(onPressed: refresh, icon: const Icon(Icons.refresh))
|
IconButton(onPressed: reload, icon: const Icon(Icons.refresh))
|
||||||
]),
|
]),
|
||||||
drawer: const Navigation(currentLocation: MealListScreen.routeName),
|
drawer: const Navigation(currentLocation: MealListScreen.routeName),
|
||||||
body: Column(
|
body: Column(
|
||||||
@ -81,9 +80,9 @@ class _MealListScreenState extends State<MealListScreen> {
|
|||||||
context,
|
context,
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (context) =>
|
builder: (context) =>
|
||||||
MealDetailScreen(meal: meal),
|
MealDetailScreen(id: meal.id),
|
||||||
),
|
),
|
||||||
).then((message) => refresh(message: message));
|
).then((message) => reload(message: message));
|
||||||
},
|
},
|
||||||
title: Text(meal.value),
|
title: Text(meal.value),
|
||||||
subtitle: Text(meal.notes ?? ''),
|
subtitle: Text(meal.notes ?? ''),
|
||||||
@ -112,7 +111,7 @@ class _MealListScreenState extends State<MealListScreen> {
|
|||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (context) => const MealDetailScreen(),
|
builder: (context) => const MealDetailScreen(),
|
||||||
),
|
),
|
||||||
).then((message) => refresh(message: message));
|
).then((message) => reload(message: message));
|
||||||
},
|
},
|
||||||
child: const Icon(Icons.add),
|
child: const Icon(Icons.add),
|
||||||
),
|
),
|
||||||
|
@ -9,10 +9,9 @@ import 'package:diameter/models/meal_portion_type.dart';
|
|||||||
class MealPortionTypeDetailScreen extends StatefulWidget {
|
class MealPortionTypeDetailScreen extends StatefulWidget {
|
||||||
static const String routeName = '/meal-portion-type';
|
static const String routeName = '/meal-portion-type';
|
||||||
|
|
||||||
final MealPortionType? mealPortionType;
|
final int id;
|
||||||
|
|
||||||
const MealPortionTypeDetailScreen({Key? key, this.mealPortionType})
|
const MealPortionTypeDetailScreen({Key? key, this.id = 0}) : super(key: key);
|
||||||
: super(key: key);
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_MealPortionTypeDetailScreenState createState() =>
|
_MealPortionTypeDetailScreenState createState() =>
|
||||||
@ -21,53 +20,56 @@ class MealPortionTypeDetailScreen extends StatefulWidget {
|
|||||||
|
|
||||||
class _MealPortionTypeDetailScreenState
|
class _MealPortionTypeDetailScreenState
|
||||||
extends State<MealPortionTypeDetailScreen> {
|
extends State<MealPortionTypeDetailScreen> {
|
||||||
|
MealPortionType? _mealPortionType;
|
||||||
|
bool _isNew = true;
|
||||||
|
|
||||||
final GlobalKey<FormState> _mealPortionTypeForm = GlobalKey<FormState>();
|
final GlobalKey<FormState> _mealPortionTypeForm = GlobalKey<FormState>();
|
||||||
|
|
||||||
final _valueController = TextEditingController(text: '');
|
final _valueController = TextEditingController(text: '');
|
||||||
final _notesController = TextEditingController(text: '');
|
final _notesController = TextEditingController(text: '');
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
if (widget.mealPortionType != null) {
|
reload();
|
||||||
_valueController.text = widget.mealPortionType!.value;
|
|
||||||
_notesController.text = widget.mealPortionType!.notes ?? '';
|
if (_mealPortionType != null) {
|
||||||
|
_valueController.text = _mealPortionType!.value;
|
||||||
|
_notesController.text = _mealPortionType!.notes ?? '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void reload() {
|
||||||
|
if (widget.id != 0) {
|
||||||
|
setState(() {
|
||||||
|
_mealPortionType = MealPortionType.get(widget.id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
_isNew = _mealPortionType == null;
|
||||||
|
}
|
||||||
|
|
||||||
void handleSaveAction() async {
|
void handleSaveAction() async {
|
||||||
if (_mealPortionTypeForm.currentState!.validate()) {
|
if (_mealPortionTypeForm.currentState!.validate()) {
|
||||||
bool isNew = widget.mealPortionType == null;
|
|
||||||
// isNew
|
|
||||||
// ? MealPortionType.save(
|
|
||||||
// value: _valueController.text,
|
|
||||||
// notes: _notesController.text,
|
|
||||||
// )
|
|
||||||
// : MealPortionType.update(
|
|
||||||
// widget.mealPortionType!.objectId!,
|
|
||||||
// value: _valueController.text,
|
|
||||||
// notes: _notesController.text,
|
|
||||||
// );
|
|
||||||
MealPortionType.put(MealPortionType(
|
MealPortionType.put(MealPortionType(
|
||||||
id: widget.mealPortionType?.id ?? 0,
|
id: _mealPortionType?.id ?? 0,
|
||||||
value: _valueController.text,
|
value: _valueController.text,
|
||||||
notes: _notesController.text,
|
notes: _notesController.text,
|
||||||
));
|
));
|
||||||
Navigator.pop(context, '${isNew ? 'New' : ''} Meal Portion Type saved');
|
Navigator.pop(context, '${_isNew ? 'New' : ''} Meal Portion Type saved');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void handleCancelAction() {
|
void handleCancelAction() {
|
||||||
bool isNew = widget.mealPortionType == null;
|
|
||||||
if (showConfirmationDialogOnCancel &&
|
if (showConfirmationDialogOnCancel &&
|
||||||
((isNew &&
|
((_isNew &&
|
||||||
(_valueController.text != '' || _notesController.text != '')) ||
|
(_valueController.text != '' || _notesController.text != '')) ||
|
||||||
(!isNew &&
|
(!_isNew &&
|
||||||
(_valueController.text != widget.mealPortionType!.value ||
|
(_valueController.text != _mealPortionType!.value ||
|
||||||
_notesController.text !=
|
_notesController.text !=
|
||||||
(widget.mealPortionType!.notes ?? ''))))) {
|
(_mealPortionType!.notes ?? ''))))) {
|
||||||
Dialogs.showCancelConfirmationDialog(
|
Dialogs.showCancelConfirmationDialog(
|
||||||
context: context,
|
context: context,
|
||||||
isNew: isNew,
|
isNew: _isNew,
|
||||||
onSave: handleSaveAction,
|
onSave: handleSaveAction,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
@ -77,11 +79,11 @@ class _MealPortionTypeDetailScreenState
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
bool isNew = widget.mealPortionType == null;
|
bool isNew = _mealPortionType == null;
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text(
|
title: Text(
|
||||||
isNew ? 'New Meal Portion Type' : widget.mealPortionType!.value),
|
isNew ? 'New Meal Portion Type' : _mealPortionType!.value),
|
||||||
),
|
),
|
||||||
drawer: const Navigation(
|
drawer: const Navigation(
|
||||||
currentLocation: MealPortionTypeDetailScreen.routeName),
|
currentLocation: MealPortionTypeDetailScreen.routeName),
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
import 'package:diameter/components/dialogs.dart';
|
import 'package:diameter/components/dialogs.dart';
|
||||||
// import 'package:diameter/components/progress_indicator.dart';
|
|
||||||
import 'package:diameter/config.dart';
|
import 'package:diameter/config.dart';
|
||||||
import 'package:diameter/navigation.dart';
|
import 'package:diameter/navigation.dart';
|
||||||
import 'package:diameter/screens/meal/meal_portion_type_detail.dart';
|
import 'package:diameter/screens/meal/meal_portion_type_detail.dart';
|
||||||
@ -19,7 +18,13 @@ class MealPortionTypeListScreen extends StatefulWidget {
|
|||||||
class _MealPortionTypeListScreenState extends State<MealPortionTypeListScreen> {
|
class _MealPortionTypeListScreenState extends State<MealPortionTypeListScreen> {
|
||||||
List<MealPortionType> _mealPortionTypes = [];
|
List<MealPortionType> _mealPortionTypes = [];
|
||||||
|
|
||||||
void refresh({String? message}) {
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
void reload({String? message}) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_mealPortionTypes = MealPortionType.getAll();
|
_mealPortionTypes = MealPortionType.getAll();
|
||||||
});
|
});
|
||||||
@ -38,7 +43,7 @@ class _MealPortionTypeListScreenState extends State<MealPortionTypeListScreen> {
|
|||||||
|
|
||||||
void onDelete(MealPortionType mealPortionType) {
|
void onDelete(MealPortionType mealPortionType) {
|
||||||
MealPortionType.remove(mealPortionType.id);
|
MealPortionType.remove(mealPortionType.id);
|
||||||
refresh(message: 'Meal Portion Type deleted');
|
reload(message: 'Meal Portion Type deleted');
|
||||||
}
|
}
|
||||||
|
|
||||||
void handleDeleteAction(MealPortionType mealPortionType) async {
|
void handleDeleteAction(MealPortionType mealPortionType) async {
|
||||||
@ -52,20 +57,14 @@ class _MealPortionTypeListScreenState extends State<MealPortionTypeListScreen> {
|
|||||||
onDelete(mealPortionType);
|
onDelete(mealPortionType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
refresh();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: const Text('Meal Portion Types'),
|
title: const Text('Meal Portion Types'),
|
||||||
actions: <Widget>[
|
actions: <Widget>[
|
||||||
IconButton(onPressed: refresh, icon: const Icon(Icons.refresh))
|
IconButton(onPressed: reload, icon: const Icon(Icons.refresh))
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
drawer: const Navigation(
|
drawer: const Navigation(
|
||||||
@ -86,12 +85,12 @@ class _MealPortionTypeListScreenState extends State<MealPortionTypeListScreen> {
|
|||||||
context,
|
context,
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (context) =>
|
builder: (context) =>
|
||||||
MealPortionTypeDetailScreen(
|
MealPortionTypeDetailScreen(
|
||||||
mealPortionType: mealPortionType,
|
id: mealPortionType.id,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
).then(
|
).then(
|
||||||
(message) => refresh(message: message));
|
(message) => reload(message: message));
|
||||||
},
|
},
|
||||||
title: Text(mealPortionType.value),
|
title: Text(mealPortionType.value),
|
||||||
subtitle: Text(mealPortionType.notes ?? ''),
|
subtitle: Text(mealPortionType.notes ?? ''),
|
||||||
@ -123,7 +122,7 @@ class _MealPortionTypeListScreenState extends State<MealPortionTypeListScreen> {
|
|||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (context) => const MealPortionTypeDetailScreen(),
|
builder: (context) => const MealPortionTypeDetailScreen(),
|
||||||
),
|
),
|
||||||
).then((message) => refresh(message: message));
|
).then((message) => reload(message: message));
|
||||||
},
|
},
|
||||||
child: const Icon(Icons.add),
|
child: const Icon(Icons.add),
|
||||||
),
|
),
|
||||||
|
@ -1,28 +1,30 @@
|
|||||||
import 'package:diameter/components/detail.dart';
|
import 'package:diameter/components/detail.dart';
|
||||||
import 'package:diameter/components/dialogs.dart';
|
import 'package:diameter/components/dialogs.dart';
|
||||||
|
import 'package:diameter/components/dropdown.dart';
|
||||||
import 'package:diameter/components/forms.dart';
|
import 'package:diameter/components/forms.dart';
|
||||||
import 'package:diameter/config.dart';
|
import 'package:diameter/config.dart';
|
||||||
// import 'package:diameter/main.dart';
|
|
||||||
import 'package:diameter/models/accuracy.dart';
|
import 'package:diameter/models/accuracy.dart';
|
||||||
import 'package:diameter/models/meal_category.dart';
|
import 'package:diameter/models/meal_category.dart';
|
||||||
import 'package:diameter/models/meal_portion_type.dart';
|
import 'package:diameter/models/meal_portion_type.dart';
|
||||||
import 'package:diameter/models/meal_source.dart';
|
import 'package:diameter/models/meal_source.dart';
|
||||||
import 'package:diameter/navigation.dart';
|
import 'package:diameter/navigation.dart';
|
||||||
// import 'package:diameter/objectbox.g.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class MealSourceDetailScreen extends StatefulWidget {
|
class MealSourceDetailScreen extends StatefulWidget {
|
||||||
static const String routeName = '/meal-source';
|
static const String routeName = '/meal-source';
|
||||||
|
|
||||||
final MealSource? mealSource;
|
final int id;
|
||||||
|
|
||||||
const MealSourceDetailScreen({Key? key, this.mealSource}) : super(key: key);
|
const MealSourceDetailScreen({Key? key, this.id = 0}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_MealSourceDetailScreenState createState() => _MealSourceDetailScreenState();
|
_MealSourceDetailScreenState createState() => _MealSourceDetailScreenState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _MealSourceDetailScreenState extends State<MealSourceDetailScreen> {
|
class _MealSourceDetailScreenState extends State<MealSourceDetailScreen> {
|
||||||
|
MealSource? _mealSource;
|
||||||
|
bool _isNew = true;
|
||||||
|
|
||||||
List<Accuracy> _portionSizeAccuracies = [];
|
List<Accuracy> _portionSizeAccuracies = [];
|
||||||
List<Accuracy> _carbsRatioAccuracies = [];
|
List<Accuracy> _carbsRatioAccuracies = [];
|
||||||
List<MealCategory> _mealCategories = [];
|
List<MealCategory> _mealCategories = [];
|
||||||
@ -41,52 +43,40 @@ class _MealSourceDetailScreenState extends State<MealSourceDetailScreen> {
|
|||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
|
||||||
|
reload();
|
||||||
|
|
||||||
_portionSizeAccuracies = Accuracy.getAllForPortionSize();
|
_portionSizeAccuracies = Accuracy.getAllForPortionSize();
|
||||||
_carbsRatioAccuracies = Accuracy.getAllForCarbsRatio();
|
_carbsRatioAccuracies = Accuracy.getAllForCarbsRatio();
|
||||||
_mealCategories = MealCategory.getAll();
|
_mealCategories = MealCategory.getAll();
|
||||||
_mealPortionTypes = MealPortionType.getAll();
|
_mealPortionTypes = MealPortionType.getAll();
|
||||||
|
|
||||||
if (widget.mealSource != null) {
|
if (_mealSource != null) {
|
||||||
_valueController.text = widget.mealSource!.value;
|
_valueController.text = _mealSource!.value;
|
||||||
_notesController.text = widget.mealSource!.notes ?? '';
|
_notesController.text = _mealSource!.notes ?? '';
|
||||||
|
|
||||||
_defaultPortionSizeAccuracy =
|
_defaultPortionSizeAccuracy =
|
||||||
widget.mealSource!.defaultPortionSizeAccuracy.target;
|
_mealSource!.defaultPortionSizeAccuracy.target;
|
||||||
_defaultCarbsRatioAccuracy = widget.mealSource!.defaultCarbsRatioAccuracy.target;
|
_defaultCarbsRatioAccuracy =
|
||||||
|
_mealSource!.defaultCarbsRatioAccuracy.target;
|
||||||
|
|
||||||
_defaultMealCategory = widget.mealSource!.defaultMealCategory.target;
|
_defaultMealCategory = _mealSource!.defaultMealCategory.target;
|
||||||
_defaultMealPortionType =
|
_defaultMealPortionType =
|
||||||
widget.mealSource!.defaultMealPortionType.target;
|
_mealSource!.defaultMealPortionType.target;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void reload() {
|
||||||
|
if (widget.id != 0) {
|
||||||
|
setState(() {
|
||||||
|
_mealSource = MealSource.get(widget.id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
_isNew = _mealSource == null;
|
||||||
|
}
|
||||||
|
|
||||||
void handleSaveAction() async {
|
void handleSaveAction() async {
|
||||||
bool isNew = widget.mealSource == null;
|
MealSource mealSource = MealSource(
|
||||||
if (_mealSourceForm.currentState!.validate()) {
|
id: widget.id,
|
||||||
// isNew
|
|
||||||
// ? await MealSource.save(
|
|
||||||
// value: _valueController.text,
|
|
||||||
// defaultCarbsRatioAccuracy: _defaultCarbsRatioAccuracy?.id.toString(),
|
|
||||||
// defaultPortionSizeAccuracy: _defaultPortionSizeAccuracy?.id.toString(),
|
|
||||||
// // defaultCarbsRatioAccuracy: _defaultCarbsRatioAccuracy,
|
|
||||||
// // defaultPortionSizeAccuracy: _defaultPortionSizeAccuracy,
|
|
||||||
// defaultMealCategory: _defaultMealCategory,
|
|
||||||
// defaultMealPortionType: _defaultMealPortionType,
|
|
||||||
// notes: _notesController.text,
|
|
||||||
// )
|
|
||||||
// : await MealSource.update(
|
|
||||||
// widget.mealSource!.objectId!,
|
|
||||||
// value: _valueController.text,
|
|
||||||
// defaultCarbsRatioAccuracy: _defaultCarbsRatioAccuracy?.id.toString(),
|
|
||||||
// defaultPortionSizeAccuracy: _defaultPortionSizeAccuracy?.id.toString(),
|
|
||||||
// // defaultCarbsRatioAccuracy: _defaultCarbsRatioAccuracy,
|
|
||||||
// // defaultPortionSizeAccuracy: _defaultPortionSizeAccuracy,
|
|
||||||
// defaultMealCategory: _defaultMealCategory,
|
|
||||||
// defaultMealPortionType: _defaultMealPortionType,
|
|
||||||
// notes: _notesController.text,
|
|
||||||
// );
|
|
||||||
MealSource mealSource = MealSource(
|
|
||||||
id: widget.mealSource?.id ?? 0,
|
|
||||||
value: _valueController.text,
|
value: _valueController.text,
|
||||||
notes: _notesController.text,
|
notes: _notesController.text,
|
||||||
);
|
);
|
||||||
@ -96,35 +86,33 @@ class _MealSourceDetailScreenState extends State<MealSourceDetailScreen> {
|
|||||||
mealSource.defaultMealCategory.target = _defaultMealCategory;
|
mealSource.defaultMealCategory.target = _defaultMealCategory;
|
||||||
mealSource.defaultMealPortionType.target = _defaultMealPortionType;
|
mealSource.defaultMealPortionType.target = _defaultMealPortionType;
|
||||||
MealSource.put(mealSource);
|
MealSource.put(mealSource);
|
||||||
Navigator.pop(context, '${isNew ? 'New' : ''} Meal Source saved');
|
Navigator.pop(context, '${_isNew ? 'New' : ''} Meal Source saved');
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void handleCancelAction() {
|
void handleCancelAction() {
|
||||||
bool isNew = widget.mealSource == null;
|
|
||||||
if (showConfirmationDialogOnCancel &&
|
if (showConfirmationDialogOnCancel &&
|
||||||
((isNew &&
|
((_isNew &&
|
||||||
(_valueController.text != '' ||
|
(_valueController.text != '' ||
|
||||||
_defaultCarbsRatioAccuracy != null ||
|
_defaultCarbsRatioAccuracy != null ||
|
||||||
_defaultPortionSizeAccuracy != null ||
|
_defaultPortionSizeAccuracy != null ||
|
||||||
_defaultMealCategory != null ||
|
_defaultMealCategory != null ||
|
||||||
_defaultMealPortionType != null ||
|
_defaultMealPortionType != null ||
|
||||||
_notesController.text != '')) ||
|
_notesController.text != '')) ||
|
||||||
(!isNew &&
|
(!_isNew &&
|
||||||
(_valueController.text != widget.mealSource!.value ||
|
(_valueController.text != _mealSource!.value ||
|
||||||
_defaultCarbsRatioAccuracy !=
|
_defaultCarbsRatioAccuracy !=
|
||||||
widget.mealSource!.defaultCarbsRatioAccuracy.target ||
|
_mealSource!.defaultCarbsRatioAccuracy.target ||
|
||||||
_defaultPortionSizeAccuracy !=
|
_defaultPortionSizeAccuracy !=
|
||||||
widget.mealSource!.defaultPortionSizeAccuracy.target ||
|
_mealSource!.defaultPortionSizeAccuracy.target ||
|
||||||
_defaultMealCategory !=
|
_defaultMealCategory !=
|
||||||
widget.mealSource!.defaultMealCategory.target ||
|
_mealSource!.defaultMealCategory.target ||
|
||||||
_defaultMealPortionType !=
|
_defaultMealPortionType !=
|
||||||
widget.mealSource!.defaultMealPortionType.target ||
|
_mealSource!.defaultMealPortionType.target ||
|
||||||
_notesController.text !=
|
_notesController.text !=
|
||||||
(widget.mealSource!.notes ?? ''))))) {
|
(_mealSource!.notes ?? ''))))) {
|
||||||
Dialogs.showCancelConfirmationDialog(
|
Dialogs.showCancelConfirmationDialog(
|
||||||
context: context,
|
context: context,
|
||||||
isNew: isNew,
|
isNew: _isNew,
|
||||||
onSave: handleSaveAction,
|
onSave: handleSaveAction,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
@ -134,10 +122,9 @@ class _MealSourceDetailScreenState extends State<MealSourceDetailScreen> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
bool isNew = widget.mealSource == null;
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text(isNew ? 'New Meal Source' : widget.mealSource!.value),
|
title: Text(_isNew ? 'New Meal Source' : _mealSource!.value),
|
||||||
),
|
),
|
||||||
drawer:
|
drawer:
|
||||||
const Navigation(currentLocation: MealSourceDetailScreen.routeName),
|
const Navigation(currentLocation: MealSourceDetailScreen.routeName),
|
||||||
@ -160,70 +147,44 @@ class _MealSourceDetailScreenState extends State<MealSourceDetailScreen> {
|
|||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
LabeledDropdownButton<Accuracy>(
|
AutoCompleteDropdownButton<Accuracy>(
|
||||||
selectedItem: _defaultCarbsRatioAccuracy,
|
selectedItem: _defaultCarbsRatioAccuracy,
|
||||||
label: 'Default Carbs Ratio Accuracy',
|
label: 'Default Carbs Ratio Accuracy',
|
||||||
items: _carbsRatioAccuracies,
|
items: _carbsRatioAccuracies,
|
||||||
renderItem: (item) => Text(item.value),
|
renderItem: (item) => item.value,
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_defaultCarbsRatioAccuracy = value;
|
_defaultCarbsRatioAccuracy = value;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
LabeledDropdownButton<Accuracy>(
|
AutoCompleteDropdownButton<Accuracy>(
|
||||||
selectedItem: _defaultPortionSizeAccuracy,
|
selectedItem: _defaultPortionSizeAccuracy,
|
||||||
label: 'Default Portion Size Accuracy',
|
label: 'Default Portion Size Accuracy',
|
||||||
items: _portionSizeAccuracies,
|
items: _portionSizeAccuracies,
|
||||||
renderItem: (item) => Text(item.value),
|
renderItem: (item) => item.value,
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_defaultPortionSizeAccuracy = value;
|
_defaultPortionSizeAccuracy = value;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
// StyledFutureDropdownButton<Accuracy>(
|
AutoCompleteDropdownButton<MealCategory>(
|
||||||
// selectedItem: _defaultCarbsRatioAccuracy,
|
|
||||||
// label: 'Default Carbs Ratio Accuracy',
|
|
||||||
// items: _carbsRatioAccuracies,
|
|
||||||
// getItemValue: (item) => item.objectId,
|
|
||||||
// renderItem: (item) => Text(item.value),
|
|
||||||
// onChanged: (value) {
|
|
||||||
// setState(() {
|
|
||||||
// _defaultCarbsRatioAccuracy = value;
|
|
||||||
// });
|
|
||||||
// },
|
|
||||||
// ),
|
|
||||||
// StyledFutureDropdownButton<Accuracy>(
|
|
||||||
// selectedItem: _defaultPortionSizeAccuracy,
|
|
||||||
// label: 'Default Portion Size Accuracy',
|
|
||||||
// items: _portionSizeAccuracies,
|
|
||||||
// getItemValue: (item) => item.objectId,
|
|
||||||
// renderItem: (item) => Text(item.value),
|
|
||||||
// onChanged: (value) {
|
|
||||||
// setState(() {
|
|
||||||
// _defaultPortionSizeAccuracy = value;
|
|
||||||
// });
|
|
||||||
// },
|
|
||||||
// ),
|
|
||||||
LabeledDropdownButton<MealCategory>(
|
|
||||||
selectedItem: _defaultMealCategory,
|
selectedItem: _defaultMealCategory,
|
||||||
label: 'Default Meal Category',
|
label: 'Default Meal Category',
|
||||||
items: _mealCategories,
|
items: _mealCategories,
|
||||||
// getItemValue: (item) => item.objectId,
|
renderItem: (item) => item.value,
|
||||||
renderItem: (item) => Text(item.value),
|
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_defaultMealCategory = value;
|
_defaultMealCategory = value;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
LabeledDropdownButton<MealPortionType>(
|
AutoCompleteDropdownButton<MealPortionType>(
|
||||||
selectedItem: _defaultMealPortionType,
|
selectedItem: _defaultMealPortionType,
|
||||||
label: 'Default Meal Portion Type',
|
label: 'Default Meal Portion Type',
|
||||||
items: _mealPortionTypes,
|
items: _mealPortionTypes,
|
||||||
// getItemValue: (item) => item.objectId,
|
renderItem: (item) => item.value,
|
||||||
renderItem: (item) => Text(item.value),
|
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_defaultMealPortionType = value;
|
_defaultMealPortionType = value;
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
// import 'package:diameter/components/progress_indicator.dart';
|
|
||||||
import 'package:diameter/components/dialogs.dart';
|
import 'package:diameter/components/dialogs.dart';
|
||||||
import 'package:diameter/config.dart';
|
import 'package:diameter/config.dart';
|
||||||
import 'package:diameter/models/meal_source.dart';
|
import 'package:diameter/models/meal_source.dart';
|
||||||
@ -18,7 +17,13 @@ class MealSourceListScreen extends StatefulWidget {
|
|||||||
class _MealSourceListScreenState extends State<MealSourceListScreen> {
|
class _MealSourceListScreenState extends State<MealSourceListScreen> {
|
||||||
List<MealSource> _mealSources = [];
|
List<MealSource> _mealSources = [];
|
||||||
|
|
||||||
void refresh({String? message}) {
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
void reload({String? message}) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_mealSources = MealSource.getAll();
|
_mealSources = MealSource.getAll();
|
||||||
});
|
});
|
||||||
@ -37,7 +42,7 @@ class _MealSourceListScreenState extends State<MealSourceListScreen> {
|
|||||||
|
|
||||||
void onDelete(MealSource mealSource) {
|
void onDelete(MealSource mealSource) {
|
||||||
MealSource.remove(mealSource.id);
|
MealSource.remove(mealSource.id);
|
||||||
refresh(message: 'Meal Source deleted');
|
reload(message: 'Meal Source deleted');
|
||||||
}
|
}
|
||||||
|
|
||||||
void handleDeleteAction(MealSource mealSource) async {
|
void handleDeleteAction(MealSource mealSource) async {
|
||||||
@ -52,19 +57,13 @@ class _MealSourceListScreenState extends State<MealSourceListScreen> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
refresh();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: const Text('Meal Sources'),
|
title: const Text('Meal Sources'),
|
||||||
actions: <Widget>[
|
actions: <Widget>[
|
||||||
IconButton(onPressed: refresh, icon: const Icon(Icons.refresh))
|
IconButton(onPressed: reload, icon: const Icon(Icons.refresh))
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
drawer: const Navigation(currentLocation: MealSourceListScreen.routeName),
|
drawer: const Navigation(currentLocation: MealSourceListScreen.routeName),
|
||||||
@ -84,10 +83,10 @@ class _MealSourceListScreenState extends State<MealSourceListScreen> {
|
|||||||
context,
|
context,
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (context) => MealSourceDetailScreen(
|
builder: (context) => MealSourceDetailScreen(
|
||||||
mealSource: mealSource,
|
id: mealSource.id,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
).then((message) => refresh(message: message));
|
).then((message) => reload(message: message));
|
||||||
},
|
},
|
||||||
title: Text(mealSource.value),
|
title: Text(mealSource.value),
|
||||||
subtitle: Text(mealSource.notes ?? ''),
|
subtitle: Text(mealSource.notes ?? ''),
|
||||||
@ -120,7 +119,7 @@ class _MealSourceListScreenState extends State<MealSourceListScreen> {
|
|||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (context) => const MealSourceDetailScreen(),
|
builder: (context) => const MealSourceDetailScreen(),
|
||||||
),
|
),
|
||||||
).then((message) => refresh(message: message));
|
).then((message) => reload(message: message));
|
||||||
},
|
},
|
||||||
child: const Icon(Icons.add),
|
child: const Icon(Icons.add),
|
||||||
),
|
),
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import 'package:diameter/components/dialogs.dart';
|
import 'package:diameter/components/dialogs.dart';
|
||||||
|
import 'package:diameter/components/dropdown.dart';
|
||||||
import 'package:diameter/components/forms.dart';
|
import 'package:diameter/components/forms.dart';
|
||||||
import 'package:diameter/config.dart';
|
import 'package:diameter/config.dart';
|
||||||
import 'package:diameter/navigation.dart';
|
import 'package:diameter/navigation.dart';
|
||||||
@ -130,11 +131,11 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||||||
FormWrapper(
|
FormWrapper(
|
||||||
formState: _settingsForm,
|
formState: _settingsForm,
|
||||||
fields: [
|
fields: [
|
||||||
LabeledDropdownButton<NutritionMeasurement>(
|
AutoCompleteDropdownButton<NutritionMeasurement>(
|
||||||
selectedItem: nutritionMeasurement,
|
selectedItem: nutritionMeasurement,
|
||||||
label: 'Preferred Nutrition Measurement',
|
label: 'Preferred Nutrition Measurement',
|
||||||
items: NutritionMeasurement.values,
|
items: NutritionMeasurement.values,
|
||||||
renderItem: (item) => Text(item.toString().split('.')[1]),
|
renderItem: (item) => item.toString().split('.')[1],
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
Settings.setNutritionMeasurement(value);
|
Settings.setNutritionMeasurement(value);
|
||||||
@ -144,11 +145,11 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
LabeledDropdownButton<GlucoseMeasurement>(
|
AutoCompleteDropdownButton<GlucoseMeasurement>(
|
||||||
selectedItem: glucoseMeasurement,
|
selectedItem: glucoseMeasurement,
|
||||||
label: 'Preferred Glucose Measurement',
|
label: 'Preferred Glucose Measurement',
|
||||||
items: GlucoseMeasurement.values,
|
items: GlucoseMeasurement.values,
|
||||||
renderItem: (item) => Text(item.toString().split('.')[1]),
|
renderItem: (item) => item.toString().split('.')[1],
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
Settings.setGlucoseMeasurement(value);
|
Settings.setGlucoseMeasurement(value);
|
||||||
|
Loading…
Reference in New Issue
Block a user