implement dropdown with autocomplete; add bolus tab to log entry
This commit is contained in:
parent
ab0839bff3
commit
13507d14c9
35
TODO
35
TODO
@ -1,11 +1,7 @@
|
||||
MAIN TASKS:
|
||||
General/Framework:
|
||||
☐ add active/deleted flag to all data models
|
||||
☐ account for deleted/disabled elements in dropdowns
|
||||
☐ place dropdown items right below their input
|
||||
✔ use local database instead of back4app @done(21-11-07 18:53)
|
||||
☐ find a general way to deal with duration fields
|
||||
☐ add explanations to each section
|
||||
☐ use ids instead of passing entities around where possible
|
||||
|
||||
Accuracies:
|
||||
☐ implement reordering
|
||||
@ -13,20 +9,16 @@ Accuracies:
|
||||
Basal/Bolus:
|
||||
☐ create distinct visual mode for picking the active profile
|
||||
|
||||
Meal:
|
||||
none
|
||||
|
||||
Log Overview:
|
||||
☐ add active events view
|
||||
☐ 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
|
||||
|
||||
Log Entry:
|
||||
☐ add tab for bolus overview
|
||||
☐ calculate bolus suggestions according to active profile
|
||||
☐ add time picker for entry date/time
|
||||
☐ replace meal and glucose boli with logbolus entities
|
||||
☐ fix logmeals/logboli/logevents (probably use queries for associated items instead of onetomany)
|
||||
|
||||
Event Types:
|
||||
☐ add option to change bolus/basal profile for event duration
|
||||
@ -35,3 +27,22 @@ Settings:
|
||||
☐ add objectbox class and use instead of shared preferences
|
||||
☐ add fields for date and time formats
|
||||
☐ add fields for glucose target
|
||||
|
||||
FUTURE TASKS:
|
||||
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
|
||||
|
||||
|
||||
ARCHIVE:
|
||||
✔ add tab for bolus overview @done(21-11-24 22:05) @project(Log Entry)
|
||||
✔ calculate bolus suggestions according to active profile @done(21-11-24 22:05) @project(Log Entry)
|
||||
✔ 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._();
|
||||
|
||||
static ThemeData lightTheme = FlexColorScheme.light(
|
||||
surfaceStyle: FlexSurface.medium,
|
||||
scheme: FlexScheme.mandyRed,
|
||||
fontFamily: 'RobotoCondensed',
|
||||
).toTheme;
|
||||
@ -16,7 +17,6 @@ class AppTheme {
|
||||
|
||||
static ThemeData makeTheme(ThemeData baseThemeData) {
|
||||
return baseThemeData.copyWith(
|
||||
visualDensity: VisualDensity.compact,
|
||||
bottomNavigationBarTheme: BottomNavigationBarThemeData(
|
||||
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> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return FormField<bool>(builder: (context) {
|
||||
return FormField<bool>(builder: (state) {
|
||||
return ListTile(
|
||||
onTap: () => widget.onChanged(!widget.value),
|
||||
trailing: Switch(
|
||||
@ -98,8 +98,7 @@ class DateTimeFormField extends StatefulWidget {
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
_DateTimeFormFieldState createState() =>
|
||||
_DateTimeFormFieldState();
|
||||
_DateTimeFormFieldState createState() => _DateTimeFormFieldState();
|
||||
}
|
||||
|
||||
class _DateTimeFormFieldState extends State<DateTimeFormField> {
|
||||
@ -139,8 +138,7 @@ class TimeOfDayFormField extends StatefulWidget {
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
_TimeOfDayFormFieldState createState() =>
|
||||
_TimeOfDayFormFieldState();
|
||||
_TimeOfDayFormFieldState createState() => _TimeOfDayFormFieldState();
|
||||
}
|
||||
|
||||
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/bolus/bolus_profile_detail.dart';
|
||||
import 'package:diameter/screens/log/log.dart';
|
||||
import 'package:diameter/screens/log/log_entry.dart';
|
||||
import 'package:diameter/screens/log/log_event_detail.dart';
|
||||
import 'package:diameter/screens/log/log_entry/log_entry.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_list.dart';
|
||||
import 'package:diameter/screens/meal/meal_category_detail.dart';
|
||||
|
@ -1,6 +1,8 @@
|
||||
import 'package:diameter/main.dart';
|
||||
import 'package:diameter/models/bolus_profile.dart';
|
||||
import 'package:diameter/objectbox.g.dart';
|
||||
import 'package:diameter/utils/date_time_utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
@Entity(uid: 3417770529060202389)
|
||||
class Bolus {
|
||||
@ -37,4 +39,22 @@ class Bolus {
|
||||
builder.link(Bolus_.bolusProfile, BolusProfile_.id.equals(id));
|
||||
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;
|
||||
}).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/models/log_bolus.dart';
|
||||
import 'package:diameter/models/log_event.dart';
|
||||
import 'package:diameter/models/log_meal.dart';
|
||||
import 'package:diameter/objectbox.g.dart';
|
||||
@ -28,6 +29,9 @@ class LogEntry {
|
||||
@Backlink('logEntry')
|
||||
final meals = ToMany<LogMeal>();
|
||||
|
||||
@Backlink('logEntry')
|
||||
final boli = ToMany<LogBolus>();
|
||||
|
||||
LogEntry({
|
||||
this.id = 0,
|
||||
required this.time,
|
||||
@ -47,7 +51,8 @@ class LogEntry {
|
||||
static Map<DateTime, List<LogEntry>> getDailyEntryMap() {
|
||||
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();
|
||||
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_list.dart';
|
||||
import 'package:diameter/screens/log/log.dart';
|
||||
import 'package:diameter/screens/log/log_entry.dart';
|
||||
import 'package:diameter/screens/log/log_event_detail.dart';
|
||||
import 'package:diameter/screens/log/log_entry/log_entry.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_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_list.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/navigation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
// import 'package:diameter/components/progress_indicator.dart';
|
||||
import 'package:diameter/models/basal_profile.dart';
|
||||
import 'package:diameter/screens/basal/basal_profile_detail.dart';
|
||||
|
||||
@ -24,7 +23,6 @@ class _BasalProfileListScreenState extends State<BasalProfileListScreen> {
|
||||
pickActiveProfileMode = false;
|
||||
_basalProfiles = BasalProfile.getAll();
|
||||
});
|
||||
// _basalProfiles.then((list) =>
|
||||
updateBanner();
|
||||
setState(() {
|
||||
if (message != null) {
|
||||
@ -94,7 +92,6 @@ class _BasalProfileListScreenState extends State<BasalProfileListScreen> {
|
||||
BasalProfile.setAllInactive;
|
||||
basalProfile.active = true;
|
||||
BasalProfile.put(basalProfile);
|
||||
// (exception: basalProfile.objectId!).then((_) =>
|
||||
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/models/log_entry.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:flutter/material.dart';
|
||||
// import 'package:diameter/components/progress_indicator.dart';
|
||||
|
||||
class ActiveLogEventListScreen extends StatefulWidget {
|
||||
static const String routeName = '/active-log-events';
|
||||
|
||||
final LogEntry? endLogEntry;
|
||||
final int endLogEntryId;
|
||||
final Function()? onSetEndTime;
|
||||
|
||||
const ActiveLogEventListScreen(
|
||||
{Key? key, this.endLogEntry, this.onSetEndTime})
|
||||
{Key? key, this.endLogEntryId = 0, this.onSetEndTime})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
@ -26,7 +23,13 @@ class ActiveLogEventListScreen extends StatefulWidget {
|
||||
class _ActiveLogEventListScreenState extends State<ActiveLogEventListScreen> {
|
||||
List<LogEvent> _activeLogEvents = [];
|
||||
|
||||
void refresh({String? message}) {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
reload();
|
||||
}
|
||||
|
||||
void reload({String? message}) {
|
||||
setState(() {
|
||||
_activeLogEvents = LogEvent.getAllOngoing();
|
||||
});
|
||||
@ -47,9 +50,9 @@ class _ActiveLogEventListScreenState extends State<ActiveLogEventListScreen> {
|
||||
void onStop(LogEvent event) async {
|
||||
event.endTime = DateTime.now();
|
||||
event.endLogEntry.target =
|
||||
widget.endLogEntry ?? LogEntry(time: DateTime.now());
|
||||
LogEntry.get(widget.endLogEntryId) ?? LogEntry(time: DateTime.now());
|
||||
LogEvent.put(event);
|
||||
refresh();
|
||||
reload();
|
||||
if (widget.onSetEndTime != null) {
|
||||
widget.onSetEndTime!();
|
||||
}
|
||||
@ -69,7 +72,7 @@ class _ActiveLogEventListScreenState extends State<ActiveLogEventListScreen> {
|
||||
|
||||
void onDelete(LogEvent event) {
|
||||
LogEvent.remove(event.id);
|
||||
refresh(message: 'Event deleted');
|
||||
reload(message: 'Event deleted');
|
||||
}
|
||||
|
||||
void handleDeleteAction(LogEvent event) async {
|
||||
@ -84,11 +87,6 @@ class _ActiveLogEventListScreenState extends State<ActiveLogEventListScreen> {
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
refresh();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@ -101,20 +99,7 @@ class _ActiveLogEventListScreenState extends State<ActiveLogEventListScreen> {
|
||||
primary: false,
|
||||
automaticallyImplyLeading: false,
|
||||
actions: [
|
||||
IconButton(
|
||||
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),
|
||||
IconButton(icon: const Icon(Icons.refresh), onPressed: reload),
|
||||
],
|
||||
),
|
||||
_activeLogEvents.isNotEmpty ?
|
||||
|
@ -2,7 +2,7 @@ import 'package:diameter/components/dialogs.dart';
|
||||
import 'package:diameter/config.dart';
|
||||
import 'package:diameter/models/log_entry.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:flutter/material.dart';
|
||||
|
||||
@ -17,7 +17,13 @@ class LogScreen extends StatefulWidget {
|
||||
class _LogScreenState extends State<LogScreen> {
|
||||
late Map<DateTime, List<LogEntry>> _logEntryDailyMap;
|
||||
|
||||
void refresh({String? message}) {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
reload();
|
||||
}
|
||||
|
||||
void reload({String? message}) {
|
||||
setState(() {
|
||||
_logEntryDailyMap = LogEntry.getDailyEntryMap();
|
||||
});
|
||||
@ -36,7 +42,7 @@ class _LogScreenState extends State<LogScreen> {
|
||||
|
||||
void onDelete(LogEntry logEntry) {
|
||||
LogEntry.remove(logEntry.id);
|
||||
refresh(message: 'Log Entry deleted');
|
||||
reload(message: 'Log Entry deleted');
|
||||
}
|
||||
|
||||
void handleDeleteAction(LogEntry logEntry) async {
|
||||
@ -51,12 +57,6 @@ class _LogScreenState extends State<LogScreen> {
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
refresh();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
@ -64,7 +64,7 @@ class _LogScreenState extends State<LogScreen> {
|
||||
title: const Text('Log Entries'),
|
||||
actions: <Widget>[
|
||||
IconButton(
|
||||
onPressed: refresh,
|
||||
onPressed: reload,
|
||||
icon: const Icon(Icons.refresh)
|
||||
),
|
||||
],
|
||||
@ -101,7 +101,7 @@ class _LogScreenState extends State<LogScreen> {
|
||||
LogEntryScreen(
|
||||
id: logEntry.id),
|
||||
),
|
||||
).then((message) => refresh(
|
||||
).then((message) => reload(
|
||||
message: message));
|
||||
},
|
||||
title: Text(
|
||||
@ -150,7 +150,7 @@ class _LogScreenState extends State<LogScreen> {
|
||||
MaterialPageRoute(
|
||||
builder: (context) => const LogEntryScreen(),
|
||||
),
|
||||
).then((message) => refresh(message: message));
|
||||
).then((message) => reload(message: message));
|
||||
},
|
||||
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/forms.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_event.dart';
|
||||
import 'package:diameter/models/log_meal.dart';
|
||||
import 'package:diameter/navigation.dart';
|
||||
import 'package:diameter/screens/log/log_event_detail.dart';
|
||||
import 'package:diameter/screens/log/log_event_list.dart';
|
||||
import 'package:diameter/screens/log/log_meal_detail.dart';
|
||||
import 'package:diameter/screens/log/log_meal_list.dart';
|
||||
import 'package:diameter/screens/log/log_entry/log_bolus_detail.dart';
|
||||
import 'package:diameter/screens/log/log_entry/log_bolus_list.dart';
|
||||
import 'package:diameter/screens/log/log_entry/log_event_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/utils/date_time_utils.dart';
|
||||
import 'package:diameter/utils/utils.dart';
|
||||
@ -25,6 +30,9 @@ class LogEntryScreen extends StatefulWidget {
|
||||
|
||||
class _LogEntryScreenState extends State<LogEntryScreen> {
|
||||
LogEntry? _logEntry;
|
||||
List<LogMeal> _logMeals = [];
|
||||
List<LogEvent> _logEvents = [];
|
||||
List<LogBolus> _logBoli = [];
|
||||
bool _isNew = true;
|
||||
bool _isSaving = false;
|
||||
|
||||
@ -42,6 +50,7 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
|
||||
final _notesController = TextEditingController(text: '');
|
||||
|
||||
late FloatingActionButton addMealButton;
|
||||
late FloatingActionButton addBolusButton;
|
||||
late FloatingActionButton addEventButton;
|
||||
late IconButton refreshButton;
|
||||
late IconButton closeButton;
|
||||
@ -62,6 +71,11 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
|
||||
child: const Icon(Icons.add),
|
||||
);
|
||||
|
||||
addBolusButton = FloatingActionButton(
|
||||
onPressed: handleAddNewBolus,
|
||||
child: const Icon(Icons.add),
|
||||
);
|
||||
|
||||
addEventButton = FloatingActionButton(
|
||||
onPressed: handleAddNewEvent,
|
||||
child: const Icon(Icons.add),
|
||||
@ -105,6 +119,9 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
|
||||
if (widget.id != 0) {
|
||||
setState(() {
|
||||
_logEntry = LogEntry.get(widget.id);
|
||||
_logMeals = _logEntry?.meals ?? [];
|
||||
_logEvents = _logEntry?.events ?? [];
|
||||
_logBoli = _logEntry?.boli ?? [];
|
||||
});
|
||||
_isNew = _logEntry == null;
|
||||
}
|
||||
@ -216,7 +233,18 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
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));
|
||||
@ -227,7 +255,7 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) {
|
||||
return LogEventDetailScreen(logEntry: _logEntry!);
|
||||
return LogEventDetailScreen(logEntryId: _logEntry!.id);
|
||||
},
|
||||
),
|
||||
).then((message) => reload(message: message));
|
||||
@ -243,6 +271,11 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
|
||||
bottomNav = null;
|
||||
break;
|
||||
case 2:
|
||||
actionButton = addBolusButton;
|
||||
appBarActions = [refreshButton, closeButton];
|
||||
bottomNav = null;
|
||||
break;
|
||||
case 3:
|
||||
actionButton = addEventButton;
|
||||
appBarActions = [refreshButton, closeButton];
|
||||
bottomNav = null;
|
||||
@ -259,7 +292,7 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return DefaultTabController(
|
||||
length: _isNew ? 1 : 3,
|
||||
length: _isNew ? 1 : 4,
|
||||
child: Builder(builder: (BuildContext context) {
|
||||
final TabController tabController = DefaultTabController.of(context)!;
|
||||
tabController.addListener(() {
|
||||
@ -452,8 +485,12 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
|
||||
];
|
||||
|
||||
if (!_isNew) {
|
||||
tabs.add(LogMealListScreen(logEntry: _logEntry!, reload: reload));
|
||||
tabs.add(LogEventListScreen(logEntry: _logEntry!, reload: reload));
|
||||
tabs.add(LogMealListScreen(
|
||||
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(
|
||||
@ -465,6 +502,7 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
|
||||
tabs: [
|
||||
Tab(text: 'GENERAL'),
|
||||
Tab(text: 'MEALS'),
|
||||
Tab(text: 'BOLI'),
|
||||
Tab(text: 'EVENTS'),
|
||||
],
|
||||
),
|
@ -1,5 +1,6 @@
|
||||
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/log_entry.dart';
|
||||
@ -10,11 +11,13 @@ import 'package:flutter/material.dart';
|
||||
|
||||
class LogEventDetailScreen extends StatefulWidget {
|
||||
static const String routeName = '/log-event';
|
||||
final LogEntry? logEntry;
|
||||
final LogEntry? endLogEntry;
|
||||
final LogEvent? logEvent;
|
||||
|
||||
final int logEntryId;
|
||||
final int endLogEntryId;
|
||||
final int id;
|
||||
|
||||
const LogEventDetailScreen(
|
||||
{Key? key, this.logEntry, this.endLogEntry, this.logEvent})
|
||||
{Key? key, this.logEntryId = 0, this.endLogEntryId = 0, this.id = 0})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
@ -22,59 +25,55 @@ class LogEventDetailScreen extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _LogEventDetailScreenState extends State<LogEventDetailScreen> {
|
||||
LogEvent? _logEvent;
|
||||
bool _isNew = true;
|
||||
bool _isSaving = false;
|
||||
|
||||
final GlobalKey<FormState> _logEventForm = GlobalKey<FormState>();
|
||||
|
||||
final _notesController = TextEditingController(text: '');
|
||||
LogEventType? _eventType;
|
||||
bool _hasEndTime = false;
|
||||
final _notesController = TextEditingController(text: '');
|
||||
|
||||
List<LogEventType> _logEventTypes = [];
|
||||
|
||||
bool _isSaving = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
reload();
|
||||
|
||||
if (widget.logEvent != null) {
|
||||
_notesController.text = widget.logEvent!.notes ?? '';
|
||||
_eventType = widget.logEvent!.eventType.target;
|
||||
_hasEndTime = widget.logEvent!.hasEndTime;
|
||||
if (_logEvent != null) {
|
||||
_notesController.text = _logEvent!.notes ?? '';
|
||||
_eventType = _logEvent!.eventType.target;
|
||||
_hasEndTime = _logEvent!.hasEndTime;
|
||||
}
|
||||
|
||||
_logEventTypes = LogEventType.getAll();
|
||||
}
|
||||
|
||||
void reload() {
|
||||
if (widget.id != 0) {
|
||||
setState(() {
|
||||
_logEvent = LogEvent.get(widget.id);
|
||||
});
|
||||
}
|
||||
_isNew = _logEvent == null;
|
||||
}
|
||||
|
||||
void handleSaveAction() async {
|
||||
setState(() {
|
||||
_isSaving = true;
|
||||
});
|
||||
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(
|
||||
id: widget.logEvent?.id ?? 0,
|
||||
time: widget.logEntry!.time,
|
||||
id: widget.id,
|
||||
time: LogEntry.get(widget.logEntryId)!.time,
|
||||
hasEndTime: _hasEndTime,
|
||||
notes: _notesController.text,
|
||||
);
|
||||
event.eventType.target = _eventType;
|
||||
LogEvent.put(event);
|
||||
Navigator.pop(context, '${isNew ? 'New' : ''} Event Saved');
|
||||
Navigator.pop(context, '${_isNew ? 'New' : ''} Event Saved');
|
||||
}
|
||||
setState(() {
|
||||
_isSaving = false;
|
||||
@ -82,19 +81,18 @@ class _LogEventDetailScreenState extends State<LogEventDetailScreen> {
|
||||
}
|
||||
|
||||
void handleCancelAction() {
|
||||
bool isNew = widget.logEvent == null;
|
||||
if (showConfirmationDialogOnCancel &&
|
||||
((isNew &&
|
||||
((_isNew &&
|
||||
(_notesController.text != '' ||
|
||||
_eventType != null ||
|
||||
_hasEndTime)) ||
|
||||
(!isNew &&
|
||||
(_notesController.text != (widget.logEvent!.notes ?? '') ||
|
||||
_eventType != widget.logEvent!.eventType.target ||
|
||||
_hasEndTime != widget.logEvent!.hasEndTime)))) {
|
||||
(!_isNew &&
|
||||
(_notesController.text != (_logEvent!.notes ?? '') ||
|
||||
_eventType != _logEvent!.eventType.target ||
|
||||
_hasEndTime != _logEvent!.hasEndTime)))) {
|
||||
Dialogs.showCancelConfirmationDialog(
|
||||
context: context,
|
||||
isNew: isNew,
|
||||
isNew: _isNew,
|
||||
onSave: handleSaveAction,
|
||||
);
|
||||
} else {
|
||||
@ -104,10 +102,9 @@ class _LogEventDetailScreenState extends State<LogEventDetailScreen> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
bool isNew = widget.logEvent == null;
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(isNew ? 'New Event' : 'Edit Event'),
|
||||
title: Text(_isNew ? 'New Event' : 'Edit Event'),
|
||||
),
|
||||
drawer: const Navigation(currentLocation: LogEventDetailScreen.routeName),
|
||||
body: SingleChildScrollView(
|
||||
@ -117,11 +114,11 @@ class _LogEventDetailScreenState extends State<LogEventDetailScreen> {
|
||||
FormWrapper(
|
||||
formState: _logEventForm,
|
||||
fields: [
|
||||
LabeledDropdownButton<LogEventType>(
|
||||
AutoCompleteDropdownButton<LogEventType>(
|
||||
selectedItem: _eventType,
|
||||
label: 'Event Type',
|
||||
items: _logEventTypes,
|
||||
renderItem: (item) => Text(item.value),
|
||||
renderItem: (item) => item.value,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_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/models/log_entry.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:flutter/material.dart';
|
||||
|
||||
class LogEventListScreen extends StatefulWidget {
|
||||
final LogEntry logEntry;
|
||||
final List<LogEvent> logEvents;
|
||||
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);
|
||||
|
||||
@override
|
||||
@ -39,8 +41,8 @@ class _LogEventListScreenState extends State<LogEventListScreen> {
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => LogEventDetailScreen(
|
||||
endLogEntry: widget.logEntry,
|
||||
logEvent: event,
|
||||
logEntryId: widget.logEntry.id,
|
||||
id: event.id,
|
||||
),
|
||||
),
|
||||
).then((message) => reload(message: message));
|
||||
@ -69,12 +71,15 @@ class _LogEventListScreenState extends State<LogEventListScreen> {
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Expanded(
|
||||
child: (widget.logEntry.events.isNotEmpty || widget.logEntry.endedEvents.isNotEmpty)
|
||||
child: (widget.logEntry.events.isNotEmpty ||
|
||||
widget.logEntry.endedEvents.isNotEmpty)
|
||||
? ListView.builder(
|
||||
shrinkWrap: true,
|
||||
itemCount: widget.logEntry.events.length + widget.logEntry.endedEvents.length,
|
||||
itemCount: widget.logEntry.events.length +
|
||||
widget.logEntry.endedEvents.length,
|
||||
itemBuilder: (context, index) {
|
||||
final event = (widget.logEntry.events + widget.logEntry.endedEvents)[index];
|
||||
final event = (widget.logEntry.events +
|
||||
widget.logEntry.endedEvents)[index];
|
||||
return ListTile(
|
||||
onTap: () {
|
||||
handleEditAction(event);
|
||||
@ -103,7 +108,8 @@ class _LogEventListScreenState extends State<LogEventListScreen> {
|
||||
);
|
||||
})
|
||||
: 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/dialogs.dart';
|
||||
import 'package:diameter/components/dropdown.dart';
|
||||
import 'package:diameter/components/forms.dart';
|
||||
import 'package:diameter/config.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/meal.dart';
|
||||
import 'package:diameter/models/meal_category.dart';
|
||||
@ -16,9 +16,11 @@ import 'package:flutter/material.dart';
|
||||
|
||||
class LogMealDetailScreen extends StatefulWidget {
|
||||
static const String routeName = '/log-meal';
|
||||
final LogEntry logEntry;
|
||||
final LogMeal? logMeal;
|
||||
const LogMealDetailScreen({Key? key, required this.logEntry, this.logMeal})
|
||||
|
||||
final int logEntryId;
|
||||
final int id;
|
||||
|
||||
const LogMealDetailScreen({Key? key, this.logEntryId = 0, this.id = 0})
|
||||
: super(key: key);
|
||||
|
||||
@override
|
||||
@ -26,6 +28,10 @@ class LogMealDetailScreen extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
|
||||
LogMeal? _logMeal;
|
||||
bool _isNew = true;
|
||||
bool _isSaving = false;
|
||||
|
||||
final GlobalKey<FormState> _logMealForm = GlobalKey<FormState>();
|
||||
|
||||
final _valueController = TextEditingController(text: '');
|
||||
@ -36,6 +42,7 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
|
||||
final _delayedBolusRateController = TextEditingController(text: '');
|
||||
final _delayedBolusDurationController = TextEditingController(text: '');
|
||||
final _notesController = TextEditingController(text: '');
|
||||
|
||||
Meal? _meal;
|
||||
MealSource? _mealSource;
|
||||
MealCategory? _mealCategory;
|
||||
@ -50,11 +57,10 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
|
||||
List<Accuracy> _portionSizeAccuracies = [];
|
||||
List<Accuracy> _carbsRatioAccuracies = [];
|
||||
|
||||
bool _isSaving = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
reload();
|
||||
|
||||
_portionSizeAccuracies = Accuracy.getAllForPortionSize();
|
||||
_carbsRatioAccuracies = Accuracy.getAllForCarbsRatio();
|
||||
@ -63,35 +69,30 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
|
||||
_mealPortionTypes = MealPortionType.getAll();
|
||||
_mealSources = MealSource.getAll();
|
||||
|
||||
if (widget.logMeal != null) {
|
||||
_valueController.text = widget.logMeal!.value;
|
||||
_carbsRatioController.text =
|
||||
(widget.logMeal!.carbsRatio ?? '').toString();
|
||||
_portionSizeController.text =
|
||||
(widget.logMeal!.portionSize ?? '').toString();
|
||||
if (widget.id != 0) {
|
||||
_valueController.text = _logMeal!.value;
|
||||
_carbsRatioController.text = (_logMeal!.carbsRatio ?? '').toString();
|
||||
_portionSizeController.text = (_logMeal!.portionSize ?? '').toString();
|
||||
_carbsPerPortionController.text =
|
||||
(widget.logMeal!.carbsPerPortion ?? '').toString();
|
||||
_bolusController.text = (widget.logMeal!.bolus ?? '').toString();
|
||||
(_logMeal!.carbsPerPortion ?? '').toString();
|
||||
_bolusController.text = (_logMeal!.bolus ?? '').toString();
|
||||
_delayedBolusRateController.text =
|
||||
(widget.logMeal!.delayedBolusRate ?? '').toString();
|
||||
(_logMeal!.delayedBolusRate ?? '').toString();
|
||||
_delayedBolusDurationController.text =
|
||||
(widget.logMeal!.delayedBolusDuration ?? '').toString();
|
||||
_notesController.text = widget.logMeal!.notes ?? '';
|
||||
|
||||
// _meal = widget.logMeal!.meal;
|
||||
// _source = widget.logMeal!.source;
|
||||
// _category = widget.logMeal!.category;
|
||||
// _portionType = widget.logMeal!.portionType;
|
||||
// _portionSizeAccuracy = _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;
|
||||
(_logMeal!.delayedBolusDuration ?? '').toString();
|
||||
_notesController.text = _logMeal!.notes ?? '';
|
||||
}
|
||||
}
|
||||
|
||||
void reload() {
|
||||
if (widget.id != 0) {
|
||||
setState(() {
|
||||
_logMeal = LogMeal.get(widget.id);
|
||||
});
|
||||
}
|
||||
_isNew = _logMeal == null;
|
||||
}
|
||||
|
||||
Future<void> onSelectMeal(Meal meal) async {
|
||||
setState(() {
|
||||
_meal = meal;
|
||||
@ -135,52 +136,8 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
|
||||
_isSaving = true;
|
||||
});
|
||||
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(
|
||||
id: widget.logMeal?.id ?? 0,
|
||||
id: widget.id,
|
||||
value: _valueController.text,
|
||||
carbsRatio: double.tryParse(_carbsRatioController.text),
|
||||
portionSize: double.tryParse(_portionSizeController.text),
|
||||
@ -199,7 +156,7 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
|
||||
logMeal.carbsRatioAccuracy.target = _carbsRatioAccuracy;
|
||||
|
||||
LogMeal.put(logMeal);
|
||||
Navigator.pop(context, '${isNew ? 'New' : ''} Meal Saved');
|
||||
Navigator.pop(context, '${_isNew ? 'New' : ''} Meal Saved');
|
||||
}
|
||||
setState(() {
|
||||
_isSaving = false;
|
||||
@ -207,9 +164,8 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
|
||||
}
|
||||
|
||||
void handleCancelAction() {
|
||||
bool isNew = widget.logMeal == null;
|
||||
if (showConfirmationDialogOnCancel &&
|
||||
((isNew &&
|
||||
((_isNew &&
|
||||
(_valueController.text != '' ||
|
||||
_meal != null ||
|
||||
_mealSource != null ||
|
||||
@ -225,35 +181,32 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
|
||||
null ||
|
||||
double.tryParse(_delayedBolusRateController.text) != null ||
|
||||
_notesController.text != '')) ||
|
||||
(!isNew &&
|
||||
(_valueController.text != widget.logMeal!.value ||
|
||||
_meal != widget.logMeal!.meal.target ||
|
||||
_mealSource != widget.logMeal!.mealSource.target ||
|
||||
_mealCategory != widget.logMeal!.mealCategory.target ||
|
||||
_mealPortionType != widget.logMeal!.mealPortionType.target ||
|
||||
(!_isNew &&
|
||||
(_valueController.text != _logMeal!.value ||
|
||||
_meal != _logMeal!.meal.target ||
|
||||
_mealSource != _logMeal!.mealSource.target ||
|
||||
_mealCategory != _logMeal!.mealCategory.target ||
|
||||
_mealPortionType != _logMeal!.mealPortionType.target ||
|
||||
double.tryParse(_carbsRatioController.text) !=
|
||||
widget.logMeal!.carbsRatio ||
|
||||
_logMeal!.carbsRatio ||
|
||||
double.tryParse(_portionSizeController.text) !=
|
||||
widget.logMeal!.portionSize ||
|
||||
_logMeal!.portionSize ||
|
||||
double.tryParse(_carbsPerPortionController.text) !=
|
||||
widget.logMeal!.carbsPerPortion ||
|
||||
// _carbsRatioAccuracy != widget.logMeal!.carbsRatioAccuracy ||
|
||||
// _portionSizeAccuracy !=
|
||||
// widget.logMeal!.portionSizeAccuracy ||
|
||||
_logMeal!.carbsPerPortion ||
|
||||
_carbsRatioAccuracy !=
|
||||
widget.logMeal!.carbsRatioAccuracy.target ||
|
||||
_logMeal!.carbsRatioAccuracy.target ||
|
||||
_portionSizeAccuracy !=
|
||||
widget.logMeal!.portionSizeAccuracy.target ||
|
||||
_logMeal!.portionSizeAccuracy.target ||
|
||||
double.tryParse(_bolusController.text) !=
|
||||
widget.logMeal!.bolus ||
|
||||
_logMeal!.bolus ||
|
||||
int.tryParse(_delayedBolusDurationController.text) !=
|
||||
widget.logMeal!.delayedBolusDuration ||
|
||||
_logMeal!.delayedBolusDuration ||
|
||||
double.tryParse(_delayedBolusRateController.text) !=
|
||||
widget.logMeal!.delayedBolusRate ||
|
||||
_notesController.text != (widget.logMeal!.notes ?? ''))))) {
|
||||
_logMeal!.delayedBolusRate ||
|
||||
_notesController.text != (_logMeal!.notes ?? ''))))) {
|
||||
Dialogs.showCancelConfirmationDialog(
|
||||
context: context,
|
||||
isNew: isNew,
|
||||
isNew: _isNew,
|
||||
onSave: handleSaveAction,
|
||||
);
|
||||
} else {
|
||||
@ -305,10 +258,9 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
bool isNew = widget.logMeal == null;
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(isNew ? 'New Meal' : widget.logMeal!.value),
|
||||
title: Text(_isNew ? 'New Meal' : _logMeal!.value),
|
||||
),
|
||||
drawer: const Navigation(currentLocation: LogMealDetailScreen.routeName),
|
||||
body: SingleChildScrollView(
|
||||
@ -330,48 +282,44 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
|
||||
return null;
|
||||
},
|
||||
),
|
||||
LabeledDropdownButton<Meal>(
|
||||
AutoCompleteDropdownButton<Meal>(
|
||||
selectedItem: _meal,
|
||||
label: 'Meal',
|
||||
items: _meals,
|
||||
// getItemValue: (item) => item.objectId,
|
||||
renderItem: (item) => Text(item.value),
|
||||
renderItem: (item) => item.value,
|
||||
onChanged: (value) {
|
||||
if (value != null) {
|
||||
onSelectMeal(value);
|
||||
}
|
||||
},
|
||||
),
|
||||
LabeledDropdownButton<MealSource>(
|
||||
AutoCompleteDropdownButton<MealSource>(
|
||||
selectedItem: _mealSource,
|
||||
label: 'Meal Source',
|
||||
items: _mealSources,
|
||||
// getItemValue: (item) => item.objectId,
|
||||
renderItem: (item) => Text(item.value),
|
||||
renderItem: (item) => item.value,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_mealSource = value;
|
||||
});
|
||||
},
|
||||
),
|
||||
LabeledDropdownButton<MealCategory>(
|
||||
AutoCompleteDropdownButton<MealCategory>(
|
||||
selectedItem: _mealCategory,
|
||||
label: 'Meal Category',
|
||||
items: _mealCategories,
|
||||
// getItemValue: (item) => item.objectId,
|
||||
renderItem: (item) => Text(item.value),
|
||||
renderItem: (item) => item.value,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_mealCategory = value;
|
||||
});
|
||||
},
|
||||
),
|
||||
LabeledDropdownButton<MealPortionType>(
|
||||
AutoCompleteDropdownButton<MealPortionType>(
|
||||
selectedItem: _mealPortionType,
|
||||
label: 'Meal Portion Type',
|
||||
items: _mealPortionTypes,
|
||||
// getItemValue: (item) => item.objectId,
|
||||
renderItem: (item) => Text(item.value),
|
||||
renderItem: (item) => item.value,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_mealPortionType = value;
|
||||
@ -434,30 +382,18 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
|
||||
),
|
||||
],
|
||||
),
|
||||
LabeledDropdownButton<Accuracy>(
|
||||
AutoCompleteDropdownButton<Accuracy>(
|
||||
selectedItem: _portionSizeAccuracy,
|
||||
label: 'Portion Size Accuracy',
|
||||
items: _portionSizeAccuracies,
|
||||
// getItemValue: (item) => item.objectId,
|
||||
renderItem: (item) => Text(item.value),
|
||||
renderItem: (item) => item.value,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_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(
|
||||
children: [
|
||||
Expanded(
|
||||
@ -488,30 +424,17 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
|
||||
),
|
||||
],
|
||||
),
|
||||
LabeledDropdownButton<Accuracy>(
|
||||
AutoCompleteDropdownButton<Accuracy>(
|
||||
selectedItem: _carbsRatioAccuracy,
|
||||
label: 'Carbs Ratio Accuracy',
|
||||
items: _carbsRatioAccuracies,
|
||||
// getItemValue: (item) => item.objectId,
|
||||
renderItem: (item) => Text(item.value),
|
||||
renderItem: (item) => item.value,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_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(
|
||||
decoration: const InputDecoration(
|
||||
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 {
|
||||
static const String routeName = '/log-event-type';
|
||||
final LogEventType? logEventType;
|
||||
const LogEventTypeDetailScreen({Key? key, this.logEventType})
|
||||
: super(key: key);
|
||||
final int id;
|
||||
const LogEventTypeDetailScreen({Key? key, this.id = 0}) : super(key: key);
|
||||
|
||||
@override
|
||||
_LogEventTypeDetailScreenState createState() =>
|
||||
@ -18,35 +17,49 @@ class LogEventTypeDetailScreen extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _LogEventTypeDetailScreenState extends State<LogEventTypeDetailScreen> {
|
||||
LogEventType? _logEventType;
|
||||
bool _isNew = true;
|
||||
bool _isSaving = false;
|
||||
|
||||
final GlobalKey<FormState> _logEventTypeForm = GlobalKey<FormState>();
|
||||
|
||||
final _valueController = TextEditingController(text: '');
|
||||
final _defaultReminderDurationController = TextEditingController(text: '');
|
||||
final _notesController = TextEditingController(text: '');
|
||||
bool _hasEndTime = false;
|
||||
|
||||
bool _isSaving = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
if (widget.logEventType != null) {
|
||||
_valueController.text = widget.logEventType!.value;
|
||||
reload();
|
||||
|
||||
if (_logEventType != null) {
|
||||
_valueController.text = _logEventType!.value;
|
||||
_defaultReminderDurationController.text =
|
||||
(widget.logEventType!.defaultReminderDuration ?? '').toString();
|
||||
_notesController.text = widget.logEventType!.notes ?? '';
|
||||
_hasEndTime = widget.logEventType!.hasEndTime;
|
||||
(_logEventType!.defaultReminderDuration ?? '').toString();
|
||||
_notesController.text = _logEventType!.notes ?? '';
|
||||
_hasEndTime = _logEventType!.hasEndTime;
|
||||
}
|
||||
}
|
||||
|
||||
void reload() {
|
||||
if (widget.id != 0) {
|
||||
setState(() {
|
||||
_logEventType = LogEventType.get(widget.id);
|
||||
});
|
||||
}
|
||||
_isNew = _logEventType == null;
|
||||
}
|
||||
|
||||
void handleSaveAction() async {
|
||||
setState(() {
|
||||
_isSaving = true;
|
||||
});
|
||||
if (_logEventTypeForm.currentState!.validate()) {
|
||||
bool isNew = widget.logEventType == null;
|
||||
bool isNew = _logEventType == null;
|
||||
LogEventType.put(LogEventType(
|
||||
id: widget.logEventType?.id ?? 0,
|
||||
id: widget.id,
|
||||
value: _valueController.text,
|
||||
notes: _notesController.text,
|
||||
defaultReminderDuration:
|
||||
@ -61,7 +74,7 @@ class _LogEventTypeDetailScreenState extends State<LogEventTypeDetailScreen> {
|
||||
}
|
||||
|
||||
void handleCancelAction() {
|
||||
bool isNew = widget.logEventType == null;
|
||||
bool isNew = _logEventType == null;
|
||||
if (showConfirmationDialogOnCancel &&
|
||||
((isNew &&
|
||||
(_valueController.text != '' ||
|
||||
@ -70,12 +83,11 @@ class _LogEventTypeDetailScreenState extends State<LogEventTypeDetailScreen> {
|
||||
_notesController.text != '' ||
|
||||
_hasEndTime)) ||
|
||||
(!isNew &&
|
||||
(_valueController.text != widget.logEventType!.value ||
|
||||
(_valueController.text != _logEventType!.value ||
|
||||
int.tryParse(_defaultReminderDurationController.text) !=
|
||||
widget.logEventType!.defaultReminderDuration ||
|
||||
_notesController.text !=
|
||||
(widget.logEventType!.notes ?? '') ||
|
||||
_hasEndTime != widget.logEventType!.hasEndTime)))) {
|
||||
_logEventType!.defaultReminderDuration ||
|
||||
_notesController.text != (_logEventType!.notes ?? '') ||
|
||||
_hasEndTime != _logEventType!.hasEndTime)))) {
|
||||
Dialogs.showCancelConfirmationDialog(
|
||||
context: context,
|
||||
isNew: isNew,
|
||||
@ -88,10 +100,9 @@ class _LogEventTypeDetailScreenState extends State<LogEventTypeDetailScreen> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
bool isNew = widget.logEventType == null;
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(isNew ? 'New Log Event Type' : widget.logEventType!.value),
|
||||
title: Text(_isNew ? 'New Log Event Type' : _logEventType!.value),
|
||||
),
|
||||
drawer:
|
||||
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/navigation.dart';
|
||||
import 'package:diameter/screens/log/log_event_type_detail.dart';
|
||||
@ -15,7 +14,13 @@ class LogEventTypeListScreen extends StatefulWidget {
|
||||
class _LogEventTypeListScreenState extends State<LogEventTypeListScreen> {
|
||||
List<LogEventType> _logEventTypes = [];
|
||||
|
||||
void refresh({String? message}) {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
reload();
|
||||
}
|
||||
|
||||
void reload({String? message}) {
|
||||
setState(() {
|
||||
_logEventTypes = LogEventType.getAll();
|
||||
});
|
||||
@ -32,17 +37,11 @@ class _LogEventTypeListScreenState extends State<LogEventTypeListScreen> {
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
refresh();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
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:
|
||||
const Navigation(currentLocation: LogEventTypeListScreen.routeName),
|
||||
@ -54,7 +53,6 @@ class _LogEventTypeListScreenState extends State<LogEventTypeListScreen> {
|
||||
padding: const EdgeInsets.all(10.0),
|
||||
itemCount: _logEventTypes.length,
|
||||
itemBuilder: (context, index) {
|
||||
// final logEventType = snapshot.data![index];
|
||||
final logEventType = _logEventTypes[index];
|
||||
return ListTile(
|
||||
onTap: () {
|
||||
@ -63,9 +61,9 @@ class _LogEventTypeListScreenState extends State<LogEventTypeListScreen> {
|
||||
MaterialPageRoute(
|
||||
builder: (context) =>
|
||||
LogEventTypeDetailScreen(
|
||||
logEventType: logEventType),
|
||||
id: logEventType.id),
|
||||
),
|
||||
).then((message) => refresh(message: message));
|
||||
).then((message) => reload(message: message));
|
||||
},
|
||||
title: Text(logEventType.value),
|
||||
subtitle: Text(logEventType.notes ?? ''),
|
||||
@ -76,7 +74,7 @@ class _LogEventTypeListScreenState extends State<LogEventTypeListScreen> {
|
||||
onPressed: () async {
|
||||
LogEventType.remove(logEventType.id);
|
||||
// await logEventType.delete().then((_) {
|
||||
refresh(
|
||||
reload(
|
||||
message: 'Log Event Type deleted');
|
||||
// });
|
||||
},
|
||||
@ -100,7 +98,7 @@ class _LogEventTypeListScreenState extends State<LogEventTypeListScreen> {
|
||||
MaterialPageRoute(
|
||||
builder: (context) => const LogEventTypeDetailScreen(),
|
||||
),
|
||||
).then((message) => refresh(message: message));
|
||||
).then((message) => reload(message: message));
|
||||
},
|
||||
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 {
|
||||
static const String routeName = '/meal-category';
|
||||
final MealCategory? mealCategory;
|
||||
final int id;
|
||||
|
||||
const MealCategoryDetailScreen({Key? key, this.mealCategory})
|
||||
: super(key: key);
|
||||
const MealCategoryDetailScreen({Key? key, this.id = 0}) : super(key: key);
|
||||
|
||||
@override
|
||||
_MealCategoryDetailScreenState createState() =>
|
||||
@ -19,45 +18,54 @@ class MealCategoryDetailScreen extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _MealCategoryDetailScreenState extends State<MealCategoryDetailScreen> {
|
||||
MealCategory? _mealCategory;
|
||||
bool _isNew = true;
|
||||
|
||||
final GlobalKey<FormState> _mealCategoryForm = GlobalKey<FormState>();
|
||||
|
||||
final _valueController = TextEditingController(text: '');
|
||||
final _notesController = TextEditingController(text: '');
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
if (widget.mealCategory != null) {
|
||||
_valueController.text = widget.mealCategory!.value;
|
||||
_notesController.text = widget.mealCategory!.notes ?? '';
|
||||
reload();
|
||||
|
||||
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 {
|
||||
if (_mealCategoryForm.currentState!.validate()) {
|
||||
bool isNew = widget.mealCategory == null;
|
||||
// isNew
|
||||
// ? await MealCategory.save(
|
||||
// value: _valueController.text, notes: _notesController.text)
|
||||
// : await MealCategory.update(widget.mealCategory!.objectId!,
|
||||
// value: _valueController.text, notes: _notesController.text);
|
||||
MealCategory.put(MealCategory(id: widget.mealCategory?.id ?? 0,
|
||||
value: _valueController.text, notes: _notesController.text));
|
||||
Navigator.pop(context, '${isNew ? 'New' : ''} Meal Category saved');
|
||||
MealCategory.put(MealCategory(
|
||||
id: widget.id,
|
||||
value: _valueController.text,
|
||||
notes: _notesController.text));
|
||||
Navigator.pop(context, '${_isNew ? 'New' : ''} Meal Category saved');
|
||||
}
|
||||
}
|
||||
|
||||
void handleCancelAction() {
|
||||
bool isNew = widget.mealCategory == null;
|
||||
|
||||
if (showConfirmationDialogOnCancel &&
|
||||
(isNew &&
|
||||
(_isNew &&
|
||||
(_valueController.text != '' || _notesController.text != '')) ||
|
||||
(!isNew &&
|
||||
(widget.mealCategory!.value != _valueController.text ||
|
||||
(widget.mealCategory!.notes ?? '') != _notesController.text))) {
|
||||
(!_isNew &&
|
||||
(_mealCategory!.value != _valueController.text ||
|
||||
(_mealCategory!.notes ?? '') != _notesController.text))) {
|
||||
Dialogs.showCancelConfirmationDialog(
|
||||
context: context,
|
||||
isNew: isNew,
|
||||
isNew: _isNew,
|
||||
onSave: handleSaveAction,
|
||||
);
|
||||
} else {
|
||||
@ -67,10 +75,9 @@ class _MealCategoryDetailScreenState extends State<MealCategoryDetailScreen> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
bool isNew = widget.mealCategory == null;
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(isNew ? 'New Meal Category' : widget.mealCategory!.value),
|
||||
title: Text(_isNew ? 'New Meal Category' : _mealCategory!.value),
|
||||
),
|
||||
drawer:
|
||||
const Navigation(currentLocation: MealCategoryDetailScreen.routeName),
|
||||
|
@ -18,7 +18,13 @@ class MealCategoryListScreen extends StatefulWidget {
|
||||
class _MealCategoryListScreenState extends State<MealCategoryListScreen> {
|
||||
List<MealCategory> _mealCategories = [];
|
||||
|
||||
void refresh({String? message}) {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
reload();
|
||||
}
|
||||
|
||||
void reload({String? message}) {
|
||||
setState(() {
|
||||
_mealCategories = MealCategory.getAll();
|
||||
});
|
||||
@ -37,7 +43,7 @@ class _MealCategoryListScreenState extends State<MealCategoryListScreen> {
|
||||
|
||||
void onDelete(MealCategory mealCategory) {
|
||||
MealCategory.remove(mealCategory.id);
|
||||
refresh(message: 'Meal Category deleted');
|
||||
reload(message: 'Meal Category deleted');
|
||||
}
|
||||
|
||||
void handleDeleteAction(MealCategory mealCategory) async {
|
||||
@ -52,12 +58,6 @@ class _MealCategoryListScreenState extends State<MealCategoryListScreen> {
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
refresh();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
@ -65,7 +65,7 @@ class _MealCategoryListScreenState extends State<MealCategoryListScreen> {
|
||||
title: const Text('Meal Categories'),
|
||||
actions: <Widget>[
|
||||
IconButton(
|
||||
onPressed: refresh,
|
||||
onPressed: reload,
|
||||
icon: const Icon(Icons.refresh),
|
||||
),
|
||||
],
|
||||
@ -89,10 +89,10 @@ class _MealCategoryListScreenState extends State<MealCategoryListScreen> {
|
||||
MaterialPageRoute(
|
||||
builder: (context) =>
|
||||
MealCategoryDetailScreen(
|
||||
mealCategory: mealCategory,
|
||||
id: mealCategory.id,
|
||||
),
|
||||
),
|
||||
).then((message) => refresh(message: message));
|
||||
).then((message) => reload(message: message));
|
||||
},
|
||||
title: Text(mealCategory.value),
|
||||
subtitle: Text(mealCategory.notes ?? ''),
|
||||
@ -124,7 +124,7 @@ class _MealCategoryListScreenState extends State<MealCategoryListScreen> {
|
||||
MaterialPageRoute(
|
||||
builder: (context) => const MealCategoryDetailScreen(),
|
||||
),
|
||||
).then((message) => refresh(message: message));
|
||||
).then((message) => reload(message: message));
|
||||
},
|
||||
child: const Icon(Icons.add),
|
||||
),
|
||||
|
@ -1,5 +1,6 @@
|
||||
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/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_source.dart';
|
||||
import 'package:diameter/navigation.dart';
|
||||
// import 'package:diameter/objectbox.g.dart';
|
||||
import 'package:diameter/settings.dart';
|
||||
import 'package:diameter/utils/utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class MealDetailScreen extends StatefulWidget {
|
||||
static const String routeName = '/meal';
|
||||
final int id;
|
||||
|
||||
final Meal? meal;
|
||||
const MealDetailScreen({Key? key, this.meal}) : super(key: key);
|
||||
const MealDetailScreen({Key? key, this.id = 0}) : super(key: key);
|
||||
|
||||
@override
|
||||
_MealDetailScreenState createState() => _MealDetailScreenState();
|
||||
}
|
||||
|
||||
class _MealDetailScreenState extends State<MealDetailScreen> {
|
||||
Meal? _meal;
|
||||
bool _isNew = true;
|
||||
bool _isSaving = false;
|
||||
|
||||
final GlobalKey<FormState> _mealForm = GlobalKey<FormState>();
|
||||
|
||||
final _valueController = TextEditingController(text: '');
|
||||
@ -46,84 +50,54 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
|
||||
List<Accuracy> _portionSizeAccuracies = [];
|
||||
List<Accuracy> _carbsRatioAccuracies = [];
|
||||
|
||||
bool isSaving = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
reload();
|
||||
|
||||
_portionSizeAccuracies = Accuracy.getAllForPortionSize();
|
||||
_carbsRatioAccuracies = Accuracy.getAllForCarbsRatio();
|
||||
_mealCategories = MealCategory.getAll();
|
||||
_mealPortionTypes = MealPortionType.getAll();
|
||||
_mealSources = MealSource.getAll();
|
||||
|
||||
if (widget.meal != null) {
|
||||
_valueController.text = widget.meal!.value;
|
||||
_carbsRatioController.text = (widget.meal!.carbsRatio ?? '').toString();
|
||||
_portionSizeController.text = (widget.meal!.portionSize ?? '').toString();
|
||||
if (_meal != null) {
|
||||
_valueController.text = _meal!.value;
|
||||
_carbsRatioController.text = (_meal!.carbsRatio ?? '').toString();
|
||||
_portionSizeController.text = (_meal!.portionSize ?? '').toString();
|
||||
_carbsPerPortionController.text =
|
||||
(widget.meal!.carbsPerPortion ?? '').toString();
|
||||
(_meal!.carbsPerPortion ?? '').toString();
|
||||
_delayedBolusRateController.text =
|
||||
(widget.meal!.delayedBolusRate ?? '').toString();
|
||||
(_meal!.delayedBolusRate ?? '').toString();
|
||||
_delayedBolusDurationController.text =
|
||||
(widget.meal!.delayedBolusDuration ?? '').toString();
|
||||
_notesController.text = widget.meal!.notes ?? '';
|
||||
(_meal!.delayedBolusDuration ?? '').toString();
|
||||
_notesController.text = _meal!.notes ?? '';
|
||||
|
||||
_mealSource = widget.meal!.mealSource.target;
|
||||
_mealCategory = widget.meal!.mealCategory.target;
|
||||
_mealPortionType = widget.meal!.mealPortionType.target;
|
||||
_portionSizeAccuracy = widget.meal!.portionSizeAccuracy.target;
|
||||
_carbsRatioAccuracy = widget.meal!.carbsRatioAccuracy.target;
|
||||
_mealSource = _meal!.mealSource.target;
|
||||
_mealCategory = _meal!.mealCategory.target;
|
||||
_mealPortionType = _meal!.mealPortionType.target;
|
||||
_portionSizeAccuracy = _meal!.portionSizeAccuracy.target;
|
||||
_carbsRatioAccuracy = _meal!.carbsRatioAccuracy.target;
|
||||
}
|
||||
}
|
||||
|
||||
void reload() {
|
||||
if (widget.id != 0) {
|
||||
setState(() {
|
||||
_meal = Meal.get(widget.id);
|
||||
});
|
||||
}
|
||||
_isNew = _meal == null;
|
||||
}
|
||||
|
||||
void handleSaveAction() async {
|
||||
setState(() {
|
||||
isSaving = true;
|
||||
_isSaving = true;
|
||||
});
|
||||
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(
|
||||
id: widget.meal?.id ?? 0,
|
||||
id: widget.id,
|
||||
value: _valueController.text,
|
||||
carbsRatio: double.tryParse(_carbsRatioController.text),
|
||||
portionSize: double.tryParse(_portionSizeController.text),
|
||||
@ -140,17 +114,16 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
|
||||
meal.carbsRatioAccuracy.target = _carbsRatioAccuracy;
|
||||
|
||||
Meal.put(meal);
|
||||
Navigator.pop(context, '${isNew ? 'New' : ''} Meal Saved');
|
||||
Navigator.pop(context, '${_isNew ? 'New' : ''} Meal Saved');
|
||||
}
|
||||
setState(() {
|
||||
isSaving = false;
|
||||
_isSaving = false;
|
||||
});
|
||||
}
|
||||
|
||||
void handleCancelAction() {
|
||||
bool isNew = widget.meal == null;
|
||||
if (showConfirmationDialogOnCancel &&
|
||||
((isNew &&
|
||||
((_isNew &&
|
||||
(_valueController.text != '' ||
|
||||
_mealSource != null ||
|
||||
_mealCategory != null ||
|
||||
@ -164,27 +137,27 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
|
||||
null ||
|
||||
double.tryParse(_delayedBolusRateController.text) != null ||
|
||||
_notesController.text != '')) ||
|
||||
(!isNew &&
|
||||
(_valueController.text != widget.meal!.value ||
|
||||
_mealSource != widget.meal!.mealSource.target ||
|
||||
_mealCategory != widget.meal!.mealCategory.target ||
|
||||
_mealPortionType != widget.meal!.mealPortionType.target ||
|
||||
(!_isNew &&
|
||||
(_valueController.text != _meal!.value ||
|
||||
_mealSource != _meal!.mealSource.target ||
|
||||
_mealCategory != _meal!.mealCategory.target ||
|
||||
_mealPortionType != _meal!.mealPortionType.target ||
|
||||
double.tryParse(_carbsRatioController.text) !=
|
||||
widget.meal!.carbsRatio ||
|
||||
_meal!.carbsRatio ||
|
||||
double.tryParse(_portionSizeController.text) !=
|
||||
widget.meal!.portionSize ||
|
||||
_meal!.portionSize ||
|
||||
double.tryParse(_carbsPerPortionController.text) !=
|
||||
widget.meal!.carbsPerPortion ||
|
||||
_carbsRatioAccuracy != widget.meal!.carbsRatioAccuracy.target ||
|
||||
_portionSizeAccuracy != widget.meal!.portionSizeAccuracy.target ||
|
||||
_meal!.carbsPerPortion ||
|
||||
_carbsRatioAccuracy != _meal!.carbsRatioAccuracy.target ||
|
||||
_portionSizeAccuracy != _meal!.portionSizeAccuracy.target ||
|
||||
int.tryParse(_delayedBolusDurationController.text) !=
|
||||
widget.meal!.delayedBolusDuration ||
|
||||
_meal!.delayedBolusDuration ||
|
||||
double.tryParse(_delayedBolusRateController.text) !=
|
||||
widget.meal!.delayedBolusRate ||
|
||||
_notesController.text != (widget.meal!.notes ?? ''))))) {
|
||||
_meal!.delayedBolusRate ||
|
||||
_notesController.text != (_meal!.notes ?? ''))))) {
|
||||
Dialogs.showCancelConfirmationDialog(
|
||||
context: context,
|
||||
isNew: isNew,
|
||||
isNew: _isNew,
|
||||
onSave: handleSaveAction,
|
||||
);
|
||||
} else {
|
||||
@ -196,12 +169,10 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
|
||||
setState(() {
|
||||
_mealSource = mealSource;
|
||||
if (mealSource.defaultCarbsRatioAccuracy.hasValue) {
|
||||
_carbsRatioAccuracy =
|
||||
mealSource.defaultCarbsRatioAccuracy.target;
|
||||
_carbsRatioAccuracy = mealSource.defaultCarbsRatioAccuracy.target;
|
||||
}
|
||||
if (mealSource.defaultPortionSizeAccuracy.hasValue) {
|
||||
_portionSizeAccuracy =
|
||||
mealSource.defaultPortionSizeAccuracy.target;
|
||||
_portionSizeAccuracy = mealSource.defaultPortionSizeAccuracy.target;
|
||||
}
|
||||
if (mealSource.defaultMealCategory.hasValue) {
|
||||
_mealCategory = mealSource.defaultMealCategory.target;
|
||||
@ -256,10 +227,9 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
bool isNew = widget.meal == null;
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(isNew ? 'New Meal' : widget.meal!.value),
|
||||
title: Text(_isNew ? 'New Meal' : _meal!.value),
|
||||
),
|
||||
drawer: const Navigation(currentLocation: MealDetailScreen.routeName),
|
||||
body: SingleChildScrollView(
|
||||
@ -281,36 +251,33 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
|
||||
return null;
|
||||
},
|
||||
),
|
||||
LabeledDropdownButton<MealSource>(
|
||||
AutoCompleteDropdownButton<MealSource>(
|
||||
selectedItem: _mealSource,
|
||||
label: 'Meal Source',
|
||||
items: _mealSources,
|
||||
// getItemValue: (item) => item.objectId,
|
||||
renderItem: (item) => Text(item.value),
|
||||
renderItem: (item) => item.value,
|
||||
onChanged: (value) {
|
||||
if (value != null) {
|
||||
onSelectMealSource(value);
|
||||
}
|
||||
},
|
||||
),
|
||||
LabeledDropdownButton<MealCategory>(
|
||||
AutoCompleteDropdownButton<MealCategory>(
|
||||
selectedItem: _mealCategory,
|
||||
label: 'Meal Category',
|
||||
items: _mealCategories,
|
||||
// getItemValue: (item) => item.objectId,
|
||||
renderItem: (item) => Text(item.value),
|
||||
renderItem: (item) => item.value,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_mealCategory = value;
|
||||
});
|
||||
},
|
||||
),
|
||||
LabeledDropdownButton<MealPortionType>(
|
||||
AutoCompleteDropdownButton<MealPortionType>(
|
||||
selectedItem: _mealPortionType,
|
||||
label: 'Meal Portion Type',
|
||||
items: _mealPortionTypes,
|
||||
// getItemValue: (item) => item.objectId,
|
||||
renderItem: (item) => Text(item.value),
|
||||
renderItem: (item) => item.value,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_mealPortionType = value;
|
||||
@ -373,12 +340,11 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
|
||||
),
|
||||
],
|
||||
),
|
||||
LabeledDropdownButton<Accuracy>(
|
||||
AutoCompleteDropdownButton<Accuracy>(
|
||||
selectedItem: _portionSizeAccuracy,
|
||||
label: 'Portion Size Accuracy',
|
||||
items: _portionSizeAccuracies,
|
||||
// getItemValue: (item) => item.objectId,
|
||||
renderItem: (item) => Text(item.value),
|
||||
renderItem: (item) => item.value,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_portionSizeAccuracy = value;
|
||||
@ -415,12 +381,11 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
|
||||
),
|
||||
],
|
||||
),
|
||||
LabeledDropdownButton<Accuracy>(
|
||||
AutoCompleteDropdownButton<Accuracy>(
|
||||
selectedItem: _carbsRatioAccuracy,
|
||||
label: 'Carbs Ratio Accuracy',
|
||||
items: _carbsRatioAccuracies,
|
||||
// getItemValue: (item) => item.objectId,
|
||||
renderItem: (item) => Text(item.value),
|
||||
renderItem: (item) => item.value,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_carbsRatioAccuracy = value;
|
||||
@ -461,7 +426,7 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
|
||||
),
|
||||
bottomNavigationBar: DetailBottomRow(
|
||||
onCancel: handleCancelAction,
|
||||
onSave: isSaving ? null : handleSaveAction,
|
||||
onSave: _isSaving ? null : handleSaveAction,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
@ -1,5 +1,4 @@
|
||||
import 'package:diameter/components/dialogs.dart';
|
||||
// import 'package:diameter/components/progress_indicator.dart';
|
||||
import 'package:diameter/config.dart';
|
||||
import 'package:diameter/models/meal.dart';
|
||||
import 'package:diameter/navigation.dart';
|
||||
@ -18,7 +17,13 @@ class MealListScreen extends StatefulWidget {
|
||||
class _MealListScreenState extends State<MealListScreen> {
|
||||
List<Meal> _meals = [];
|
||||
|
||||
void refresh({String? message}) {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
reload();
|
||||
}
|
||||
|
||||
void reload({String? message}) {
|
||||
setState(() {
|
||||
_meals = Meal.getAll();
|
||||
});
|
||||
@ -37,7 +42,7 @@ class _MealListScreenState extends State<MealListScreen> {
|
||||
|
||||
void onDelete(Meal meal) {
|
||||
Meal.remove(meal.id);
|
||||
refresh(message: 'Meal deleted');
|
||||
reload(message: 'Meal deleted');
|
||||
}
|
||||
|
||||
void handleDeleteAction(Meal meal) async {
|
||||
@ -52,17 +57,11 @@ class _MealListScreenState extends State<MealListScreen> {
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
refresh();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
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),
|
||||
body: Column(
|
||||
@ -81,9 +80,9 @@ class _MealListScreenState extends State<MealListScreen> {
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) =>
|
||||
MealDetailScreen(meal: meal),
|
||||
MealDetailScreen(id: meal.id),
|
||||
),
|
||||
).then((message) => refresh(message: message));
|
||||
).then((message) => reload(message: message));
|
||||
},
|
||||
title: Text(meal.value),
|
||||
subtitle: Text(meal.notes ?? ''),
|
||||
@ -112,7 +111,7 @@ class _MealListScreenState extends State<MealListScreen> {
|
||||
MaterialPageRoute(
|
||||
builder: (context) => const MealDetailScreen(),
|
||||
),
|
||||
).then((message) => refresh(message: message));
|
||||
).then((message) => reload(message: message));
|
||||
},
|
||||
child: const Icon(Icons.add),
|
||||
),
|
||||
|
@ -9,10 +9,9 @@ import 'package:diameter/models/meal_portion_type.dart';
|
||||
class MealPortionTypeDetailScreen extends StatefulWidget {
|
||||
static const String routeName = '/meal-portion-type';
|
||||
|
||||
final MealPortionType? mealPortionType;
|
||||
final int id;
|
||||
|
||||
const MealPortionTypeDetailScreen({Key? key, this.mealPortionType})
|
||||
: super(key: key);
|
||||
const MealPortionTypeDetailScreen({Key? key, this.id = 0}) : super(key: key);
|
||||
|
||||
@override
|
||||
_MealPortionTypeDetailScreenState createState() =>
|
||||
@ -21,53 +20,56 @@ class MealPortionTypeDetailScreen extends StatefulWidget {
|
||||
|
||||
class _MealPortionTypeDetailScreenState
|
||||
extends State<MealPortionTypeDetailScreen> {
|
||||
MealPortionType? _mealPortionType;
|
||||
bool _isNew = true;
|
||||
|
||||
final GlobalKey<FormState> _mealPortionTypeForm = GlobalKey<FormState>();
|
||||
|
||||
final _valueController = TextEditingController(text: '');
|
||||
final _notesController = TextEditingController(text: '');
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
if (widget.mealPortionType != null) {
|
||||
_valueController.text = widget.mealPortionType!.value;
|
||||
_notesController.text = widget.mealPortionType!.notes ?? '';
|
||||
reload();
|
||||
|
||||
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 {
|
||||
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(
|
||||
id: widget.mealPortionType?.id ?? 0,
|
||||
id: _mealPortionType?.id ?? 0,
|
||||
value: _valueController.text,
|
||||
notes: _notesController.text,
|
||||
));
|
||||
Navigator.pop(context, '${isNew ? 'New' : ''} Meal Portion Type saved');
|
||||
Navigator.pop(context, '${_isNew ? 'New' : ''} Meal Portion Type saved');
|
||||
}
|
||||
}
|
||||
|
||||
void handleCancelAction() {
|
||||
bool isNew = widget.mealPortionType == null;
|
||||
if (showConfirmationDialogOnCancel &&
|
||||
((isNew &&
|
||||
((_isNew &&
|
||||
(_valueController.text != '' || _notesController.text != '')) ||
|
||||
(!isNew &&
|
||||
(_valueController.text != widget.mealPortionType!.value ||
|
||||
(!_isNew &&
|
||||
(_valueController.text != _mealPortionType!.value ||
|
||||
_notesController.text !=
|
||||
(widget.mealPortionType!.notes ?? ''))))) {
|
||||
(_mealPortionType!.notes ?? ''))))) {
|
||||
Dialogs.showCancelConfirmationDialog(
|
||||
context: context,
|
||||
isNew: isNew,
|
||||
isNew: _isNew,
|
||||
onSave: handleSaveAction,
|
||||
);
|
||||
} else {
|
||||
@ -77,11 +79,11 @@ class _MealPortionTypeDetailScreenState
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
bool isNew = widget.mealPortionType == null;
|
||||
bool isNew = _mealPortionType == null;
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(
|
||||
isNew ? 'New Meal Portion Type' : widget.mealPortionType!.value),
|
||||
isNew ? 'New Meal Portion Type' : _mealPortionType!.value),
|
||||
),
|
||||
drawer: const Navigation(
|
||||
currentLocation: MealPortionTypeDetailScreen.routeName),
|
||||
|
@ -1,5 +1,4 @@
|
||||
import 'package:diameter/components/dialogs.dart';
|
||||
// import 'package:diameter/components/progress_indicator.dart';
|
||||
import 'package:diameter/config.dart';
|
||||
import 'package:diameter/navigation.dart';
|
||||
import 'package:diameter/screens/meal/meal_portion_type_detail.dart';
|
||||
@ -19,7 +18,13 @@ class MealPortionTypeListScreen extends StatefulWidget {
|
||||
class _MealPortionTypeListScreenState extends State<MealPortionTypeListScreen> {
|
||||
List<MealPortionType> _mealPortionTypes = [];
|
||||
|
||||
void refresh({String? message}) {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
reload();
|
||||
}
|
||||
|
||||
void reload({String? message}) {
|
||||
setState(() {
|
||||
_mealPortionTypes = MealPortionType.getAll();
|
||||
});
|
||||
@ -38,7 +43,7 @@ class _MealPortionTypeListScreenState extends State<MealPortionTypeListScreen> {
|
||||
|
||||
void onDelete(MealPortionType mealPortionType) {
|
||||
MealPortionType.remove(mealPortionType.id);
|
||||
refresh(message: 'Meal Portion Type deleted');
|
||||
reload(message: 'Meal Portion Type deleted');
|
||||
}
|
||||
|
||||
void handleDeleteAction(MealPortionType mealPortionType) async {
|
||||
@ -53,19 +58,13 @@ class _MealPortionTypeListScreenState extends State<MealPortionTypeListScreen> {
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
refresh();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Meal Portion Types'),
|
||||
actions: <Widget>[
|
||||
IconButton(onPressed: refresh, icon: const Icon(Icons.refresh))
|
||||
IconButton(onPressed: reload, icon: const Icon(Icons.refresh))
|
||||
],
|
||||
),
|
||||
drawer: const Navigation(
|
||||
@ -87,11 +86,11 @@ class _MealPortionTypeListScreenState extends State<MealPortionTypeListScreen> {
|
||||
MaterialPageRoute(
|
||||
builder: (context) =>
|
||||
MealPortionTypeDetailScreen(
|
||||
mealPortionType: mealPortionType,
|
||||
id: mealPortionType.id,
|
||||
),
|
||||
),
|
||||
).then(
|
||||
(message) => refresh(message: message));
|
||||
(message) => reload(message: message));
|
||||
},
|
||||
title: Text(mealPortionType.value),
|
||||
subtitle: Text(mealPortionType.notes ?? ''),
|
||||
@ -123,7 +122,7 @@ class _MealPortionTypeListScreenState extends State<MealPortionTypeListScreen> {
|
||||
MaterialPageRoute(
|
||||
builder: (context) => const MealPortionTypeDetailScreen(),
|
||||
),
|
||||
).then((message) => refresh(message: message));
|
||||
).then((message) => reload(message: message));
|
||||
},
|
||||
child: const Icon(Icons.add),
|
||||
),
|
||||
|
@ -1,28 +1,30 @@
|
||||
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/main.dart';
|
||||
import 'package:diameter/models/accuracy.dart';
|
||||
import 'package:diameter/models/meal_category.dart';
|
||||
import 'package:diameter/models/meal_portion_type.dart';
|
||||
import 'package:diameter/models/meal_source.dart';
|
||||
import 'package:diameter/navigation.dart';
|
||||
// import 'package:diameter/objectbox.g.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class MealSourceDetailScreen extends StatefulWidget {
|
||||
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
|
||||
_MealSourceDetailScreenState createState() => _MealSourceDetailScreenState();
|
||||
}
|
||||
|
||||
class _MealSourceDetailScreenState extends State<MealSourceDetailScreen> {
|
||||
MealSource? _mealSource;
|
||||
bool _isNew = true;
|
||||
|
||||
List<Accuracy> _portionSizeAccuracies = [];
|
||||
List<Accuracy> _carbsRatioAccuracies = [];
|
||||
List<MealCategory> _mealCategories = [];
|
||||
@ -41,52 +43,40 @@ class _MealSourceDetailScreenState extends State<MealSourceDetailScreen> {
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
reload();
|
||||
|
||||
_portionSizeAccuracies = Accuracy.getAllForPortionSize();
|
||||
_carbsRatioAccuracies = Accuracy.getAllForCarbsRatio();
|
||||
_mealCategories = MealCategory.getAll();
|
||||
_mealPortionTypes = MealPortionType.getAll();
|
||||
|
||||
if (widget.mealSource != null) {
|
||||
_valueController.text = widget.mealSource!.value;
|
||||
_notesController.text = widget.mealSource!.notes ?? '';
|
||||
if (_mealSource != null) {
|
||||
_valueController.text = _mealSource!.value;
|
||||
_notesController.text = _mealSource!.notes ?? '';
|
||||
|
||||
_defaultPortionSizeAccuracy =
|
||||
widget.mealSource!.defaultPortionSizeAccuracy.target;
|
||||
_defaultCarbsRatioAccuracy = widget.mealSource!.defaultCarbsRatioAccuracy.target;
|
||||
_mealSource!.defaultPortionSizeAccuracy.target;
|
||||
_defaultCarbsRatioAccuracy =
|
||||
_mealSource!.defaultCarbsRatioAccuracy.target;
|
||||
|
||||
_defaultMealCategory = widget.mealSource!.defaultMealCategory.target;
|
||||
_defaultMealCategory = _mealSource!.defaultMealCategory.target;
|
||||
_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 {
|
||||
bool isNew = widget.mealSource == null;
|
||||
if (_mealSourceForm.currentState!.validate()) {
|
||||
// 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,
|
||||
id: widget.id,
|
||||
value: _valueController.text,
|
||||
notes: _notesController.text,
|
||||
);
|
||||
@ -96,35 +86,33 @@ class _MealSourceDetailScreenState extends State<MealSourceDetailScreen> {
|
||||
mealSource.defaultMealCategory.target = _defaultMealCategory;
|
||||
mealSource.defaultMealPortionType.target = _defaultMealPortionType;
|
||||
MealSource.put(mealSource);
|
||||
Navigator.pop(context, '${isNew ? 'New' : ''} Meal Source saved');
|
||||
}
|
||||
Navigator.pop(context, '${_isNew ? 'New' : ''} Meal Source saved');
|
||||
}
|
||||
|
||||
void handleCancelAction() {
|
||||
bool isNew = widget.mealSource == null;
|
||||
if (showConfirmationDialogOnCancel &&
|
||||
((isNew &&
|
||||
((_isNew &&
|
||||
(_valueController.text != '' ||
|
||||
_defaultCarbsRatioAccuracy != null ||
|
||||
_defaultPortionSizeAccuracy != null ||
|
||||
_defaultMealCategory != null ||
|
||||
_defaultMealPortionType != null ||
|
||||
_notesController.text != '')) ||
|
||||
(!isNew &&
|
||||
(_valueController.text != widget.mealSource!.value ||
|
||||
(!_isNew &&
|
||||
(_valueController.text != _mealSource!.value ||
|
||||
_defaultCarbsRatioAccuracy !=
|
||||
widget.mealSource!.defaultCarbsRatioAccuracy.target ||
|
||||
_mealSource!.defaultCarbsRatioAccuracy.target ||
|
||||
_defaultPortionSizeAccuracy !=
|
||||
widget.mealSource!.defaultPortionSizeAccuracy.target ||
|
||||
_mealSource!.defaultPortionSizeAccuracy.target ||
|
||||
_defaultMealCategory !=
|
||||
widget.mealSource!.defaultMealCategory.target ||
|
||||
_mealSource!.defaultMealCategory.target ||
|
||||
_defaultMealPortionType !=
|
||||
widget.mealSource!.defaultMealPortionType.target ||
|
||||
_mealSource!.defaultMealPortionType.target ||
|
||||
_notesController.text !=
|
||||
(widget.mealSource!.notes ?? ''))))) {
|
||||
(_mealSource!.notes ?? ''))))) {
|
||||
Dialogs.showCancelConfirmationDialog(
|
||||
context: context,
|
||||
isNew: isNew,
|
||||
isNew: _isNew,
|
||||
onSave: handleSaveAction,
|
||||
);
|
||||
} else {
|
||||
@ -134,10 +122,9 @@ class _MealSourceDetailScreenState extends State<MealSourceDetailScreen> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
bool isNew = widget.mealSource == null;
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(isNew ? 'New Meal Source' : widget.mealSource!.value),
|
||||
title: Text(_isNew ? 'New Meal Source' : _mealSource!.value),
|
||||
),
|
||||
drawer:
|
||||
const Navigation(currentLocation: MealSourceDetailScreen.routeName),
|
||||
@ -160,70 +147,44 @@ class _MealSourceDetailScreenState extends State<MealSourceDetailScreen> {
|
||||
return null;
|
||||
},
|
||||
),
|
||||
LabeledDropdownButton<Accuracy>(
|
||||
AutoCompleteDropdownButton<Accuracy>(
|
||||
selectedItem: _defaultCarbsRatioAccuracy,
|
||||
label: 'Default Carbs Ratio Accuracy',
|
||||
items: _carbsRatioAccuracies,
|
||||
renderItem: (item) => Text(item.value),
|
||||
renderItem: (item) => item.value,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_defaultCarbsRatioAccuracy = value;
|
||||
});
|
||||
},
|
||||
),
|
||||
LabeledDropdownButton<Accuracy>(
|
||||
AutoCompleteDropdownButton<Accuracy>(
|
||||
selectedItem: _defaultPortionSizeAccuracy,
|
||||
label: 'Default Portion Size Accuracy',
|
||||
items: _portionSizeAccuracies,
|
||||
renderItem: (item) => Text(item.value),
|
||||
renderItem: (item) => item.value,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_defaultPortionSizeAccuracy = value;
|
||||
});
|
||||
},
|
||||
),
|
||||
// StyledFutureDropdownButton<Accuracy>(
|
||||
// selectedItem: _defaultCarbsRatioAccuracy,
|
||||
// label: 'Default Carbs Ratio Accuracy',
|
||||
// items: _carbsRatioAccuracies,
|
||||
// getItemValue: (item) => item.objectId,
|
||||
// renderItem: (item) => Text(item.value),
|
||||
// onChanged: (value) {
|
||||
// setState(() {
|
||||
// _defaultCarbsRatioAccuracy = value;
|
||||
// });
|
||||
// },
|
||||
// ),
|
||||
// StyledFutureDropdownButton<Accuracy>(
|
||||
// selectedItem: _defaultPortionSizeAccuracy,
|
||||
// label: 'Default Portion Size Accuracy',
|
||||
// items: _portionSizeAccuracies,
|
||||
// getItemValue: (item) => item.objectId,
|
||||
// renderItem: (item) => Text(item.value),
|
||||
// onChanged: (value) {
|
||||
// setState(() {
|
||||
// _defaultPortionSizeAccuracy = value;
|
||||
// });
|
||||
// },
|
||||
// ),
|
||||
LabeledDropdownButton<MealCategory>(
|
||||
AutoCompleteDropdownButton<MealCategory>(
|
||||
selectedItem: _defaultMealCategory,
|
||||
label: 'Default Meal Category',
|
||||
items: _mealCategories,
|
||||
// getItemValue: (item) => item.objectId,
|
||||
renderItem: (item) => Text(item.value),
|
||||
renderItem: (item) => item.value,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_defaultMealCategory = value;
|
||||
});
|
||||
},
|
||||
),
|
||||
LabeledDropdownButton<MealPortionType>(
|
||||
AutoCompleteDropdownButton<MealPortionType>(
|
||||
selectedItem: _defaultMealPortionType,
|
||||
label: 'Default Meal Portion Type',
|
||||
items: _mealPortionTypes,
|
||||
// getItemValue: (item) => item.objectId,
|
||||
renderItem: (item) => Text(item.value),
|
||||
renderItem: (item) => item.value,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_defaultMealPortionType = value;
|
||||
|
@ -1,4 +1,3 @@
|
||||
// import 'package:diameter/components/progress_indicator.dart';
|
||||
import 'package:diameter/components/dialogs.dart';
|
||||
import 'package:diameter/config.dart';
|
||||
import 'package:diameter/models/meal_source.dart';
|
||||
@ -18,7 +17,13 @@ class MealSourceListScreen extends StatefulWidget {
|
||||
class _MealSourceListScreenState extends State<MealSourceListScreen> {
|
||||
List<MealSource> _mealSources = [];
|
||||
|
||||
void refresh({String? message}) {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
reload();
|
||||
}
|
||||
|
||||
void reload({String? message}) {
|
||||
setState(() {
|
||||
_mealSources = MealSource.getAll();
|
||||
});
|
||||
@ -37,7 +42,7 @@ class _MealSourceListScreenState extends State<MealSourceListScreen> {
|
||||
|
||||
void onDelete(MealSource mealSource) {
|
||||
MealSource.remove(mealSource.id);
|
||||
refresh(message: 'Meal Source deleted');
|
||||
reload(message: 'Meal Source deleted');
|
||||
}
|
||||
|
||||
void handleDeleteAction(MealSource mealSource) async {
|
||||
@ -52,19 +57,13 @@ class _MealSourceListScreenState extends State<MealSourceListScreen> {
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
refresh();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Meal Sources'),
|
||||
actions: <Widget>[
|
||||
IconButton(onPressed: refresh, icon: const Icon(Icons.refresh))
|
||||
IconButton(onPressed: reload, icon: const Icon(Icons.refresh))
|
||||
],
|
||||
),
|
||||
drawer: const Navigation(currentLocation: MealSourceListScreen.routeName),
|
||||
@ -84,10 +83,10 @@ class _MealSourceListScreenState extends State<MealSourceListScreen> {
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => MealSourceDetailScreen(
|
||||
mealSource: mealSource,
|
||||
id: mealSource.id,
|
||||
),
|
||||
),
|
||||
).then((message) => refresh(message: message));
|
||||
).then((message) => reload(message: message));
|
||||
},
|
||||
title: Text(mealSource.value),
|
||||
subtitle: Text(mealSource.notes ?? ''),
|
||||
@ -120,7 +119,7 @@ class _MealSourceListScreenState extends State<MealSourceListScreen> {
|
||||
MaterialPageRoute(
|
||||
builder: (context) => const MealSourceDetailScreen(),
|
||||
),
|
||||
).then((message) => refresh(message: message));
|
||||
).then((message) => reload(message: message));
|
||||
},
|
||||
child: const Icon(Icons.add),
|
||||
),
|
||||
|
@ -1,4 +1,5 @@
|
||||
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/navigation.dart';
|
||||
@ -130,11 +131,11 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
FormWrapper(
|
||||
formState: _settingsForm,
|
||||
fields: [
|
||||
LabeledDropdownButton<NutritionMeasurement>(
|
||||
AutoCompleteDropdownButton<NutritionMeasurement>(
|
||||
selectedItem: nutritionMeasurement,
|
||||
label: 'Preferred Nutrition Measurement',
|
||||
items: NutritionMeasurement.values,
|
||||
renderItem: (item) => Text(item.toString().split('.')[1]),
|
||||
renderItem: (item) => item.toString().split('.')[1],
|
||||
onChanged: (value) {
|
||||
if (value != null) {
|
||||
Settings.setNutritionMeasurement(value);
|
||||
@ -144,11 +145,11 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
||||
}
|
||||
},
|
||||
),
|
||||
LabeledDropdownButton<GlucoseMeasurement>(
|
||||
AutoCompleteDropdownButton<GlucoseMeasurement>(
|
||||
selectedItem: glucoseMeasurement,
|
||||
label: 'Preferred Glucose Measurement',
|
||||
items: GlucoseMeasurement.values,
|
||||
renderItem: (item) => Text(item.toString().split('.')[1]),
|
||||
renderItem: (item) => item.toString().split('.')[1],
|
||||
onChanged: (value) {
|
||||
if (value != null) {
|
||||
Settings.setGlucoseMeasurement(value);
|
||||
|
Loading…
Reference in New Issue
Block a user