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:
☐ add active/deleted flag to all data models
☐ account for deleted/disabled elements in dropdowns
☐ place dropdown items right below their input
✔ use local database instead of back4app @done(21-11-07 18:53)
☐ find a general way to deal with duration fields
☐ add explanations to each section
☐ use ids instead of passing entities around where possible
MAIN TASKS:
General/Framework:
☐ add active/deleted flag to all data models
☐ find a general way to deal with duration fields
Accuracies:
☐ implement reordering
Accuracies:
☐ implement reordering
Basal/Bolus:
☐ create distinct visual mode for picking the active profile
Basal/Bolus:
☐ create distinct visual mode for picking the active profile
Log Overview:
☐ add active events view (as main menu item?)
☐ adjust/debug active events view
☐ display more information
☐ apply target color settings to glucose
☐ total bolus and carbs per entry
Meal:
none
Log Entry:
☐ replace meal and glucose boli with logbolus entities
☐ fix logmeals/logboli/logevents (probably use queries for associated items instead of onetomany)
Log Overview:
☐ add active events view
☐ adjust/debug active events view
☐ display more information
☐ apply target color settings to glucose
☐ total bolus and carbs per entry
Event Types:
☐ add option to change bolus/basal profile for event duration
Log Entry:
☐ add tab for bolus overview
☐ calculate bolus suggestions according to active profile
☐ add time picker for entry date/time
Settings:
☐ add objectbox class and use instead of shared preferences
☐ add fields for date and time formats
☐ add fields for glucose target
Event Types:
☐ add option to change bolus/basal profile for event duration
FUTURE TASKS:
General/Framework:
☐ add explanations to each section
☐ account for deleted/disabled elements in dropdowns
☐ hide dropdown overlay on tapping anywhere else (especially menu)
☐ add clear button to dropdown (or all text fields?)
Log Overview:
☐ add pagination
Settings:
☐ add objectbox class and use instead of shared preferences
☐ add fields for date and time formats
☐ add fields for glucose target
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._();
static ThemeData lightTheme = FlexColorScheme.light(
surfaceStyle: FlexSurface.medium,
scheme: FlexScheme.mandyRed,
fontFamily: 'RobotoCondensed',
).toTheme;
@ -16,8 +17,7 @@ class AppTheme {
static ThemeData makeTheme(ThemeData baseThemeData) {
return baseThemeData.copyWith(
visualDensity: VisualDensity.compact,
bottomNavigationBarTheme: BottomNavigationBarThemeData(
backgroundColor: baseThemeData.primaryColor));
bottomNavigationBarTheme: BottomNavigationBarThemeData(
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> {
@override
Widget build(BuildContext context) {
return FormField<bool>(builder: (context) {
return FormField<bool>(builder: (state) {
return ListTile(
onTap: () => widget.onChanged(!widget.value),
trailing: Switch(
@ -98,8 +98,7 @@ class DateTimeFormField extends StatefulWidget {
: super(key: key);
@override
_DateTimeFormFieldState createState() =>
_DateTimeFormFieldState();
_DateTimeFormFieldState createState() => _DateTimeFormFieldState();
}
class _DateTimeFormFieldState extends State<DateTimeFormField> {
@ -115,7 +114,7 @@ class _DateTimeFormFieldState extends State<DateTimeFormField> {
final newTime = await showDatePicker(
context: context,
initialDate: widget.date,
firstDate: widget.minDate ?? DateTime(2000,1,1),
firstDate: widget.minDate ?? DateTime(2000, 1, 1),
lastDate: widget.maxDate ?? DateTime.now(),
);
widget.onChanged(newTime);
@ -139,8 +138,7 @@ class TimeOfDayFormField extends StatefulWidget {
: super(key: key);
@override
_TimeOfDayFormFieldState createState() =>
_TimeOfDayFormFieldState();
_TimeOfDayFormFieldState createState() => _TimeOfDayFormFieldState();
}
class _TimeOfDayFormFieldState extends State<TimeOfDayFormField> {
@ -163,42 +161,3 @@ class _TimeOfDayFormFieldState extends State<TimeOfDayFormField> {
}
}
class LabeledDropdownButton<T> extends StatefulWidget {
final String label;
final T? selectedItem;
final List<T> items;
final Widget Function(T item) renderItem;
final void Function(T? value) onChanged;
const LabeledDropdownButton(
{Key? key,
this.selectedItem,
required this.label,
required this.items,
required this.renderItem,
required this.onChanged})
: super(key: key);
@override
_LabeledDropdownButtonState<T> createState() => _LabeledDropdownButtonState();
}
class _LabeledDropdownButtonState<T> extends State<LabeledDropdownButton<T>> {
@override
Widget build(BuildContext context) {
return DropdownButtonFormField<T>(
decoration: InputDecoration(
labelText: widget.label,
),
value: widget.selectedItem,
onChanged: widget.onChanged,
items: widget.items
.map((item) => DropdownMenuItem<T>(
value: item,
child: widget.renderItem(item),
))
.toList(),
);
}
}

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/bolus/bolus_profile_detail.dart';
import 'package:diameter/screens/log/log.dart';
import 'package:diameter/screens/log/log_entry.dart';
import 'package:diameter/screens/log/log_event_detail.dart';
import 'package:diameter/screens/log/log_entry/log_entry.dart';
import 'package:diameter/screens/log/log_entry/log_event_detail.dart';
import 'package:diameter/screens/log/log_event_type_detail.dart';
import 'package:diameter/screens/log/log_event_type_list.dart';
import 'package:diameter/screens/meal/meal_category_detail.dart';

View File

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

View File

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

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

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_list.dart';
import 'package:diameter/screens/log/log.dart';
import 'package:diameter/screens/log/log_entry.dart';
import 'package:diameter/screens/log/log_event_detail.dart';
import 'package:diameter/screens/log/log_entry/log_entry.dart';
import 'package:diameter/screens/log/log_entry/log_event_detail.dart';
import 'package:diameter/screens/log/log_event_type_detail.dart';
import 'package:diameter/screens/log/log_event_type_list.dart';
import 'package:diameter/screens/log/log_meal_detail.dart';
import 'package:diameter/screens/log/log_entry/log_meal_detail.dart';
import 'package:diameter/screens/meal/meal_category_detail.dart';
import 'package:diameter/screens/meal/meal_category_list.dart';
import 'package:diameter/screens/meal/meal_detail.dart';

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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