implement dropdown with autocomplete; add bolus tab to log entry

This commit is contained in:
spinel 2021-11-25 00:19:27 +01:00
parent ab0839bff3
commit 13507d14c9
32 changed files with 1248 additions and 766 deletions

71
TODO
View File

@ -1,37 +1,48 @@
General/Framework: MAIN TASKS:
☐ add active/deleted flag to all data models General/Framework:
☐ account for deleted/disabled elements in dropdowns ☐ add active/deleted flag to all data models
☐ place dropdown items right below their input ☐ find a general way to deal with duration fields
✔ use local database instead of back4app @done(21-11-07 18:53)
☐ find a general way to deal with duration fields
☐ add explanations to each section
☐ use ids instead of passing entities around where possible
Accuracies: Accuracies:
☐ implement reordering ☐ implement reordering
Basal/Bolus: Basal/Bolus:
☐ create distinct visual mode for picking the active profile ☐ create distinct visual mode for picking the active profile
Meal: Log Overview:
none ☐ 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 Overview: Log Entry:
☐ add active events view ☐ replace meal and glucose boli with logbolus entities
☐ adjust/debug active events view ☐ fix logmeals/logboli/logevents (probably use queries for associated items instead of onetomany)
☐ display more information
☐ apply target color settings to glucose
☐ total bolus and carbs per entry
Log Entry: Event Types:
☐ add tab for bolus overview ☐ add option to change bolus/basal profile for event duration
☐ calculate bolus suggestions according to active profile
☐ add time picker for entry date/time
Event Types: Settings:
☐ add option to change bolus/basal profile for event duration ☐ add objectbox class and use instead of shared preferences
☐ add fields for date and time formats
☐ add fields for glucose target
Settings: FUTURE TASKS:
☐ add objectbox class and use instead of shared preferences General/Framework:
☐ add fields for date and time formats ☐ add explanations to each section
☐ add fields for glucose target ☐ 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)

View File

@ -5,6 +5,7 @@ class AppTheme {
AppTheme._(); AppTheme._();
static ThemeData lightTheme = FlexColorScheme.light( static ThemeData lightTheme = FlexColorScheme.light(
surfaceStyle: FlexSurface.medium,
scheme: FlexScheme.mandyRed, scheme: FlexScheme.mandyRed,
fontFamily: 'RobotoCondensed', fontFamily: 'RobotoCondensed',
).toTheme; ).toTheme;
@ -16,8 +17,7 @@ class AppTheme {
static ThemeData makeTheme(ThemeData baseThemeData) { static ThemeData makeTheme(ThemeData baseThemeData) {
return baseThemeData.copyWith( return baseThemeData.copyWith(
visualDensity: VisualDensity.compact, bottomNavigationBarTheme: BottomNavigationBarThemeData(
bottomNavigationBarTheme: BottomNavigationBarThemeData( backgroundColor: baseThemeData.primaryColor));
backgroundColor: baseThemeData.primaryColor));
} }
} }

View 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),
),
),
),
);
}
}

View File

@ -65,7 +65,7 @@ class BooleanFormField extends StatefulWidget {
class _BooleanFormFieldState extends State<BooleanFormField> { class _BooleanFormFieldState extends State<BooleanFormField> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return FormField<bool>(builder: (context) { return FormField<bool>(builder: (state) {
return ListTile( return ListTile(
onTap: () => widget.onChanged(!widget.value), onTap: () => widget.onChanged(!widget.value),
trailing: Switch( trailing: Switch(
@ -98,8 +98,7 @@ class DateTimeFormField extends StatefulWidget {
: super(key: key); : super(key: key);
@override @override
_DateTimeFormFieldState createState() => _DateTimeFormFieldState createState() => _DateTimeFormFieldState();
_DateTimeFormFieldState();
} }
class _DateTimeFormFieldState extends State<DateTimeFormField> { class _DateTimeFormFieldState extends State<DateTimeFormField> {
@ -115,7 +114,7 @@ class _DateTimeFormFieldState extends State<DateTimeFormField> {
final newTime = await showDatePicker( final newTime = await showDatePicker(
context: context, context: context,
initialDate: widget.date, initialDate: widget.date,
firstDate: widget.minDate ?? DateTime(2000,1,1), firstDate: widget.minDate ?? DateTime(2000, 1, 1),
lastDate: widget.maxDate ?? DateTime.now(), lastDate: widget.maxDate ?? DateTime.now(),
); );
widget.onChanged(newTime); widget.onChanged(newTime);
@ -139,8 +138,7 @@ class TimeOfDayFormField extends StatefulWidget {
: super(key: key); : super(key: key);
@override @override
_TimeOfDayFormFieldState createState() => _TimeOfDayFormFieldState createState() => _TimeOfDayFormFieldState();
_TimeOfDayFormFieldState();
} }
class _TimeOfDayFormFieldState extends State<TimeOfDayFormField> { class _TimeOfDayFormFieldState extends State<TimeOfDayFormField> {
@ -163,42 +161,3 @@ class _TimeOfDayFormFieldState extends State<TimeOfDayFormField> {
} }
} }
class LabeledDropdownButton<T> extends StatefulWidget {
final String label;
final T? selectedItem;
final List<T> items;
final Widget Function(T item) renderItem;
final void Function(T? value) onChanged;
const LabeledDropdownButton(
{Key? key,
this.selectedItem,
required this.label,
required this.items,
required this.renderItem,
required this.onChanged})
: super(key: key);
@override
_LabeledDropdownButtonState<T> createState() => _LabeledDropdownButtonState();
}
class _LabeledDropdownButtonState<T> extends State<LabeledDropdownButton<T>> {
@override
Widget build(BuildContext context) {
return DropdownButtonFormField<T>(
decoration: InputDecoration(
labelText: widget.label,
),
value: widget.selectedItem,
onChanged: widget.onChanged,
items: widget.items
.map((item) => DropdownMenuItem<T>(
value: item,
child: widget.renderItem(item),
))
.toList(),
);
}
}

View File

@ -4,8 +4,8 @@ import 'package:diameter/screens/accuracy_detail.dart';
import 'package:diameter/screens/basal/basal_profile_detail.dart'; import 'package:diameter/screens/basal/basal_profile_detail.dart';
import 'package:diameter/screens/bolus/bolus_profile_detail.dart'; import 'package:diameter/screens/bolus/bolus_profile_detail.dart';
import 'package:diameter/screens/log/log.dart'; import 'package:diameter/screens/log/log.dart';
import 'package:diameter/screens/log/log_entry.dart'; import 'package:diameter/screens/log/log_entry/log_entry.dart';
import 'package:diameter/screens/log/log_event_detail.dart'; import 'package:diameter/screens/log/log_entry/log_event_detail.dart';
import 'package:diameter/screens/log/log_event_type_detail.dart'; import 'package:diameter/screens/log/log_event_type_detail.dart';
import 'package:diameter/screens/log/log_event_type_list.dart'; import 'package:diameter/screens/log/log_event_type_list.dart';
import 'package:diameter/screens/meal/meal_category_detail.dart'; import 'package:diameter/screens/meal/meal_category_detail.dart';

View File

@ -1,6 +1,8 @@
import 'package:diameter/main.dart'; import 'package:diameter/main.dart';
import 'package:diameter/models/bolus_profile.dart'; import 'package:diameter/models/bolus_profile.dart';
import 'package:diameter/objectbox.g.dart'; import 'package:diameter/objectbox.g.dart';
import 'package:diameter/utils/date_time_utils.dart';
import 'package:flutter/material.dart';
@Entity(uid: 3417770529060202389) @Entity(uid: 3417770529060202389)
class Bolus { class Bolus {
@ -37,4 +39,22 @@ class Bolus {
builder.link(Bolus_.bolusProfile, BolusProfile_.id.equals(id)); builder.link(Bolus_.bolusProfile, BolusProfile_.id.equals(id));
return builder.build().find(); return builder.build().find();
} }
static Bolus? getRateForTime(DateTime? dateTime) {
if (dateTime != null) {
// ignore: todo
// TODO: check if an event is active that would change the active profile
final bolusProfile = BolusProfile.getActive();
final time = DateTimeUtils.convertTimeOfDayToDateTime(TimeOfDay.fromDateTime(dateTime));
if (bolusProfile != null) {
final rates = Bolus.getAllForProfile(bolusProfile.id);
final result = rates.where((rate) =>
(time.isAfter(rate.startTime) ||
time.isAtSameMomentAs(rate.startTime)) &&
time.isBefore(rate.endTime));
return result.length != 1 ? null : result.single;
}
return null;
}
}
} }

View File

@ -34,4 +34,11 @@ class BolusProfile {
return element; return element;
}).toList()); }).toList());
} }
static BolusProfile? getActive() {
Query<BolusProfile> query =
box.query(BolusProfile_.active.equals(true)).build();
final result = query.find();
return result.length != 1 ? null : result.single;
}
} }

38
lib/models/log_bolus.dart Normal file
View 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);
}

View File

@ -1,4 +1,5 @@
import 'package:diameter/main.dart'; import 'package:diameter/main.dart';
import 'package:diameter/models/log_bolus.dart';
import 'package:diameter/models/log_event.dart'; import 'package:diameter/models/log_event.dart';
import 'package:diameter/models/log_meal.dart'; import 'package:diameter/models/log_meal.dart';
import 'package:diameter/objectbox.g.dart'; import 'package:diameter/objectbox.g.dart';
@ -28,6 +29,9 @@ class LogEntry {
@Backlink('logEntry') @Backlink('logEntry')
final meals = ToMany<LogMeal>(); final meals = ToMany<LogMeal>();
@Backlink('logEntry')
final boli = ToMany<LogBolus>();
LogEntry({ LogEntry({
this.id = 0, this.id = 0,
required this.time, required this.time,
@ -47,7 +51,8 @@ class LogEntry {
static Map<DateTime, List<LogEntry>> getDailyEntryMap() { static Map<DateTime, List<LogEntry>> getDailyEntryMap() {
Map<DateTime, List<LogEntry>> dateMap = <DateTime, List<LogEntry>>{}; Map<DateTime, List<LogEntry>> dateMap = <DateTime, List<LogEntry>>{};
QueryBuilder<LogEntry> allByDate = box.query()..order(LogEntry_.time, flags: Order.descending); QueryBuilder<LogEntry> allByDate = box.query()
..order(LogEntry_.time, flags: Order.descending);
List<LogEntry> entries = allByDate.build().find(); List<LogEntry> entries = allByDate.build().find();
DateTime? date; DateTime? date;

View File

@ -7,11 +7,11 @@ import 'package:diameter/screens/bolus/bolus_detail.dart';
import 'package:diameter/screens/bolus/bolus_profile_detail.dart'; import 'package:diameter/screens/bolus/bolus_profile_detail.dart';
import 'package:diameter/screens/bolus/bolus_profile_list.dart'; import 'package:diameter/screens/bolus/bolus_profile_list.dart';
import 'package:diameter/screens/log/log.dart'; import 'package:diameter/screens/log/log.dart';
import 'package:diameter/screens/log/log_entry.dart'; import 'package:diameter/screens/log/log_entry/log_entry.dart';
import 'package:diameter/screens/log/log_event_detail.dart'; import 'package:diameter/screens/log/log_entry/log_event_detail.dart';
import 'package:diameter/screens/log/log_event_type_detail.dart'; import 'package:diameter/screens/log/log_event_type_detail.dart';
import 'package:diameter/screens/log/log_event_type_list.dart'; import 'package:diameter/screens/log/log_event_type_list.dart';
import 'package:diameter/screens/log/log_meal_detail.dart'; import 'package:diameter/screens/log/log_entry/log_meal_detail.dart';
import 'package:diameter/screens/meal/meal_category_detail.dart'; import 'package:diameter/screens/meal/meal_category_detail.dart';
import 'package:diameter/screens/meal/meal_category_list.dart'; import 'package:diameter/screens/meal/meal_category_list.dart';
import 'package:diameter/screens/meal/meal_detail.dart'; import 'package:diameter/screens/meal/meal_detail.dart';

View File

@ -2,7 +2,6 @@ import 'package:diameter/components/dialogs.dart';
import 'package:diameter/config.dart'; import 'package:diameter/config.dart';
import 'package:diameter/navigation.dart'; import 'package:diameter/navigation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
// import 'package:diameter/components/progress_indicator.dart';
import 'package:diameter/models/basal_profile.dart'; import 'package:diameter/models/basal_profile.dart';
import 'package:diameter/screens/basal/basal_profile_detail.dart'; import 'package:diameter/screens/basal/basal_profile_detail.dart';
@ -24,7 +23,6 @@ class _BasalProfileListScreenState extends State<BasalProfileListScreen> {
pickActiveProfileMode = false; pickActiveProfileMode = false;
_basalProfiles = BasalProfile.getAll(); _basalProfiles = BasalProfile.getAll();
}); });
// _basalProfiles.then((list) =>
updateBanner(); updateBanner();
setState(() { setState(() {
if (message != null) { if (message != null) {
@ -94,7 +92,6 @@ class _BasalProfileListScreenState extends State<BasalProfileListScreen> {
BasalProfile.setAllInactive; BasalProfile.setAllInactive;
basalProfile.active = true; basalProfile.active = true;
BasalProfile.put(basalProfile); BasalProfile.put(basalProfile);
// (exception: basalProfile.objectId!).then((_) =>
refresh(message: '${basalProfile.name} has been set as your active Profile'); refresh(message: '${basalProfile.name} has been set as your active Profile');
} }

View File

@ -2,20 +2,17 @@ import 'package:diameter/components/dialogs.dart';
import 'package:diameter/config.dart'; import 'package:diameter/config.dart';
import 'package:diameter/models/log_entry.dart'; import 'package:diameter/models/log_entry.dart';
import 'package:diameter/models/log_event.dart'; import 'package:diameter/models/log_event.dart';
// import 'package:diameter/models/log_event_type.dart';
import 'package:diameter/screens/log/log_event_detail.dart';
import 'package:diameter/utils/date_time_utils.dart'; import 'package:diameter/utils/date_time_utils.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
// import 'package:diameter/components/progress_indicator.dart';
class ActiveLogEventListScreen extends StatefulWidget { class ActiveLogEventListScreen extends StatefulWidget {
static const String routeName = '/active-log-events'; static const String routeName = '/active-log-events';
final LogEntry? endLogEntry; final int endLogEntryId;
final Function()? onSetEndTime; final Function()? onSetEndTime;
const ActiveLogEventListScreen( const ActiveLogEventListScreen(
{Key? key, this.endLogEntry, this.onSetEndTime}) {Key? key, this.endLogEntryId = 0, this.onSetEndTime})
: super(key: key); : super(key: key);
@override @override
@ -26,7 +23,13 @@ class ActiveLogEventListScreen extends StatefulWidget {
class _ActiveLogEventListScreenState extends State<ActiveLogEventListScreen> { class _ActiveLogEventListScreenState extends State<ActiveLogEventListScreen> {
List<LogEvent> _activeLogEvents = []; List<LogEvent> _activeLogEvents = [];
void refresh({String? message}) { @override
void initState() {
super.initState();
reload();
}
void reload({String? message}) {
setState(() { setState(() {
_activeLogEvents = LogEvent.getAllOngoing(); _activeLogEvents = LogEvent.getAllOngoing();
}); });
@ -47,9 +50,9 @@ class _ActiveLogEventListScreenState extends State<ActiveLogEventListScreen> {
void onStop(LogEvent event) async { void onStop(LogEvent event) async {
event.endTime = DateTime.now(); event.endTime = DateTime.now();
event.endLogEntry.target = event.endLogEntry.target =
widget.endLogEntry ?? LogEntry(time: DateTime.now()); LogEntry.get(widget.endLogEntryId) ?? LogEntry(time: DateTime.now());
LogEvent.put(event); LogEvent.put(event);
refresh(); reload();
if (widget.onSetEndTime != null) { if (widget.onSetEndTime != null) {
widget.onSetEndTime!(); widget.onSetEndTime!();
} }
@ -69,7 +72,7 @@ class _ActiveLogEventListScreenState extends State<ActiveLogEventListScreen> {
void onDelete(LogEvent event) { void onDelete(LogEvent event) {
LogEvent.remove(event.id); LogEvent.remove(event.id);
refresh(message: 'Event deleted'); reload(message: 'Event deleted');
} }
void handleDeleteAction(LogEvent event) async { void handleDeleteAction(LogEvent event) async {
@ -84,11 +87,6 @@ class _ActiveLogEventListScreenState extends State<ActiveLogEventListScreen> {
} }
} }
@override
void initState() {
super.initState();
refresh();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -101,20 +99,7 @@ class _ActiveLogEventListScreenState extends State<ActiveLogEventListScreen> {
primary: false, primary: false,
automaticallyImplyLeading: false, automaticallyImplyLeading: false,
actions: [ actions: [
IconButton( IconButton(icon: const Icon(Icons.refresh), onPressed: reload),
icon: const Icon(Icons.add),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => LogEventDetailScreen(
logEntry: widget.endLogEntry!,
),
),
).then((message) => refresh(message: message));
},
),
IconButton(icon: const Icon(Icons.refresh), onPressed: refresh),
], ],
), ),
_activeLogEvents.isNotEmpty ? _activeLogEvents.isNotEmpty ?

View File

@ -2,7 +2,7 @@ import 'package:diameter/components/dialogs.dart';
import 'package:diameter/config.dart'; import 'package:diameter/config.dart';
import 'package:diameter/models/log_entry.dart'; import 'package:diameter/models/log_entry.dart';
import 'package:diameter/navigation.dart'; import 'package:diameter/navigation.dart';
import 'package:diameter/screens/log/log_entry.dart'; import 'package:diameter/screens/log/log_entry/log_entry.dart';
import 'package:diameter/utils/date_time_utils.dart'; import 'package:diameter/utils/date_time_utils.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -17,7 +17,13 @@ class LogScreen extends StatefulWidget {
class _LogScreenState extends State<LogScreen> { class _LogScreenState extends State<LogScreen> {
late Map<DateTime, List<LogEntry>> _logEntryDailyMap; late Map<DateTime, List<LogEntry>> _logEntryDailyMap;
void refresh({String? message}) { @override
void initState() {
super.initState();
reload();
}
void reload({String? message}) {
setState(() { setState(() {
_logEntryDailyMap = LogEntry.getDailyEntryMap(); _logEntryDailyMap = LogEntry.getDailyEntryMap();
}); });
@ -36,7 +42,7 @@ class _LogScreenState extends State<LogScreen> {
void onDelete(LogEntry logEntry) { void onDelete(LogEntry logEntry) {
LogEntry.remove(logEntry.id); LogEntry.remove(logEntry.id);
refresh(message: 'Log Entry deleted'); reload(message: 'Log Entry deleted');
} }
void handleDeleteAction(LogEntry logEntry) async { void handleDeleteAction(LogEntry logEntry) async {
@ -51,12 +57,6 @@ class _LogScreenState extends State<LogScreen> {
} }
} }
@override
void initState() {
super.initState();
refresh();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
@ -64,7 +64,7 @@ class _LogScreenState extends State<LogScreen> {
title: const Text('Log Entries'), title: const Text('Log Entries'),
actions: <Widget>[ actions: <Widget>[
IconButton( IconButton(
onPressed: refresh, onPressed: reload,
icon: const Icon(Icons.refresh) icon: const Icon(Icons.refresh)
), ),
], ],
@ -101,7 +101,7 @@ class _LogScreenState extends State<LogScreen> {
LogEntryScreen( LogEntryScreen(
id: logEntry.id), id: logEntry.id),
), ),
).then((message) => refresh( ).then((message) => reload(
message: message)); message: message));
}, },
title: Text( title: Text(
@ -150,7 +150,7 @@ class _LogScreenState extends State<LogScreen> {
MaterialPageRoute( MaterialPageRoute(
builder: (context) => const LogEntryScreen(), builder: (context) => const LogEntryScreen(),
), ),
).then((message) => refresh(message: message)); ).then((message) => reload(message: message));
}, },
child: const Icon(Icons.add), child: const Icon(Icons.add),
), ),

View 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,
),
);
}
}

View 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!'),
),
),
],
);
}
}

View File

@ -2,12 +2,17 @@ import 'package:diameter/components/detail.dart';
import 'package:diameter/components/dialogs.dart'; import 'package:diameter/components/dialogs.dart';
import 'package:diameter/components/forms.dart'; import 'package:diameter/components/forms.dart';
import 'package:diameter/config.dart'; import 'package:diameter/config.dart';
import 'package:diameter/models/log_bolus.dart';
import 'package:diameter/models/log_entry.dart'; import 'package:diameter/models/log_entry.dart';
import 'package:diameter/models/log_event.dart';
import 'package:diameter/models/log_meal.dart';
import 'package:diameter/navigation.dart'; import 'package:diameter/navigation.dart';
import 'package:diameter/screens/log/log_event_detail.dart'; import 'package:diameter/screens/log/log_entry/log_bolus_detail.dart';
import 'package:diameter/screens/log/log_event_list.dart'; import 'package:diameter/screens/log/log_entry/log_bolus_list.dart';
import 'package:diameter/screens/log/log_meal_detail.dart'; import 'package:diameter/screens/log/log_entry/log_event_list.dart';
import 'package:diameter/screens/log/log_meal_list.dart'; import 'package:diameter/screens/log/log_entry/log_event_detail.dart';
import 'package:diameter/screens/log/log_entry/log_meal_detail.dart';
import 'package:diameter/screens/log/log_entry/log_meal_list.dart';
import 'package:diameter/settings.dart'; import 'package:diameter/settings.dart';
import 'package:diameter/utils/date_time_utils.dart'; import 'package:diameter/utils/date_time_utils.dart';
import 'package:diameter/utils/utils.dart'; import 'package:diameter/utils/utils.dart';
@ -25,6 +30,9 @@ class LogEntryScreen extends StatefulWidget {
class _LogEntryScreenState extends State<LogEntryScreen> { class _LogEntryScreenState extends State<LogEntryScreen> {
LogEntry? _logEntry; LogEntry? _logEntry;
List<LogMeal> _logMeals = [];
List<LogEvent> _logEvents = [];
List<LogBolus> _logBoli = [];
bool _isNew = true; bool _isNew = true;
bool _isSaving = false; bool _isSaving = false;
@ -42,6 +50,7 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
final _notesController = TextEditingController(text: ''); final _notesController = TextEditingController(text: '');
late FloatingActionButton addMealButton; late FloatingActionButton addMealButton;
late FloatingActionButton addBolusButton;
late FloatingActionButton addEventButton; late FloatingActionButton addEventButton;
late IconButton refreshButton; late IconButton refreshButton;
late IconButton closeButton; late IconButton closeButton;
@ -62,6 +71,11 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
child: const Icon(Icons.add), child: const Icon(Icons.add),
); );
addBolusButton = FloatingActionButton(
onPressed: handleAddNewBolus,
child: const Icon(Icons.add),
);
addEventButton = FloatingActionButton( addEventButton = FloatingActionButton(
onPressed: handleAddNewEvent, onPressed: handleAddNewEvent,
child: const Icon(Icons.add), child: const Icon(Icons.add),
@ -105,6 +119,9 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
if (widget.id != 0) { if (widget.id != 0) {
setState(() { setState(() {
_logEntry = LogEntry.get(widget.id); _logEntry = LogEntry.get(widget.id);
_logMeals = _logEntry?.meals ?? [];
_logEvents = _logEntry?.events ?? [];
_logBoli = _logEntry?.boli ?? [];
}); });
_isNew = _logEntry == null; _isNew = _logEntry == null;
} }
@ -216,7 +233,18 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) { builder: (context) {
return LogMealDetailScreen(logEntry: _logEntry!); return LogMealDetailScreen(logEntryId: _logEntry!.id);
},
),
).then((message) => reload(message: message));
}
void handleAddNewBolus() async {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) {
return LogBolusDetailScreen(logEntryId: _logEntry!.id);
}, },
), ),
).then((message) => reload(message: message)); ).then((message) => reload(message: message));
@ -227,7 +255,7 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) { builder: (context) {
return LogEventDetailScreen(logEntry: _logEntry!); return LogEventDetailScreen(logEntryId: _logEntry!.id);
}, },
), ),
).then((message) => reload(message: message)); ).then((message) => reload(message: message));
@ -243,6 +271,11 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
bottomNav = null; bottomNav = null;
break; break;
case 2: case 2:
actionButton = addBolusButton;
appBarActions = [refreshButton, closeButton];
bottomNav = null;
break;
case 3:
actionButton = addEventButton; actionButton = addEventButton;
appBarActions = [refreshButton, closeButton]; appBarActions = [refreshButton, closeButton];
bottomNav = null; bottomNav = null;
@ -259,7 +292,7 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return DefaultTabController( return DefaultTabController(
length: _isNew ? 1 : 3, length: _isNew ? 1 : 4,
child: Builder(builder: (BuildContext context) { child: Builder(builder: (BuildContext context) {
final TabController tabController = DefaultTabController.of(context)!; final TabController tabController = DefaultTabController.of(context)!;
tabController.addListener(() { tabController.addListener(() {
@ -452,8 +485,12 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
]; ];
if (!_isNew) { if (!_isNew) {
tabs.add(LogMealListScreen(logEntry: _logEntry!, reload: reload)); tabs.add(LogMealListScreen(
tabs.add(LogEventListScreen(logEntry: _logEntry!, reload: reload)); logEntry: _logEntry!, logMeals: _logMeals, reload: reload));
tabs.add(LogBolusListScreen(
logEntry: _logEntry!, logBoli: _logBoli, reload: reload));
tabs.add(LogEventListScreen(
logEntry: _logEntry!, logEvents: _logEvents, reload: reload));
} }
return Scaffold( return Scaffold(
@ -465,6 +502,7 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
tabs: [ tabs: [
Tab(text: 'GENERAL'), Tab(text: 'GENERAL'),
Tab(text: 'MEALS'), Tab(text: 'MEALS'),
Tab(text: 'BOLI'),
Tab(text: 'EVENTS'), Tab(text: 'EVENTS'),
], ],
), ),

View File

@ -1,5 +1,6 @@
import 'package:diameter/components/detail.dart'; import 'package:diameter/components/detail.dart';
import 'package:diameter/components/dialogs.dart'; import 'package:diameter/components/dialogs.dart';
import 'package:diameter/components/dropdown.dart';
import 'package:diameter/components/forms.dart'; import 'package:diameter/components/forms.dart';
import 'package:diameter/config.dart'; import 'package:diameter/config.dart';
import 'package:diameter/models/log_entry.dart'; import 'package:diameter/models/log_entry.dart';
@ -10,11 +11,13 @@ import 'package:flutter/material.dart';
class LogEventDetailScreen extends StatefulWidget { class LogEventDetailScreen extends StatefulWidget {
static const String routeName = '/log-event'; static const String routeName = '/log-event';
final LogEntry? logEntry;
final LogEntry? endLogEntry; final int logEntryId;
final LogEvent? logEvent; final int endLogEntryId;
final int id;
const LogEventDetailScreen( const LogEventDetailScreen(
{Key? key, this.logEntry, this.endLogEntry, this.logEvent}) {Key? key, this.logEntryId = 0, this.endLogEntryId = 0, this.id = 0})
: super(key: key); : super(key: key);
@override @override
@ -22,59 +25,55 @@ class LogEventDetailScreen extends StatefulWidget {
} }
class _LogEventDetailScreenState extends State<LogEventDetailScreen> { class _LogEventDetailScreenState extends State<LogEventDetailScreen> {
LogEvent? _logEvent;
bool _isNew = true;
bool _isSaving = false;
final GlobalKey<FormState> _logEventForm = GlobalKey<FormState>(); final GlobalKey<FormState> _logEventForm = GlobalKey<FormState>();
final _notesController = TextEditingController(text: '');
LogEventType? _eventType; LogEventType? _eventType;
bool _hasEndTime = false; bool _hasEndTime = false;
final _notesController = TextEditingController(text: '');
List<LogEventType> _logEventTypes = []; List<LogEventType> _logEventTypes = [];
bool _isSaving = false;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
reload();
if (widget.logEvent != null) { if (_logEvent != null) {
_notesController.text = widget.logEvent!.notes ?? ''; _notesController.text = _logEvent!.notes ?? '';
_eventType = widget.logEvent!.eventType.target; _eventType = _logEvent!.eventType.target;
_hasEndTime = widget.logEvent!.hasEndTime; _hasEndTime = _logEvent!.hasEndTime;
} }
_logEventTypes = LogEventType.getAll(); _logEventTypes = LogEventType.getAll();
} }
void reload() {
if (widget.id != 0) {
setState(() {
_logEvent = LogEvent.get(widget.id);
});
}
_isNew = _logEvent == null;
}
void handleSaveAction() async { void handleSaveAction() async {
setState(() { setState(() {
_isSaving = true; _isSaving = true;
}); });
if (_logEventForm.currentState!.validate()) { if (_logEventForm.currentState!.validate()) {
bool isNew = widget.logEvent == null;
// isNew
// ? await LogEvent.save(
// logEntry: widget.logEntry!.objectId!,
// eventType: _eventType!,
// time: widget.logEntry!.time,
// hasEndTime: _hasEndTime,
// notes: _notesController.text,
// )
// : await LogEvent.update(
// widget.logEvent!.objectId!,
// eventType: _eventType!,
// time: widget.logEntry!.time,
// hasEndTime: _hasEndTime,
// notes: _notesController.text,
// );
LogEvent event = LogEvent( LogEvent event = LogEvent(
id: widget.logEvent?.id ?? 0, id: widget.id,
time: widget.logEntry!.time, time: LogEntry.get(widget.logEntryId)!.time,
hasEndTime: _hasEndTime, hasEndTime: _hasEndTime,
notes: _notesController.text, notes: _notesController.text,
); );
event.eventType.target = _eventType; event.eventType.target = _eventType;
LogEvent.put(event); LogEvent.put(event);
Navigator.pop(context, '${isNew ? 'New' : ''} Event Saved'); Navigator.pop(context, '${_isNew ? 'New' : ''} Event Saved');
} }
setState(() { setState(() {
_isSaving = false; _isSaving = false;
@ -82,19 +81,18 @@ class _LogEventDetailScreenState extends State<LogEventDetailScreen> {
} }
void handleCancelAction() { void handleCancelAction() {
bool isNew = widget.logEvent == null;
if (showConfirmationDialogOnCancel && if (showConfirmationDialogOnCancel &&
((isNew && ((_isNew &&
(_notesController.text != '' || (_notesController.text != '' ||
_eventType != null || _eventType != null ||
_hasEndTime)) || _hasEndTime)) ||
(!isNew && (!_isNew &&
(_notesController.text != (widget.logEvent!.notes ?? '') || (_notesController.text != (_logEvent!.notes ?? '') ||
_eventType != widget.logEvent!.eventType.target || _eventType != _logEvent!.eventType.target ||
_hasEndTime != widget.logEvent!.hasEndTime)))) { _hasEndTime != _logEvent!.hasEndTime)))) {
Dialogs.showCancelConfirmationDialog( Dialogs.showCancelConfirmationDialog(
context: context, context: context,
isNew: isNew, isNew: _isNew,
onSave: handleSaveAction, onSave: handleSaveAction,
); );
} else { } else {
@ -104,10 +102,9 @@ class _LogEventDetailScreenState extends State<LogEventDetailScreen> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
bool isNew = widget.logEvent == null;
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text(isNew ? 'New Event' : 'Edit Event'), title: Text(_isNew ? 'New Event' : 'Edit Event'),
), ),
drawer: const Navigation(currentLocation: LogEventDetailScreen.routeName), drawer: const Navigation(currentLocation: LogEventDetailScreen.routeName),
body: SingleChildScrollView( body: SingleChildScrollView(
@ -117,11 +114,11 @@ class _LogEventDetailScreenState extends State<LogEventDetailScreen> {
FormWrapper( FormWrapper(
formState: _logEventForm, formState: _logEventForm,
fields: [ fields: [
LabeledDropdownButton<LogEventType>( AutoCompleteDropdownButton<LogEventType>(
selectedItem: _eventType, selectedItem: _eventType,
label: 'Event Type', label: 'Event Type',
items: _logEventTypes, items: _logEventTypes,
renderItem: (item) => Text(item.value), renderItem: (item) => item.value,
onChanged: (value) { onChanged: (value) {
setState(() { setState(() {
_eventType = value; _eventType = value;
@ -147,7 +144,6 @@ class _LogEventDetailScreenState extends State<LogEventDetailScreen> {
), ),
], ],
), ),
// ActiveLogEventListScreen(onSetEndTime: onSetEndTime)
], ],
), ),
), ),

View File

@ -2,15 +2,17 @@ import 'package:diameter/components/dialogs.dart';
import 'package:diameter/config.dart'; import 'package:diameter/config.dart';
import 'package:diameter/models/log_entry.dart'; import 'package:diameter/models/log_entry.dart';
import 'package:diameter/models/log_event.dart'; import 'package:diameter/models/log_event.dart';
import 'package:diameter/screens/log/log_event_detail.dart'; import 'package:diameter/screens/log/log_entry/log_event_detail.dart';
import 'package:diameter/utils/date_time_utils.dart'; import 'package:diameter/utils/date_time_utils.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class LogEventListScreen extends StatefulWidget { class LogEventListScreen extends StatefulWidget {
final LogEntry logEntry; final LogEntry logEntry;
final List<LogEvent> logEvents;
final Function() reload; final Function() reload;
const LogEventListScreen({Key? key, required this.logEntry, required this.reload}) const LogEventListScreen(
{Key? key, required this.logEntry, this.logEvents = const [], required this.reload})
: super(key: key); : super(key: key);
@override @override
@ -39,8 +41,8 @@ class _LogEventListScreenState extends State<LogEventListScreen> {
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => LogEventDetailScreen( builder: (context) => LogEventDetailScreen(
endLogEntry: widget.logEntry, logEntryId: widget.logEntry.id,
logEvent: event, id: event.id,
), ),
), ),
).then((message) => reload(message: message)); ).then((message) => reload(message: message));
@ -66,15 +68,18 @@ class _LogEventListScreenState extends State<LogEventListScreen> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
children: [ children: [
Expanded( Expanded(
child: (widget.logEntry.events.isNotEmpty || widget.logEntry.endedEvents.isNotEmpty) child: (widget.logEntry.events.isNotEmpty ||
widget.logEntry.endedEvents.isNotEmpty)
? ListView.builder( ? ListView.builder(
shrinkWrap: true, shrinkWrap: true,
itemCount: widget.logEntry.events.length + widget.logEntry.endedEvents.length, itemCount: widget.logEntry.events.length +
widget.logEntry.endedEvents.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {
final event = (widget.logEntry.events + widget.logEntry.endedEvents)[index]; final event = (widget.logEntry.events +
widget.logEntry.endedEvents)[index];
return ListTile( return ListTile(
onTap: () { onTap: () {
handleEditAction(event); handleEditAction(event);
@ -103,10 +108,11 @@ class _LogEventListScreenState extends State<LogEventListScreen> {
); );
}) })
: const Center( : const Center(
child: Text('You have not added any Events to this Log Entry yet!'), child: Text(
), 'You have not added any Events to this Log Entry yet!'),
), ),
], ),
],
); );
} }
} }

View File

@ -1,9 +1,9 @@
import 'package:diameter/components/detail.dart'; import 'package:diameter/components/detail.dart';
import 'package:diameter/components/dialogs.dart'; import 'package:diameter/components/dialogs.dart';
import 'package:diameter/components/dropdown.dart';
import 'package:diameter/components/forms.dart'; import 'package:diameter/components/forms.dart';
import 'package:diameter/config.dart'; import 'package:diameter/config.dart';
import 'package:diameter/models/accuracy.dart'; import 'package:diameter/models/accuracy.dart';
import 'package:diameter/models/log_entry.dart';
import 'package:diameter/models/log_meal.dart'; import 'package:diameter/models/log_meal.dart';
import 'package:diameter/models/meal.dart'; import 'package:diameter/models/meal.dart';
import 'package:diameter/models/meal_category.dart'; import 'package:diameter/models/meal_category.dart';
@ -16,9 +16,11 @@ import 'package:flutter/material.dart';
class LogMealDetailScreen extends StatefulWidget { class LogMealDetailScreen extends StatefulWidget {
static const String routeName = '/log-meal'; static const String routeName = '/log-meal';
final LogEntry logEntry;
final LogMeal? logMeal; final int logEntryId;
const LogMealDetailScreen({Key? key, required this.logEntry, this.logMeal}) final int id;
const LogMealDetailScreen({Key? key, this.logEntryId = 0, this.id = 0})
: super(key: key); : super(key: key);
@override @override
@ -26,6 +28,10 @@ class LogMealDetailScreen extends StatefulWidget {
} }
class _LogMealDetailScreenState extends State<LogMealDetailScreen> { class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
LogMeal? _logMeal;
bool _isNew = true;
bool _isSaving = false;
final GlobalKey<FormState> _logMealForm = GlobalKey<FormState>(); final GlobalKey<FormState> _logMealForm = GlobalKey<FormState>();
final _valueController = TextEditingController(text: ''); final _valueController = TextEditingController(text: '');
@ -36,6 +42,7 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
final _delayedBolusRateController = TextEditingController(text: ''); final _delayedBolusRateController = TextEditingController(text: '');
final _delayedBolusDurationController = TextEditingController(text: ''); final _delayedBolusDurationController = TextEditingController(text: '');
final _notesController = TextEditingController(text: ''); final _notesController = TextEditingController(text: '');
Meal? _meal; Meal? _meal;
MealSource? _mealSource; MealSource? _mealSource;
MealCategory? _mealCategory; MealCategory? _mealCategory;
@ -50,11 +57,10 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
List<Accuracy> _portionSizeAccuracies = []; List<Accuracy> _portionSizeAccuracies = [];
List<Accuracy> _carbsRatioAccuracies = []; List<Accuracy> _carbsRatioAccuracies = [];
bool _isSaving = false;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
reload();
_portionSizeAccuracies = Accuracy.getAllForPortionSize(); _portionSizeAccuracies = Accuracy.getAllForPortionSize();
_carbsRatioAccuracies = Accuracy.getAllForCarbsRatio(); _carbsRatioAccuracies = Accuracy.getAllForCarbsRatio();
@ -63,35 +69,30 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
_mealPortionTypes = MealPortionType.getAll(); _mealPortionTypes = MealPortionType.getAll();
_mealSources = MealSource.getAll(); _mealSources = MealSource.getAll();
if (widget.logMeal != null) { if (widget.id != 0) {
_valueController.text = widget.logMeal!.value; _valueController.text = _logMeal!.value;
_carbsRatioController.text = _carbsRatioController.text = (_logMeal!.carbsRatio ?? '').toString();
(widget.logMeal!.carbsRatio ?? '').toString(); _portionSizeController.text = (_logMeal!.portionSize ?? '').toString();
_portionSizeController.text =
(widget.logMeal!.portionSize ?? '').toString();
_carbsPerPortionController.text = _carbsPerPortionController.text =
(widget.logMeal!.carbsPerPortion ?? '').toString(); (_logMeal!.carbsPerPortion ?? '').toString();
_bolusController.text = (widget.logMeal!.bolus ?? '').toString(); _bolusController.text = (_logMeal!.bolus ?? '').toString();
_delayedBolusRateController.text = _delayedBolusRateController.text =
(widget.logMeal!.delayedBolusRate ?? '').toString(); (_logMeal!.delayedBolusRate ?? '').toString();
_delayedBolusDurationController.text = _delayedBolusDurationController.text =
(widget.logMeal!.delayedBolusDuration ?? '').toString(); (_logMeal!.delayedBolusDuration ?? '').toString();
_notesController.text = widget.logMeal!.notes ?? ''; _notesController.text = _logMeal!.notes ?? '';
// _meal = widget.logMeal!.meal;
// _source = widget.logMeal!.source;
// _category = widget.logMeal!.category;
// _portionType = widget.logMeal!.portionType;
// _portionSizeAccuracy = _portionSizeAccuracies.firstWhere((element) =>
// element.id ==
// int.tryParse(widget.logMeal!.portionSizeAccuracy ?? ''));
// _carbsRatioAccuracy = _carbsRatioAccuracies.firstWhere((element) =>
// element.id == int.tryParse(widget.logMeal!.carbsRatioAccuracy ?? ''));
// _portionSizeAccuracy = widget.meal!.portionSizeAccuracy;
// _carbsRatioAccuracy = widget.meal!.carbsRatioAccuracy;
} }
} }
void reload() {
if (widget.id != 0) {
setState(() {
_logMeal = LogMeal.get(widget.id);
});
}
_isNew = _logMeal == null;
}
Future<void> onSelectMeal(Meal meal) async { Future<void> onSelectMeal(Meal meal) async {
setState(() { setState(() {
_meal = meal; _meal = meal;
@ -135,52 +136,8 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
_isSaving = true; _isSaving = true;
}); });
if (_logMealForm.currentState!.validate()) { if (_logMealForm.currentState!.validate()) {
bool isNew = widget.logMeal == null;
// isNew
// ? await LogMeal.save(
// logEntry: widget.logEntry.objectId!,
// meal: _meal,
// value: _valueController.text,
// source: _mealSource,
// category: _category,
// portionType: _portionType,
// carbsRatio: double.tryParse(_carbsRatioController.text),
// portionSize: double.tryParse(_portionSizeController.text),
// carbsPerPortion: double.tryParse(_carbsPerPortionController.text),
// // portionSizeAccuracy: _portionSizeAccuracy,
// // carbsRatioAccuracy: _carbsRatioAccuracy,
// portionSizeAccuracy: _portionSizeAccuracy?.id.toString(),
// carbsRatioAccuracy: _carbsRatioAccuracy?.id.toString(),
// bolus: double.tryParse(_bolusController.text),
// delayedBolusDuration:
// int.tryParse(_delayedBolusDurationController.text),
// delayedBolusRate:
// double.tryParse(_delayedBolusRateController.text),
// notes: _notesController.text,
// )
// : await LogMeal.update(
// widget.logMeal!.objectId!,
// meal: _meal,
// value: _valueController.text,
// source: _mealSource,
// category: _category,
// portionType: _portionType,
// carbsRatio: double.tryParse(_carbsRatioController.text),
// portionSize: double.tryParse(_portionSizeController.text),
// carbsPerPortion: double.tryParse(_carbsPerPortionController.text),
// // portionSizeAccuracy: _portionSizeAccuracy,
// // carbsRatioAccuracy: _carbsRatioAccuracy,
// portionSizeAccuracy: _portionSizeAccuracy?.id.toString(),
// carbsRatioAccuracy: _carbsRatioAccuracy?.id.toString(),
// bolus: double.tryParse(_bolusController.text),
// delayedBolusDuration:
// int.tryParse(_delayedBolusDurationController.text),
// delayedBolusRate:
// double.tryParse(_delayedBolusRateController.text),
// notes: _notesController.text,
// );
LogMeal logMeal = LogMeal( LogMeal logMeal = LogMeal(
id: widget.logMeal?.id ?? 0, id: widget.id,
value: _valueController.text, value: _valueController.text,
carbsRatio: double.tryParse(_carbsRatioController.text), carbsRatio: double.tryParse(_carbsRatioController.text),
portionSize: double.tryParse(_portionSizeController.text), portionSize: double.tryParse(_portionSizeController.text),
@ -199,7 +156,7 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
logMeal.carbsRatioAccuracy.target = _carbsRatioAccuracy; logMeal.carbsRatioAccuracy.target = _carbsRatioAccuracy;
LogMeal.put(logMeal); LogMeal.put(logMeal);
Navigator.pop(context, '${isNew ? 'New' : ''} Meal Saved'); Navigator.pop(context, '${_isNew ? 'New' : ''} Meal Saved');
} }
setState(() { setState(() {
_isSaving = false; _isSaving = false;
@ -207,9 +164,8 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
} }
void handleCancelAction() { void handleCancelAction() {
bool isNew = widget.logMeal == null;
if (showConfirmationDialogOnCancel && if (showConfirmationDialogOnCancel &&
((isNew && ((_isNew &&
(_valueController.text != '' || (_valueController.text != '' ||
_meal != null || _meal != null ||
_mealSource != null || _mealSource != null ||
@ -225,35 +181,32 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
null || null ||
double.tryParse(_delayedBolusRateController.text) != null || double.tryParse(_delayedBolusRateController.text) != null ||
_notesController.text != '')) || _notesController.text != '')) ||
(!isNew && (!_isNew &&
(_valueController.text != widget.logMeal!.value || (_valueController.text != _logMeal!.value ||
_meal != widget.logMeal!.meal.target || _meal != _logMeal!.meal.target ||
_mealSource != widget.logMeal!.mealSource.target || _mealSource != _logMeal!.mealSource.target ||
_mealCategory != widget.logMeal!.mealCategory.target || _mealCategory != _logMeal!.mealCategory.target ||
_mealPortionType != widget.logMeal!.mealPortionType.target || _mealPortionType != _logMeal!.mealPortionType.target ||
double.tryParse(_carbsRatioController.text) != double.tryParse(_carbsRatioController.text) !=
widget.logMeal!.carbsRatio || _logMeal!.carbsRatio ||
double.tryParse(_portionSizeController.text) != double.tryParse(_portionSizeController.text) !=
widget.logMeal!.portionSize || _logMeal!.portionSize ||
double.tryParse(_carbsPerPortionController.text) != double.tryParse(_carbsPerPortionController.text) !=
widget.logMeal!.carbsPerPortion || _logMeal!.carbsPerPortion ||
// _carbsRatioAccuracy != widget.logMeal!.carbsRatioAccuracy ||
// _portionSizeAccuracy !=
// widget.logMeal!.portionSizeAccuracy ||
_carbsRatioAccuracy != _carbsRatioAccuracy !=
widget.logMeal!.carbsRatioAccuracy.target || _logMeal!.carbsRatioAccuracy.target ||
_portionSizeAccuracy != _portionSizeAccuracy !=
widget.logMeal!.portionSizeAccuracy.target || _logMeal!.portionSizeAccuracy.target ||
double.tryParse(_bolusController.text) != double.tryParse(_bolusController.text) !=
widget.logMeal!.bolus || _logMeal!.bolus ||
int.tryParse(_delayedBolusDurationController.text) != int.tryParse(_delayedBolusDurationController.text) !=
widget.logMeal!.delayedBolusDuration || _logMeal!.delayedBolusDuration ||
double.tryParse(_delayedBolusRateController.text) != double.tryParse(_delayedBolusRateController.text) !=
widget.logMeal!.delayedBolusRate || _logMeal!.delayedBolusRate ||
_notesController.text != (widget.logMeal!.notes ?? ''))))) { _notesController.text != (_logMeal!.notes ?? ''))))) {
Dialogs.showCancelConfirmationDialog( Dialogs.showCancelConfirmationDialog(
context: context, context: context,
isNew: isNew, isNew: _isNew,
onSave: handleSaveAction, onSave: handleSaveAction,
); );
} else { } else {
@ -305,10 +258,9 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
bool isNew = widget.logMeal == null;
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text(isNew ? 'New Meal' : widget.logMeal!.value), title: Text(_isNew ? 'New Meal' : _logMeal!.value),
), ),
drawer: const Navigation(currentLocation: LogMealDetailScreen.routeName), drawer: const Navigation(currentLocation: LogMealDetailScreen.routeName),
body: SingleChildScrollView( body: SingleChildScrollView(
@ -330,48 +282,44 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
return null; return null;
}, },
), ),
LabeledDropdownButton<Meal>( AutoCompleteDropdownButton<Meal>(
selectedItem: _meal, selectedItem: _meal,
label: 'Meal', label: 'Meal',
items: _meals, items: _meals,
// getItemValue: (item) => item.objectId, renderItem: (item) => item.value,
renderItem: (item) => Text(item.value),
onChanged: (value) { onChanged: (value) {
if (value != null) { if (value != null) {
onSelectMeal(value); onSelectMeal(value);
} }
}, },
), ),
LabeledDropdownButton<MealSource>( AutoCompleteDropdownButton<MealSource>(
selectedItem: _mealSource, selectedItem: _mealSource,
label: 'Meal Source', label: 'Meal Source',
items: _mealSources, items: _mealSources,
// getItemValue: (item) => item.objectId, renderItem: (item) => item.value,
renderItem: (item) => Text(item.value),
onChanged: (value) { onChanged: (value) {
setState(() { setState(() {
_mealSource = value; _mealSource = value;
}); });
}, },
), ),
LabeledDropdownButton<MealCategory>( AutoCompleteDropdownButton<MealCategory>(
selectedItem: _mealCategory, selectedItem: _mealCategory,
label: 'Meal Category', label: 'Meal Category',
items: _mealCategories, items: _mealCategories,
// getItemValue: (item) => item.objectId, renderItem: (item) => item.value,
renderItem: (item) => Text(item.value),
onChanged: (value) { onChanged: (value) {
setState(() { setState(() {
_mealCategory = value; _mealCategory = value;
}); });
}, },
), ),
LabeledDropdownButton<MealPortionType>( AutoCompleteDropdownButton<MealPortionType>(
selectedItem: _mealPortionType, selectedItem: _mealPortionType,
label: 'Meal Portion Type', label: 'Meal Portion Type',
items: _mealPortionTypes, items: _mealPortionTypes,
// getItemValue: (item) => item.objectId, renderItem: (item) => item.value,
renderItem: (item) => Text(item.value),
onChanged: (value) { onChanged: (value) {
setState(() { setState(() {
_mealPortionType = value; _mealPortionType = value;
@ -434,30 +382,18 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
), ),
], ],
), ),
LabeledDropdownButton<Accuracy>( AutoCompleteDropdownButton<Accuracy>(
selectedItem: _portionSizeAccuracy, selectedItem: _portionSizeAccuracy,
label: 'Portion Size Accuracy', label: 'Portion Size Accuracy',
items: _portionSizeAccuracies, items: _portionSizeAccuracies,
// getItemValue: (item) => item.objectId, // getItemValue: (item) => item.objectId,
renderItem: (item) => Text(item.value), renderItem: (item) => item.value,
onChanged: (value) { onChanged: (value) {
setState(() { setState(() {
_portionSizeAccuracy = value; _portionSizeAccuracy = value;
}); });
}, },
), ),
// StyledFutureDropdownButton<Accuracy>(
// selectedItem: _portionSizeAccuracy,
// label: 'Portion Size Accuracy',
// items: _portionSizeAccuracies,
// getItemValue: (item) => item.objectId,
// renderItem: (item) => Text(item.value),
// onChanged: (value) {
// setState(() {
// _portionSizeAccuracy = value;
// });
// },
// ),
Row( Row(
children: [ children: [
Expanded( Expanded(
@ -488,30 +424,17 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
), ),
], ],
), ),
LabeledDropdownButton<Accuracy>( AutoCompleteDropdownButton<Accuracy>(
selectedItem: _carbsRatioAccuracy, selectedItem: _carbsRatioAccuracy,
label: 'Carbs Ratio Accuracy', label: 'Carbs Ratio Accuracy',
items: _carbsRatioAccuracies, items: _carbsRatioAccuracies,
// getItemValue: (item) => item.objectId, renderItem: (item) => item.value,
renderItem: (item) => Text(item.value),
onChanged: (value) { onChanged: (value) {
setState(() { setState(() {
_carbsRatioAccuracy = value; _carbsRatioAccuracy = value;
}); });
}, },
), ),
// StyledFutureDropdownButton<Accuracy>(
// selectedItem: _carbsRatioAccuracy,
// label: 'Carbs Ratio Accuracy',
// items: _carbsRatioAccuracies,
// getItemValue: (item) => item.objectId,
// renderItem: (item) => Text(item.value),
// onChanged: (value) {
// setState(() {
// _carbsRatioAccuracy = value;
// });
// },
// ),
TextFormField( TextFormField(
decoration: const InputDecoration( decoration: const InputDecoration(
labelText: 'Bolus Units', labelText: 'Bolus Units',

View 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!'),
),
),
],
);
}
}

View File

@ -8,9 +8,8 @@ import 'package:flutter/material.dart';
class LogEventTypeDetailScreen extends StatefulWidget { class LogEventTypeDetailScreen extends StatefulWidget {
static const String routeName = '/log-event-type'; static const String routeName = '/log-event-type';
final LogEventType? logEventType; final int id;
const LogEventTypeDetailScreen({Key? key, this.logEventType}) const LogEventTypeDetailScreen({Key? key, this.id = 0}) : super(key: key);
: super(key: key);
@override @override
_LogEventTypeDetailScreenState createState() => _LogEventTypeDetailScreenState createState() =>
@ -18,35 +17,49 @@ class LogEventTypeDetailScreen extends StatefulWidget {
} }
class _LogEventTypeDetailScreenState extends State<LogEventTypeDetailScreen> { class _LogEventTypeDetailScreenState extends State<LogEventTypeDetailScreen> {
LogEventType? _logEventType;
bool _isNew = true;
bool _isSaving = false;
final GlobalKey<FormState> _logEventTypeForm = GlobalKey<FormState>(); final GlobalKey<FormState> _logEventTypeForm = GlobalKey<FormState>();
final _valueController = TextEditingController(text: ''); final _valueController = TextEditingController(text: '');
final _defaultReminderDurationController = TextEditingController(text: ''); final _defaultReminderDurationController = TextEditingController(text: '');
final _notesController = TextEditingController(text: ''); final _notesController = TextEditingController(text: '');
bool _hasEndTime = false; bool _hasEndTime = false;
bool _isSaving = false;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
if (widget.logEventType != null) { reload();
_valueController.text = widget.logEventType!.value;
if (_logEventType != null) {
_valueController.text = _logEventType!.value;
_defaultReminderDurationController.text = _defaultReminderDurationController.text =
(widget.logEventType!.defaultReminderDuration ?? '').toString(); (_logEventType!.defaultReminderDuration ?? '').toString();
_notesController.text = widget.logEventType!.notes ?? ''; _notesController.text = _logEventType!.notes ?? '';
_hasEndTime = widget.logEventType!.hasEndTime; _hasEndTime = _logEventType!.hasEndTime;
} }
} }
void reload() {
if (widget.id != 0) {
setState(() {
_logEventType = LogEventType.get(widget.id);
});
}
_isNew = _logEventType == null;
}
void handleSaveAction() async { void handleSaveAction() async {
setState(() { setState(() {
_isSaving = true; _isSaving = true;
}); });
if (_logEventTypeForm.currentState!.validate()) { if (_logEventTypeForm.currentState!.validate()) {
bool isNew = widget.logEventType == null; bool isNew = _logEventType == null;
LogEventType.put(LogEventType( LogEventType.put(LogEventType(
id: widget.logEventType?.id ?? 0, id: widget.id,
value: _valueController.text, value: _valueController.text,
notes: _notesController.text, notes: _notesController.text,
defaultReminderDuration: defaultReminderDuration:
@ -61,7 +74,7 @@ class _LogEventTypeDetailScreenState extends State<LogEventTypeDetailScreen> {
} }
void handleCancelAction() { void handleCancelAction() {
bool isNew = widget.logEventType == null; bool isNew = _logEventType == null;
if (showConfirmationDialogOnCancel && if (showConfirmationDialogOnCancel &&
((isNew && ((isNew &&
(_valueController.text != '' || (_valueController.text != '' ||
@ -70,12 +83,11 @@ class _LogEventTypeDetailScreenState extends State<LogEventTypeDetailScreen> {
_notesController.text != '' || _notesController.text != '' ||
_hasEndTime)) || _hasEndTime)) ||
(!isNew && (!isNew &&
(_valueController.text != widget.logEventType!.value || (_valueController.text != _logEventType!.value ||
int.tryParse(_defaultReminderDurationController.text) != int.tryParse(_defaultReminderDurationController.text) !=
widget.logEventType!.defaultReminderDuration || _logEventType!.defaultReminderDuration ||
_notesController.text != _notesController.text != (_logEventType!.notes ?? '') ||
(widget.logEventType!.notes ?? '') || _hasEndTime != _logEventType!.hasEndTime)))) {
_hasEndTime != widget.logEventType!.hasEndTime)))) {
Dialogs.showCancelConfirmationDialog( Dialogs.showCancelConfirmationDialog(
context: context, context: context,
isNew: isNew, isNew: isNew,
@ -88,10 +100,9 @@ class _LogEventTypeDetailScreenState extends State<LogEventTypeDetailScreen> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
bool isNew = widget.logEventType == null;
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text(isNew ? 'New Log Event Type' : widget.logEventType!.value), title: Text(_isNew ? 'New Log Event Type' : _logEventType!.value),
), ),
drawer: drawer:
const Navigation(currentLocation: LogEventTypeDetailScreen.routeName), const Navigation(currentLocation: LogEventTypeDetailScreen.routeName),

View File

@ -1,4 +1,3 @@
// import 'package:diameter/components/progress_indicator.dart';
import 'package:diameter/models/log_event_type.dart'; import 'package:diameter/models/log_event_type.dart';
import 'package:diameter/navigation.dart'; import 'package:diameter/navigation.dart';
import 'package:diameter/screens/log/log_event_type_detail.dart'; import 'package:diameter/screens/log/log_event_type_detail.dart';
@ -15,7 +14,13 @@ class LogEventTypeListScreen extends StatefulWidget {
class _LogEventTypeListScreenState extends State<LogEventTypeListScreen> { class _LogEventTypeListScreenState extends State<LogEventTypeListScreen> {
List<LogEventType> _logEventTypes = []; List<LogEventType> _logEventTypes = [];
void refresh({String? message}) { @override
void initState() {
super.initState();
reload();
}
void reload({String? message}) {
setState(() { setState(() {
_logEventTypes = LogEventType.getAll(); _logEventTypes = LogEventType.getAll();
}); });
@ -32,17 +37,11 @@ class _LogEventTypeListScreenState extends State<LogEventTypeListScreen> {
}); });
} }
@override
void initState() {
super.initState();
refresh();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar(title: const Text('Log Event Types'), actions: <Widget>[ appBar: AppBar(title: const Text('Log Event Types'), actions: <Widget>[
IconButton(onPressed: refresh, icon: const Icon(Icons.refresh)) IconButton(onPressed: reload, icon: const Icon(Icons.refresh))
]), ]),
drawer: drawer:
const Navigation(currentLocation: LogEventTypeListScreen.routeName), const Navigation(currentLocation: LogEventTypeListScreen.routeName),
@ -54,7 +53,6 @@ class _LogEventTypeListScreenState extends State<LogEventTypeListScreen> {
padding: const EdgeInsets.all(10.0), padding: const EdgeInsets.all(10.0),
itemCount: _logEventTypes.length, itemCount: _logEventTypes.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {
// final logEventType = snapshot.data![index];
final logEventType = _logEventTypes[index]; final logEventType = _logEventTypes[index];
return ListTile( return ListTile(
onTap: () { onTap: () {
@ -63,9 +61,9 @@ class _LogEventTypeListScreenState extends State<LogEventTypeListScreen> {
MaterialPageRoute( MaterialPageRoute(
builder: (context) => builder: (context) =>
LogEventTypeDetailScreen( LogEventTypeDetailScreen(
logEventType: logEventType), id: logEventType.id),
), ),
).then((message) => refresh(message: message)); ).then((message) => reload(message: message));
}, },
title: Text(logEventType.value), title: Text(logEventType.value),
subtitle: Text(logEventType.notes ?? ''), subtitle: Text(logEventType.notes ?? ''),
@ -76,7 +74,7 @@ class _LogEventTypeListScreenState extends State<LogEventTypeListScreen> {
onPressed: () async { onPressed: () async {
LogEventType.remove(logEventType.id); LogEventType.remove(logEventType.id);
// await logEventType.delete().then((_) { // await logEventType.delete().then((_) {
refresh( reload(
message: 'Log Event Type deleted'); message: 'Log Event Type deleted');
// }); // });
}, },
@ -100,7 +98,7 @@ class _LogEventTypeListScreenState extends State<LogEventTypeListScreen> {
MaterialPageRoute( MaterialPageRoute(
builder: (context) => const LogEventTypeDetailScreen(), builder: (context) => const LogEventTypeDetailScreen(),
), ),
).then((message) => refresh(message: message)); ).then((message) => reload(message: message));
}, },
child: const Icon(Icons.add), child: const Icon(Icons.add),
), ),

View File

@ -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!'),
),
),
],
);
}
}

View File

@ -8,10 +8,9 @@ import 'package:diameter/models/meal_category.dart';
class MealCategoryDetailScreen extends StatefulWidget { class MealCategoryDetailScreen extends StatefulWidget {
static const String routeName = '/meal-category'; static const String routeName = '/meal-category';
final MealCategory? mealCategory; final int id;
const MealCategoryDetailScreen({Key? key, this.mealCategory}) const MealCategoryDetailScreen({Key? key, this.id = 0}) : super(key: key);
: super(key: key);
@override @override
_MealCategoryDetailScreenState createState() => _MealCategoryDetailScreenState createState() =>
@ -19,45 +18,54 @@ class MealCategoryDetailScreen extends StatefulWidget {
} }
class _MealCategoryDetailScreenState extends State<MealCategoryDetailScreen> { class _MealCategoryDetailScreenState extends State<MealCategoryDetailScreen> {
MealCategory? _mealCategory;
bool _isNew = true;
final GlobalKey<FormState> _mealCategoryForm = GlobalKey<FormState>(); final GlobalKey<FormState> _mealCategoryForm = GlobalKey<FormState>();
final _valueController = TextEditingController(text: ''); final _valueController = TextEditingController(text: '');
final _notesController = TextEditingController(text: ''); final _notesController = TextEditingController(text: '');
@override @override
void initState() { void initState() {
super.initState(); super.initState();
if (widget.mealCategory != null) { reload();
_valueController.text = widget.mealCategory!.value;
_notesController.text = widget.mealCategory!.notes ?? ''; if (_mealCategory != null) {
_valueController.text = _mealCategory!.value;
_notesController.text = _mealCategory!.notes ?? '';
} }
} }
void reload() {
if (widget.id != 0) {
setState(() {
_mealCategory = MealCategory.get(widget.id);
});
}
_isNew = _mealCategory == null;
}
void handleSaveAction() async { void handleSaveAction() async {
if (_mealCategoryForm.currentState!.validate()) { if (_mealCategoryForm.currentState!.validate()) {
bool isNew = widget.mealCategory == null; MealCategory.put(MealCategory(
// isNew id: widget.id,
// ? await MealCategory.save( value: _valueController.text,
// value: _valueController.text, notes: _notesController.text) notes: _notesController.text));
// : await MealCategory.update(widget.mealCategory!.objectId!, Navigator.pop(context, '${_isNew ? 'New' : ''} Meal Category saved');
// value: _valueController.text, notes: _notesController.text);
MealCategory.put(MealCategory(id: widget.mealCategory?.id ?? 0,
value: _valueController.text, notes: _notesController.text));
Navigator.pop(context, '${isNew ? 'New' : ''} Meal Category saved');
} }
} }
void handleCancelAction() { void handleCancelAction() {
bool isNew = widget.mealCategory == null;
if (showConfirmationDialogOnCancel && if (showConfirmationDialogOnCancel &&
(isNew && (_isNew &&
(_valueController.text != '' || _notesController.text != '')) || (_valueController.text != '' || _notesController.text != '')) ||
(!isNew && (!_isNew &&
(widget.mealCategory!.value != _valueController.text || (_mealCategory!.value != _valueController.text ||
(widget.mealCategory!.notes ?? '') != _notesController.text))) { (_mealCategory!.notes ?? '') != _notesController.text))) {
Dialogs.showCancelConfirmationDialog( Dialogs.showCancelConfirmationDialog(
context: context, context: context,
isNew: isNew, isNew: _isNew,
onSave: handleSaveAction, onSave: handleSaveAction,
); );
} else { } else {
@ -67,10 +75,9 @@ class _MealCategoryDetailScreenState extends State<MealCategoryDetailScreen> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
bool isNew = widget.mealCategory == null;
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text(isNew ? 'New Meal Category' : widget.mealCategory!.value), title: Text(_isNew ? 'New Meal Category' : _mealCategory!.value),
), ),
drawer: drawer:
const Navigation(currentLocation: MealCategoryDetailScreen.routeName), const Navigation(currentLocation: MealCategoryDetailScreen.routeName),

View File

@ -18,7 +18,13 @@ class MealCategoryListScreen extends StatefulWidget {
class _MealCategoryListScreenState extends State<MealCategoryListScreen> { class _MealCategoryListScreenState extends State<MealCategoryListScreen> {
List<MealCategory> _mealCategories = []; List<MealCategory> _mealCategories = [];
void refresh({String? message}) { @override
void initState() {
super.initState();
reload();
}
void reload({String? message}) {
setState(() { setState(() {
_mealCategories = MealCategory.getAll(); _mealCategories = MealCategory.getAll();
}); });
@ -37,7 +43,7 @@ class _MealCategoryListScreenState extends State<MealCategoryListScreen> {
void onDelete(MealCategory mealCategory) { void onDelete(MealCategory mealCategory) {
MealCategory.remove(mealCategory.id); MealCategory.remove(mealCategory.id);
refresh(message: 'Meal Category deleted'); reload(message: 'Meal Category deleted');
} }
void handleDeleteAction(MealCategory mealCategory) async { void handleDeleteAction(MealCategory mealCategory) async {
@ -52,12 +58,6 @@ class _MealCategoryListScreenState extends State<MealCategoryListScreen> {
} }
} }
@override
void initState() {
super.initState();
refresh();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
@ -65,7 +65,7 @@ class _MealCategoryListScreenState extends State<MealCategoryListScreen> {
title: const Text('Meal Categories'), title: const Text('Meal Categories'),
actions: <Widget>[ actions: <Widget>[
IconButton( IconButton(
onPressed: refresh, onPressed: reload,
icon: const Icon(Icons.refresh), icon: const Icon(Icons.refresh),
), ),
], ],
@ -88,11 +88,11 @@ class _MealCategoryListScreenState extends State<MealCategoryListScreen> {
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => builder: (context) =>
MealCategoryDetailScreen( MealCategoryDetailScreen(
mealCategory: mealCategory, id: mealCategory.id,
), ),
), ),
).then((message) => refresh(message: message)); ).then((message) => reload(message: message));
}, },
title: Text(mealCategory.value), title: Text(mealCategory.value),
subtitle: Text(mealCategory.notes ?? ''), subtitle: Text(mealCategory.notes ?? ''),
@ -124,7 +124,7 @@ class _MealCategoryListScreenState extends State<MealCategoryListScreen> {
MaterialPageRoute( MaterialPageRoute(
builder: (context) => const MealCategoryDetailScreen(), builder: (context) => const MealCategoryDetailScreen(),
), ),
).then((message) => refresh(message: message)); ).then((message) => reload(message: message));
}, },
child: const Icon(Icons.add), child: const Icon(Icons.add),
), ),

View File

@ -1,5 +1,6 @@
import 'package:diameter/components/detail.dart'; import 'package:diameter/components/detail.dart';
import 'package:diameter/components/dialogs.dart'; import 'package:diameter/components/dialogs.dart';
import 'package:diameter/components/dropdown.dart';
import 'package:diameter/components/forms.dart'; import 'package:diameter/components/forms.dart';
import 'package:diameter/config.dart'; import 'package:diameter/config.dart';
import 'package:diameter/models/accuracy.dart'; import 'package:diameter/models/accuracy.dart';
@ -8,22 +9,25 @@ import 'package:diameter/models/meal_category.dart';
import 'package:diameter/models/meal_portion_type.dart'; import 'package:diameter/models/meal_portion_type.dart';
import 'package:diameter/models/meal_source.dart'; import 'package:diameter/models/meal_source.dart';
import 'package:diameter/navigation.dart'; import 'package:diameter/navigation.dart';
// import 'package:diameter/objectbox.g.dart';
import 'package:diameter/settings.dart'; import 'package:diameter/settings.dart';
import 'package:diameter/utils/utils.dart'; import 'package:diameter/utils/utils.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class MealDetailScreen extends StatefulWidget { class MealDetailScreen extends StatefulWidget {
static const String routeName = '/meal'; static const String routeName = '/meal';
final int id;
final Meal? meal; const MealDetailScreen({Key? key, this.id = 0}) : super(key: key);
const MealDetailScreen({Key? key, this.meal}) : super(key: key);
@override @override
_MealDetailScreenState createState() => _MealDetailScreenState(); _MealDetailScreenState createState() => _MealDetailScreenState();
} }
class _MealDetailScreenState extends State<MealDetailScreen> { class _MealDetailScreenState extends State<MealDetailScreen> {
Meal? _meal;
bool _isNew = true;
bool _isSaving = false;
final GlobalKey<FormState> _mealForm = GlobalKey<FormState>(); final GlobalKey<FormState> _mealForm = GlobalKey<FormState>();
final _valueController = TextEditingController(text: ''); final _valueController = TextEditingController(text: '');
@ -46,84 +50,54 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
List<Accuracy> _portionSizeAccuracies = []; List<Accuracy> _portionSizeAccuracies = [];
List<Accuracy> _carbsRatioAccuracies = []; List<Accuracy> _carbsRatioAccuracies = [];
bool isSaving = false;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
reload();
_portionSizeAccuracies = Accuracy.getAllForPortionSize(); _portionSizeAccuracies = Accuracy.getAllForPortionSize();
_carbsRatioAccuracies = Accuracy.getAllForCarbsRatio(); _carbsRatioAccuracies = Accuracy.getAllForCarbsRatio();
_mealCategories = MealCategory.getAll(); _mealCategories = MealCategory.getAll();
_mealPortionTypes = MealPortionType.getAll(); _mealPortionTypes = MealPortionType.getAll();
_mealSources = MealSource.getAll(); _mealSources = MealSource.getAll();
if (widget.meal != null) { if (_meal != null) {
_valueController.text = widget.meal!.value; _valueController.text = _meal!.value;
_carbsRatioController.text = (widget.meal!.carbsRatio ?? '').toString(); _carbsRatioController.text = (_meal!.carbsRatio ?? '').toString();
_portionSizeController.text = (widget.meal!.portionSize ?? '').toString(); _portionSizeController.text = (_meal!.portionSize ?? '').toString();
_carbsPerPortionController.text = _carbsPerPortionController.text =
(widget.meal!.carbsPerPortion ?? '').toString(); (_meal!.carbsPerPortion ?? '').toString();
_delayedBolusRateController.text = _delayedBolusRateController.text =
(widget.meal!.delayedBolusRate ?? '').toString(); (_meal!.delayedBolusRate ?? '').toString();
_delayedBolusDurationController.text = _delayedBolusDurationController.text =
(widget.meal!.delayedBolusDuration ?? '').toString(); (_meal!.delayedBolusDuration ?? '').toString();
_notesController.text = widget.meal!.notes ?? ''; _notesController.text = _meal!.notes ?? '';
_mealSource = widget.meal!.mealSource.target; _mealSource = _meal!.mealSource.target;
_mealCategory = widget.meal!.mealCategory.target; _mealCategory = _meal!.mealCategory.target;
_mealPortionType = widget.meal!.mealPortionType.target; _mealPortionType = _meal!.mealPortionType.target;
_portionSizeAccuracy = widget.meal!.portionSizeAccuracy.target; _portionSizeAccuracy = _meal!.portionSizeAccuracy.target;
_carbsRatioAccuracy = widget.meal!.carbsRatioAccuracy.target; _carbsRatioAccuracy = _meal!.carbsRatioAccuracy.target;
} }
} }
void reload() {
if (widget.id != 0) {
setState(() {
_meal = Meal.get(widget.id);
});
}
_isNew = _meal == null;
}
void handleSaveAction() async { void handleSaveAction() async {
setState(() { setState(() {
isSaving = true; _isSaving = true;
}); });
if (_mealForm.currentState!.validate()) { if (_mealForm.currentState!.validate()) {
bool isNew = widget.meal == null;
// isNew
// ? await Meal.save(
// value: _valueController.text,
// source: _mealSource,
// category: _mealCategory,
// portionType: _mealPortionType,
// carbsRatio: double.tryParse(_carbsRatioController.text),
// portionSize: double.tryParse(_portionSizeController.text),
// carbsPerPortion: double.tryParse(_carbsPerPortionController.text),
// // portionSizeAccuracy: _portionSizeAccuracy,
// // carbsRatioAccuracy: _carbsRatioAccuracy,
// portionSizeAccuracy: _portionSizeAccuracy?.id.toString(),
// carbsRatioAccuracy: _carbsRatioAccuracy?.id.toString(),
// delayedBolusDuration:
// int.tryParse(_delayedBolusDurationController.text),
// delayedBolusRate:
// double.tryParse(_delayedBolusRateController.text),
// notes: _notesController.text,
// )
// : await Meal.update(
// widget.meal!.objectId!,
// value: _valueController.text,
// source: _mealSource,
// category: _mealCategory,
// portionType: _mealPortionType,
// carbsRatio: double.tryParse(_carbsRatioController.text),
// portionSize: double.tryParse(_portionSizeController.text),
// carbsPerPortion: double.tryParse(_carbsPerPortionController.text),
// // portionSizeAccuracy: _portionSizeAccuracy,
// // carbsRatioAccuracy: _carbsRatioAccuracy,
// portionSizeAccuracy: _portionSizeAccuracy?.id.toString(),
// carbsRatioAccuracy: _carbsRatioAccuracy?.id.toString(),
// delayedBolusDuration:
// int.tryParse(_delayedBolusDurationController.text),
// delayedBolusRate:
// double.tryParse(_delayedBolusRateController.text),
// notes: _notesController.text,
// );
Meal meal = Meal( Meal meal = Meal(
id: widget.meal?.id ?? 0, id: widget.id,
value: _valueController.text, value: _valueController.text,
carbsRatio: double.tryParse(_carbsRatioController.text), carbsRatio: double.tryParse(_carbsRatioController.text),
portionSize: double.tryParse(_portionSizeController.text), portionSize: double.tryParse(_portionSizeController.text),
@ -140,17 +114,16 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
meal.carbsRatioAccuracy.target = _carbsRatioAccuracy; meal.carbsRatioAccuracy.target = _carbsRatioAccuracy;
Meal.put(meal); Meal.put(meal);
Navigator.pop(context, '${isNew ? 'New' : ''} Meal Saved'); Navigator.pop(context, '${_isNew ? 'New' : ''} Meal Saved');
} }
setState(() { setState(() {
isSaving = false; _isSaving = false;
}); });
} }
void handleCancelAction() { void handleCancelAction() {
bool isNew = widget.meal == null;
if (showConfirmationDialogOnCancel && if (showConfirmationDialogOnCancel &&
((isNew && ((_isNew &&
(_valueController.text != '' || (_valueController.text != '' ||
_mealSource != null || _mealSource != null ||
_mealCategory != null || _mealCategory != null ||
@ -164,27 +137,27 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
null || null ||
double.tryParse(_delayedBolusRateController.text) != null || double.tryParse(_delayedBolusRateController.text) != null ||
_notesController.text != '')) || _notesController.text != '')) ||
(!isNew && (!_isNew &&
(_valueController.text != widget.meal!.value || (_valueController.text != _meal!.value ||
_mealSource != widget.meal!.mealSource.target || _mealSource != _meal!.mealSource.target ||
_mealCategory != widget.meal!.mealCategory.target || _mealCategory != _meal!.mealCategory.target ||
_mealPortionType != widget.meal!.mealPortionType.target || _mealPortionType != _meal!.mealPortionType.target ||
double.tryParse(_carbsRatioController.text) != double.tryParse(_carbsRatioController.text) !=
widget.meal!.carbsRatio || _meal!.carbsRatio ||
double.tryParse(_portionSizeController.text) != double.tryParse(_portionSizeController.text) !=
widget.meal!.portionSize || _meal!.portionSize ||
double.tryParse(_carbsPerPortionController.text) != double.tryParse(_carbsPerPortionController.text) !=
widget.meal!.carbsPerPortion || _meal!.carbsPerPortion ||
_carbsRatioAccuracy != widget.meal!.carbsRatioAccuracy.target || _carbsRatioAccuracy != _meal!.carbsRatioAccuracy.target ||
_portionSizeAccuracy != widget.meal!.portionSizeAccuracy.target || _portionSizeAccuracy != _meal!.portionSizeAccuracy.target ||
int.tryParse(_delayedBolusDurationController.text) != int.tryParse(_delayedBolusDurationController.text) !=
widget.meal!.delayedBolusDuration || _meal!.delayedBolusDuration ||
double.tryParse(_delayedBolusRateController.text) != double.tryParse(_delayedBolusRateController.text) !=
widget.meal!.delayedBolusRate || _meal!.delayedBolusRate ||
_notesController.text != (widget.meal!.notes ?? ''))))) { _notesController.text != (_meal!.notes ?? ''))))) {
Dialogs.showCancelConfirmationDialog( Dialogs.showCancelConfirmationDialog(
context: context, context: context,
isNew: isNew, isNew: _isNew,
onSave: handleSaveAction, onSave: handleSaveAction,
); );
} else { } else {
@ -196,12 +169,10 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
setState(() { setState(() {
_mealSource = mealSource; _mealSource = mealSource;
if (mealSource.defaultCarbsRatioAccuracy.hasValue) { if (mealSource.defaultCarbsRatioAccuracy.hasValue) {
_carbsRatioAccuracy = _carbsRatioAccuracy = mealSource.defaultCarbsRatioAccuracy.target;
mealSource.defaultCarbsRatioAccuracy.target;
} }
if (mealSource.defaultPortionSizeAccuracy.hasValue) { if (mealSource.defaultPortionSizeAccuracy.hasValue) {
_portionSizeAccuracy = _portionSizeAccuracy = mealSource.defaultPortionSizeAccuracy.target;
mealSource.defaultPortionSizeAccuracy.target;
} }
if (mealSource.defaultMealCategory.hasValue) { if (mealSource.defaultMealCategory.hasValue) {
_mealCategory = mealSource.defaultMealCategory.target; _mealCategory = mealSource.defaultMealCategory.target;
@ -256,10 +227,9 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
bool isNew = widget.meal == null;
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text(isNew ? 'New Meal' : widget.meal!.value), title: Text(_isNew ? 'New Meal' : _meal!.value),
), ),
drawer: const Navigation(currentLocation: MealDetailScreen.routeName), drawer: const Navigation(currentLocation: MealDetailScreen.routeName),
body: SingleChildScrollView( body: SingleChildScrollView(
@ -281,36 +251,33 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
return null; return null;
}, },
), ),
LabeledDropdownButton<MealSource>( AutoCompleteDropdownButton<MealSource>(
selectedItem: _mealSource, selectedItem: _mealSource,
label: 'Meal Source', label: 'Meal Source',
items: _mealSources, items: _mealSources,
// getItemValue: (item) => item.objectId, renderItem: (item) => item.value,
renderItem: (item) => Text(item.value),
onChanged: (value) { onChanged: (value) {
if (value != null) { if (value != null) {
onSelectMealSource(value); onSelectMealSource(value);
} }
}, },
), ),
LabeledDropdownButton<MealCategory>( AutoCompleteDropdownButton<MealCategory>(
selectedItem: _mealCategory, selectedItem: _mealCategory,
label: 'Meal Category', label: 'Meal Category',
items: _mealCategories, items: _mealCategories,
// getItemValue: (item) => item.objectId, renderItem: (item) => item.value,
renderItem: (item) => Text(item.value),
onChanged: (value) { onChanged: (value) {
setState(() { setState(() {
_mealCategory = value; _mealCategory = value;
}); });
}, },
), ),
LabeledDropdownButton<MealPortionType>( AutoCompleteDropdownButton<MealPortionType>(
selectedItem: _mealPortionType, selectedItem: _mealPortionType,
label: 'Meal Portion Type', label: 'Meal Portion Type',
items: _mealPortionTypes, items: _mealPortionTypes,
// getItemValue: (item) => item.objectId, renderItem: (item) => item.value,
renderItem: (item) => Text(item.value),
onChanged: (value) { onChanged: (value) {
setState(() { setState(() {
_mealPortionType = value; _mealPortionType = value;
@ -373,12 +340,11 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
), ),
], ],
), ),
LabeledDropdownButton<Accuracy>( AutoCompleteDropdownButton<Accuracy>(
selectedItem: _portionSizeAccuracy, selectedItem: _portionSizeAccuracy,
label: 'Portion Size Accuracy', label: 'Portion Size Accuracy',
items: _portionSizeAccuracies, items: _portionSizeAccuracies,
// getItemValue: (item) => item.objectId, renderItem: (item) => item.value,
renderItem: (item) => Text(item.value),
onChanged: (value) { onChanged: (value) {
setState(() { setState(() {
_portionSizeAccuracy = value; _portionSizeAccuracy = value;
@ -415,12 +381,11 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
), ),
], ],
), ),
LabeledDropdownButton<Accuracy>( AutoCompleteDropdownButton<Accuracy>(
selectedItem: _carbsRatioAccuracy, selectedItem: _carbsRatioAccuracy,
label: 'Carbs Ratio Accuracy', label: 'Carbs Ratio Accuracy',
items: _carbsRatioAccuracies, items: _carbsRatioAccuracies,
// getItemValue: (item) => item.objectId, renderItem: (item) => item.value,
renderItem: (item) => Text(item.value),
onChanged: (value) { onChanged: (value) {
setState(() { setState(() {
_carbsRatioAccuracy = value; _carbsRatioAccuracy = value;
@ -461,7 +426,7 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
), ),
bottomNavigationBar: DetailBottomRow( bottomNavigationBar: DetailBottomRow(
onCancel: handleCancelAction, onCancel: handleCancelAction,
onSave: isSaving ? null : handleSaveAction, onSave: _isSaving ? null : handleSaveAction,
), ),
); );
} }

View File

@ -1,5 +1,4 @@
import 'package:diameter/components/dialogs.dart'; import 'package:diameter/components/dialogs.dart';
// import 'package:diameter/components/progress_indicator.dart';
import 'package:diameter/config.dart'; import 'package:diameter/config.dart';
import 'package:diameter/models/meal.dart'; import 'package:diameter/models/meal.dart';
import 'package:diameter/navigation.dart'; import 'package:diameter/navigation.dart';
@ -18,7 +17,13 @@ class MealListScreen extends StatefulWidget {
class _MealListScreenState extends State<MealListScreen> { class _MealListScreenState extends State<MealListScreen> {
List<Meal> _meals = []; List<Meal> _meals = [];
void refresh({String? message}) { @override
void initState() {
super.initState();
reload();
}
void reload({String? message}) {
setState(() { setState(() {
_meals = Meal.getAll(); _meals = Meal.getAll();
}); });
@ -37,7 +42,7 @@ class _MealListScreenState extends State<MealListScreen> {
void onDelete(Meal meal) { void onDelete(Meal meal) {
Meal.remove(meal.id); Meal.remove(meal.id);
refresh(message: 'Meal deleted'); reload(message: 'Meal deleted');
} }
void handleDeleteAction(Meal meal) async { void handleDeleteAction(Meal meal) async {
@ -52,17 +57,11 @@ class _MealListScreenState extends State<MealListScreen> {
} }
} }
@override
void initState() {
super.initState();
refresh();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar(title: const Text('Meals'), actions: <Widget>[ appBar: AppBar(title: const Text('Meals'), actions: <Widget>[
IconButton(onPressed: refresh, icon: const Icon(Icons.refresh)) IconButton(onPressed: reload, icon: const Icon(Icons.refresh))
]), ]),
drawer: const Navigation(currentLocation: MealListScreen.routeName), drawer: const Navigation(currentLocation: MealListScreen.routeName),
body: Column( body: Column(
@ -81,9 +80,9 @@ class _MealListScreenState extends State<MealListScreen> {
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => builder: (context) =>
MealDetailScreen(meal: meal), MealDetailScreen(id: meal.id),
), ),
).then((message) => refresh(message: message)); ).then((message) => reload(message: message));
}, },
title: Text(meal.value), title: Text(meal.value),
subtitle: Text(meal.notes ?? ''), subtitle: Text(meal.notes ?? ''),
@ -112,7 +111,7 @@ class _MealListScreenState extends State<MealListScreen> {
MaterialPageRoute( MaterialPageRoute(
builder: (context) => const MealDetailScreen(), builder: (context) => const MealDetailScreen(),
), ),
).then((message) => refresh(message: message)); ).then((message) => reload(message: message));
}, },
child: const Icon(Icons.add), child: const Icon(Icons.add),
), ),

View File

@ -9,10 +9,9 @@ import 'package:diameter/models/meal_portion_type.dart';
class MealPortionTypeDetailScreen extends StatefulWidget { class MealPortionTypeDetailScreen extends StatefulWidget {
static const String routeName = '/meal-portion-type'; static const String routeName = '/meal-portion-type';
final MealPortionType? mealPortionType; final int id;
const MealPortionTypeDetailScreen({Key? key, this.mealPortionType}) const MealPortionTypeDetailScreen({Key? key, this.id = 0}) : super(key: key);
: super(key: key);
@override @override
_MealPortionTypeDetailScreenState createState() => _MealPortionTypeDetailScreenState createState() =>
@ -21,53 +20,56 @@ class MealPortionTypeDetailScreen extends StatefulWidget {
class _MealPortionTypeDetailScreenState class _MealPortionTypeDetailScreenState
extends State<MealPortionTypeDetailScreen> { extends State<MealPortionTypeDetailScreen> {
MealPortionType? _mealPortionType;
bool _isNew = true;
final GlobalKey<FormState> _mealPortionTypeForm = GlobalKey<FormState>(); final GlobalKey<FormState> _mealPortionTypeForm = GlobalKey<FormState>();
final _valueController = TextEditingController(text: ''); final _valueController = TextEditingController(text: '');
final _notesController = TextEditingController(text: ''); final _notesController = TextEditingController(text: '');
@override @override
void initState() { void initState() {
super.initState(); super.initState();
if (widget.mealPortionType != null) { reload();
_valueController.text = widget.mealPortionType!.value;
_notesController.text = widget.mealPortionType!.notes ?? ''; if (_mealPortionType != null) {
_valueController.text = _mealPortionType!.value;
_notesController.text = _mealPortionType!.notes ?? '';
} }
} }
void reload() {
if (widget.id != 0) {
setState(() {
_mealPortionType = MealPortionType.get(widget.id);
});
}
_isNew = _mealPortionType == null;
}
void handleSaveAction() async { void handleSaveAction() async {
if (_mealPortionTypeForm.currentState!.validate()) { if (_mealPortionTypeForm.currentState!.validate()) {
bool isNew = widget.mealPortionType == null;
// isNew
// ? MealPortionType.save(
// value: _valueController.text,
// notes: _notesController.text,
// )
// : MealPortionType.update(
// widget.mealPortionType!.objectId!,
// value: _valueController.text,
// notes: _notesController.text,
// );
MealPortionType.put(MealPortionType( MealPortionType.put(MealPortionType(
id: widget.mealPortionType?.id ?? 0, id: _mealPortionType?.id ?? 0,
value: _valueController.text, value: _valueController.text,
notes: _notesController.text, notes: _notesController.text,
)); ));
Navigator.pop(context, '${isNew ? 'New' : ''} Meal Portion Type saved'); Navigator.pop(context, '${_isNew ? 'New' : ''} Meal Portion Type saved');
} }
} }
void handleCancelAction() { void handleCancelAction() {
bool isNew = widget.mealPortionType == null;
if (showConfirmationDialogOnCancel && if (showConfirmationDialogOnCancel &&
((isNew && ((_isNew &&
(_valueController.text != '' || _notesController.text != '')) || (_valueController.text != '' || _notesController.text != '')) ||
(!isNew && (!_isNew &&
(_valueController.text != widget.mealPortionType!.value || (_valueController.text != _mealPortionType!.value ||
_notesController.text != _notesController.text !=
(widget.mealPortionType!.notes ?? ''))))) { (_mealPortionType!.notes ?? ''))))) {
Dialogs.showCancelConfirmationDialog( Dialogs.showCancelConfirmationDialog(
context: context, context: context,
isNew: isNew, isNew: _isNew,
onSave: handleSaveAction, onSave: handleSaveAction,
); );
} else { } else {
@ -77,11 +79,11 @@ class _MealPortionTypeDetailScreenState
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
bool isNew = widget.mealPortionType == null; bool isNew = _mealPortionType == null;
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text( title: Text(
isNew ? 'New Meal Portion Type' : widget.mealPortionType!.value), isNew ? 'New Meal Portion Type' : _mealPortionType!.value),
), ),
drawer: const Navigation( drawer: const Navigation(
currentLocation: MealPortionTypeDetailScreen.routeName), currentLocation: MealPortionTypeDetailScreen.routeName),

View File

@ -1,5 +1,4 @@
import 'package:diameter/components/dialogs.dart'; import 'package:diameter/components/dialogs.dart';
// import 'package:diameter/components/progress_indicator.dart';
import 'package:diameter/config.dart'; import 'package:diameter/config.dart';
import 'package:diameter/navigation.dart'; import 'package:diameter/navigation.dart';
import 'package:diameter/screens/meal/meal_portion_type_detail.dart'; import 'package:diameter/screens/meal/meal_portion_type_detail.dart';
@ -19,7 +18,13 @@ class MealPortionTypeListScreen extends StatefulWidget {
class _MealPortionTypeListScreenState extends State<MealPortionTypeListScreen> { class _MealPortionTypeListScreenState extends State<MealPortionTypeListScreen> {
List<MealPortionType> _mealPortionTypes = []; List<MealPortionType> _mealPortionTypes = [];
void refresh({String? message}) { @override
void initState() {
super.initState();
reload();
}
void reload({String? message}) {
setState(() { setState(() {
_mealPortionTypes = MealPortionType.getAll(); _mealPortionTypes = MealPortionType.getAll();
}); });
@ -38,7 +43,7 @@ class _MealPortionTypeListScreenState extends State<MealPortionTypeListScreen> {
void onDelete(MealPortionType mealPortionType) { void onDelete(MealPortionType mealPortionType) {
MealPortionType.remove(mealPortionType.id); MealPortionType.remove(mealPortionType.id);
refresh(message: 'Meal Portion Type deleted'); reload(message: 'Meal Portion Type deleted');
} }
void handleDeleteAction(MealPortionType mealPortionType) async { void handleDeleteAction(MealPortionType mealPortionType) async {
@ -53,19 +58,13 @@ class _MealPortionTypeListScreenState extends State<MealPortionTypeListScreen> {
} }
} }
@override
void initState() {
super.initState();
refresh();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: const Text('Meal Portion Types'), title: const Text('Meal Portion Types'),
actions: <Widget>[ actions: <Widget>[
IconButton(onPressed: refresh, icon: const Icon(Icons.refresh)) IconButton(onPressed: reload, icon: const Icon(Icons.refresh))
], ],
), ),
drawer: const Navigation( drawer: const Navigation(
@ -86,12 +85,12 @@ class _MealPortionTypeListScreenState extends State<MealPortionTypeListScreen> {
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => builder: (context) =>
MealPortionTypeDetailScreen( MealPortionTypeDetailScreen(
mealPortionType: mealPortionType, id: mealPortionType.id,
), ),
), ),
).then( ).then(
(message) => refresh(message: message)); (message) => reload(message: message));
}, },
title: Text(mealPortionType.value), title: Text(mealPortionType.value),
subtitle: Text(mealPortionType.notes ?? ''), subtitle: Text(mealPortionType.notes ?? ''),
@ -123,7 +122,7 @@ class _MealPortionTypeListScreenState extends State<MealPortionTypeListScreen> {
MaterialPageRoute( MaterialPageRoute(
builder: (context) => const MealPortionTypeDetailScreen(), builder: (context) => const MealPortionTypeDetailScreen(),
), ),
).then((message) => refresh(message: message)); ).then((message) => reload(message: message));
}, },
child: const Icon(Icons.add), child: const Icon(Icons.add),
), ),

View File

@ -1,28 +1,30 @@
import 'package:diameter/components/detail.dart'; import 'package:diameter/components/detail.dart';
import 'package:diameter/components/dialogs.dart'; import 'package:diameter/components/dialogs.dart';
import 'package:diameter/components/dropdown.dart';
import 'package:diameter/components/forms.dart'; import 'package:diameter/components/forms.dart';
import 'package:diameter/config.dart'; import 'package:diameter/config.dart';
// import 'package:diameter/main.dart';
import 'package:diameter/models/accuracy.dart'; import 'package:diameter/models/accuracy.dart';
import 'package:diameter/models/meal_category.dart'; import 'package:diameter/models/meal_category.dart';
import 'package:diameter/models/meal_portion_type.dart'; import 'package:diameter/models/meal_portion_type.dart';
import 'package:diameter/models/meal_source.dart'; import 'package:diameter/models/meal_source.dart';
import 'package:diameter/navigation.dart'; import 'package:diameter/navigation.dart';
// import 'package:diameter/objectbox.g.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class MealSourceDetailScreen extends StatefulWidget { class MealSourceDetailScreen extends StatefulWidget {
static const String routeName = '/meal-source'; static const String routeName = '/meal-source';
final MealSource? mealSource; final int id;
const MealSourceDetailScreen({Key? key, this.mealSource}) : super(key: key); const MealSourceDetailScreen({Key? key, this.id = 0}) : super(key: key);
@override @override
_MealSourceDetailScreenState createState() => _MealSourceDetailScreenState(); _MealSourceDetailScreenState createState() => _MealSourceDetailScreenState();
} }
class _MealSourceDetailScreenState extends State<MealSourceDetailScreen> { class _MealSourceDetailScreenState extends State<MealSourceDetailScreen> {
MealSource? _mealSource;
bool _isNew = true;
List<Accuracy> _portionSizeAccuracies = []; List<Accuracy> _portionSizeAccuracies = [];
List<Accuracy> _carbsRatioAccuracies = []; List<Accuracy> _carbsRatioAccuracies = [];
List<MealCategory> _mealCategories = []; List<MealCategory> _mealCategories = [];
@ -41,52 +43,40 @@ class _MealSourceDetailScreenState extends State<MealSourceDetailScreen> {
void initState() { void initState() {
super.initState(); super.initState();
reload();
_portionSizeAccuracies = Accuracy.getAllForPortionSize(); _portionSizeAccuracies = Accuracy.getAllForPortionSize();
_carbsRatioAccuracies = Accuracy.getAllForCarbsRatio(); _carbsRatioAccuracies = Accuracy.getAllForCarbsRatio();
_mealCategories = MealCategory.getAll(); _mealCategories = MealCategory.getAll();
_mealPortionTypes = MealPortionType.getAll(); _mealPortionTypes = MealPortionType.getAll();
if (widget.mealSource != null) { if (_mealSource != null) {
_valueController.text = widget.mealSource!.value; _valueController.text = _mealSource!.value;
_notesController.text = widget.mealSource!.notes ?? ''; _notesController.text = _mealSource!.notes ?? '';
_defaultPortionSizeAccuracy = _defaultPortionSizeAccuracy =
widget.mealSource!.defaultPortionSizeAccuracy.target; _mealSource!.defaultPortionSizeAccuracy.target;
_defaultCarbsRatioAccuracy = widget.mealSource!.defaultCarbsRatioAccuracy.target; _defaultCarbsRatioAccuracy =
_mealSource!.defaultCarbsRatioAccuracy.target;
_defaultMealCategory = widget.mealSource!.defaultMealCategory.target; _defaultMealCategory = _mealSource!.defaultMealCategory.target;
_defaultMealPortionType = _defaultMealPortionType =
widget.mealSource!.defaultMealPortionType.target; _mealSource!.defaultMealPortionType.target;
} }
} }
void reload() {
if (widget.id != 0) {
setState(() {
_mealSource = MealSource.get(widget.id);
});
}
_isNew = _mealSource == null;
}
void handleSaveAction() async { void handleSaveAction() async {
bool isNew = widget.mealSource == null; MealSource mealSource = MealSource(
if (_mealSourceForm.currentState!.validate()) { id: widget.id,
// isNew
// ? await MealSource.save(
// value: _valueController.text,
// defaultCarbsRatioAccuracy: _defaultCarbsRatioAccuracy?.id.toString(),
// defaultPortionSizeAccuracy: _defaultPortionSizeAccuracy?.id.toString(),
// // defaultCarbsRatioAccuracy: _defaultCarbsRatioAccuracy,
// // defaultPortionSizeAccuracy: _defaultPortionSizeAccuracy,
// defaultMealCategory: _defaultMealCategory,
// defaultMealPortionType: _defaultMealPortionType,
// notes: _notesController.text,
// )
// : await MealSource.update(
// widget.mealSource!.objectId!,
// value: _valueController.text,
// defaultCarbsRatioAccuracy: _defaultCarbsRatioAccuracy?.id.toString(),
// defaultPortionSizeAccuracy: _defaultPortionSizeAccuracy?.id.toString(),
// // defaultCarbsRatioAccuracy: _defaultCarbsRatioAccuracy,
// // defaultPortionSizeAccuracy: _defaultPortionSizeAccuracy,
// defaultMealCategory: _defaultMealCategory,
// defaultMealPortionType: _defaultMealPortionType,
// notes: _notesController.text,
// );
MealSource mealSource = MealSource(
id: widget.mealSource?.id ?? 0,
value: _valueController.text, value: _valueController.text,
notes: _notesController.text, notes: _notesController.text,
); );
@ -96,35 +86,33 @@ class _MealSourceDetailScreenState extends State<MealSourceDetailScreen> {
mealSource.defaultMealCategory.target = _defaultMealCategory; mealSource.defaultMealCategory.target = _defaultMealCategory;
mealSource.defaultMealPortionType.target = _defaultMealPortionType; mealSource.defaultMealPortionType.target = _defaultMealPortionType;
MealSource.put(mealSource); MealSource.put(mealSource);
Navigator.pop(context, '${isNew ? 'New' : ''} Meal Source saved'); Navigator.pop(context, '${_isNew ? 'New' : ''} Meal Source saved');
}
} }
void handleCancelAction() { void handleCancelAction() {
bool isNew = widget.mealSource == null;
if (showConfirmationDialogOnCancel && if (showConfirmationDialogOnCancel &&
((isNew && ((_isNew &&
(_valueController.text != '' || (_valueController.text != '' ||
_defaultCarbsRatioAccuracy != null || _defaultCarbsRatioAccuracy != null ||
_defaultPortionSizeAccuracy != null || _defaultPortionSizeAccuracy != null ||
_defaultMealCategory != null || _defaultMealCategory != null ||
_defaultMealPortionType != null || _defaultMealPortionType != null ||
_notesController.text != '')) || _notesController.text != '')) ||
(!isNew && (!_isNew &&
(_valueController.text != widget.mealSource!.value || (_valueController.text != _mealSource!.value ||
_defaultCarbsRatioAccuracy != _defaultCarbsRatioAccuracy !=
widget.mealSource!.defaultCarbsRatioAccuracy.target || _mealSource!.defaultCarbsRatioAccuracy.target ||
_defaultPortionSizeAccuracy != _defaultPortionSizeAccuracy !=
widget.mealSource!.defaultPortionSizeAccuracy.target || _mealSource!.defaultPortionSizeAccuracy.target ||
_defaultMealCategory != _defaultMealCategory !=
widget.mealSource!.defaultMealCategory.target || _mealSource!.defaultMealCategory.target ||
_defaultMealPortionType != _defaultMealPortionType !=
widget.mealSource!.defaultMealPortionType.target || _mealSource!.defaultMealPortionType.target ||
_notesController.text != _notesController.text !=
(widget.mealSource!.notes ?? ''))))) { (_mealSource!.notes ?? ''))))) {
Dialogs.showCancelConfirmationDialog( Dialogs.showCancelConfirmationDialog(
context: context, context: context,
isNew: isNew, isNew: _isNew,
onSave: handleSaveAction, onSave: handleSaveAction,
); );
} else { } else {
@ -134,10 +122,9 @@ class _MealSourceDetailScreenState extends State<MealSourceDetailScreen> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
bool isNew = widget.mealSource == null;
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: Text(isNew ? 'New Meal Source' : widget.mealSource!.value), title: Text(_isNew ? 'New Meal Source' : _mealSource!.value),
), ),
drawer: drawer:
const Navigation(currentLocation: MealSourceDetailScreen.routeName), const Navigation(currentLocation: MealSourceDetailScreen.routeName),
@ -160,70 +147,44 @@ class _MealSourceDetailScreenState extends State<MealSourceDetailScreen> {
return null; return null;
}, },
), ),
LabeledDropdownButton<Accuracy>( AutoCompleteDropdownButton<Accuracy>(
selectedItem: _defaultCarbsRatioAccuracy, selectedItem: _defaultCarbsRatioAccuracy,
label: 'Default Carbs Ratio Accuracy', label: 'Default Carbs Ratio Accuracy',
items: _carbsRatioAccuracies, items: _carbsRatioAccuracies,
renderItem: (item) => Text(item.value), renderItem: (item) => item.value,
onChanged: (value) { onChanged: (value) {
setState(() { setState(() {
_defaultCarbsRatioAccuracy = value; _defaultCarbsRatioAccuracy = value;
}); });
}, },
), ),
LabeledDropdownButton<Accuracy>( AutoCompleteDropdownButton<Accuracy>(
selectedItem: _defaultPortionSizeAccuracy, selectedItem: _defaultPortionSizeAccuracy,
label: 'Default Portion Size Accuracy', label: 'Default Portion Size Accuracy',
items: _portionSizeAccuracies, items: _portionSizeAccuracies,
renderItem: (item) => Text(item.value), renderItem: (item) => item.value,
onChanged: (value) { onChanged: (value) {
setState(() { setState(() {
_defaultPortionSizeAccuracy = value; _defaultPortionSizeAccuracy = value;
}); });
}, },
), ),
// StyledFutureDropdownButton<Accuracy>( AutoCompleteDropdownButton<MealCategory>(
// selectedItem: _defaultCarbsRatioAccuracy,
// label: 'Default Carbs Ratio Accuracy',
// items: _carbsRatioAccuracies,
// getItemValue: (item) => item.objectId,
// renderItem: (item) => Text(item.value),
// onChanged: (value) {
// setState(() {
// _defaultCarbsRatioAccuracy = value;
// });
// },
// ),
// StyledFutureDropdownButton<Accuracy>(
// selectedItem: _defaultPortionSizeAccuracy,
// label: 'Default Portion Size Accuracy',
// items: _portionSizeAccuracies,
// getItemValue: (item) => item.objectId,
// renderItem: (item) => Text(item.value),
// onChanged: (value) {
// setState(() {
// _defaultPortionSizeAccuracy = value;
// });
// },
// ),
LabeledDropdownButton<MealCategory>(
selectedItem: _defaultMealCategory, selectedItem: _defaultMealCategory,
label: 'Default Meal Category', label: 'Default Meal Category',
items: _mealCategories, items: _mealCategories,
// getItemValue: (item) => item.objectId, renderItem: (item) => item.value,
renderItem: (item) => Text(item.value),
onChanged: (value) { onChanged: (value) {
setState(() { setState(() {
_defaultMealCategory = value; _defaultMealCategory = value;
}); });
}, },
), ),
LabeledDropdownButton<MealPortionType>( AutoCompleteDropdownButton<MealPortionType>(
selectedItem: _defaultMealPortionType, selectedItem: _defaultMealPortionType,
label: 'Default Meal Portion Type', label: 'Default Meal Portion Type',
items: _mealPortionTypes, items: _mealPortionTypes,
// getItemValue: (item) => item.objectId, renderItem: (item) => item.value,
renderItem: (item) => Text(item.value),
onChanged: (value) { onChanged: (value) {
setState(() { setState(() {
_defaultMealPortionType = value; _defaultMealPortionType = value;

View File

@ -1,4 +1,3 @@
// import 'package:diameter/components/progress_indicator.dart';
import 'package:diameter/components/dialogs.dart'; import 'package:diameter/components/dialogs.dart';
import 'package:diameter/config.dart'; import 'package:diameter/config.dart';
import 'package:diameter/models/meal_source.dart'; import 'package:diameter/models/meal_source.dart';
@ -18,7 +17,13 @@ class MealSourceListScreen extends StatefulWidget {
class _MealSourceListScreenState extends State<MealSourceListScreen> { class _MealSourceListScreenState extends State<MealSourceListScreen> {
List<MealSource> _mealSources = []; List<MealSource> _mealSources = [];
void refresh({String? message}) { @override
void initState() {
super.initState();
reload();
}
void reload({String? message}) {
setState(() { setState(() {
_mealSources = MealSource.getAll(); _mealSources = MealSource.getAll();
}); });
@ -37,7 +42,7 @@ class _MealSourceListScreenState extends State<MealSourceListScreen> {
void onDelete(MealSource mealSource) { void onDelete(MealSource mealSource) {
MealSource.remove(mealSource.id); MealSource.remove(mealSource.id);
refresh(message: 'Meal Source deleted'); reload(message: 'Meal Source deleted');
} }
void handleDeleteAction(MealSource mealSource) async { void handleDeleteAction(MealSource mealSource) async {
@ -52,19 +57,13 @@ class _MealSourceListScreenState extends State<MealSourceListScreen> {
} }
} }
@override
void initState() {
super.initState();
refresh();
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
title: const Text('Meal Sources'), title: const Text('Meal Sources'),
actions: <Widget>[ actions: <Widget>[
IconButton(onPressed: refresh, icon: const Icon(Icons.refresh)) IconButton(onPressed: reload, icon: const Icon(Icons.refresh))
], ],
), ),
drawer: const Navigation(currentLocation: MealSourceListScreen.routeName), drawer: const Navigation(currentLocation: MealSourceListScreen.routeName),
@ -84,10 +83,10 @@ class _MealSourceListScreenState extends State<MealSourceListScreen> {
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => MealSourceDetailScreen( builder: (context) => MealSourceDetailScreen(
mealSource: mealSource, id: mealSource.id,
), ),
), ),
).then((message) => refresh(message: message)); ).then((message) => reload(message: message));
}, },
title: Text(mealSource.value), title: Text(mealSource.value),
subtitle: Text(mealSource.notes ?? ''), subtitle: Text(mealSource.notes ?? ''),
@ -120,7 +119,7 @@ class _MealSourceListScreenState extends State<MealSourceListScreen> {
MaterialPageRoute( MaterialPageRoute(
builder: (context) => const MealSourceDetailScreen(), builder: (context) => const MealSourceDetailScreen(),
), ),
).then((message) => refresh(message: message)); ).then((message) => reload(message: message));
}, },
child: const Icon(Icons.add), child: const Icon(Icons.add),
), ),

View File

@ -1,4 +1,5 @@
import 'package:diameter/components/dialogs.dart'; import 'package:diameter/components/dialogs.dart';
import 'package:diameter/components/dropdown.dart';
import 'package:diameter/components/forms.dart'; import 'package:diameter/components/forms.dart';
import 'package:diameter/config.dart'; import 'package:diameter/config.dart';
import 'package:diameter/navigation.dart'; import 'package:diameter/navigation.dart';
@ -130,11 +131,11 @@ class _SettingsScreenState extends State<SettingsScreen> {
FormWrapper( FormWrapper(
formState: _settingsForm, formState: _settingsForm,
fields: [ fields: [
LabeledDropdownButton<NutritionMeasurement>( AutoCompleteDropdownButton<NutritionMeasurement>(
selectedItem: nutritionMeasurement, selectedItem: nutritionMeasurement,
label: 'Preferred Nutrition Measurement', label: 'Preferred Nutrition Measurement',
items: NutritionMeasurement.values, items: NutritionMeasurement.values,
renderItem: (item) => Text(item.toString().split('.')[1]), renderItem: (item) => item.toString().split('.')[1],
onChanged: (value) { onChanged: (value) {
if (value != null) { if (value != null) {
Settings.setNutritionMeasurement(value); Settings.setNutritionMeasurement(value);
@ -144,11 +145,11 @@ class _SettingsScreenState extends State<SettingsScreen> {
} }
}, },
), ),
LabeledDropdownButton<GlucoseMeasurement>( AutoCompleteDropdownButton<GlucoseMeasurement>(
selectedItem: glucoseMeasurement, selectedItem: glucoseMeasurement,
label: 'Preferred Glucose Measurement', label: 'Preferred Glucose Measurement',
items: GlucoseMeasurement.values, items: GlucoseMeasurement.values,
renderItem: (item) => Text(item.toString().split('.')[1]), renderItem: (item) => item.toString().split('.')[1],
onChanged: (value) { onChanged: (value) {
if (value != null) { if (value != null) {
Settings.setGlucoseMeasurement(value); Settings.setGlucoseMeasurement(value);