518 lines
20 KiB
Dart
518 lines
20 KiB
Dart
import 'package:diameter/components/detail.dart';
|
|
import 'package:diameter/components/forms/boolean_form_field.dart';
|
|
import 'package:diameter/components/forms/date_time_form_field.dart';
|
|
import 'package:diameter/components/forms/time_of_day_form_field.dart';
|
|
import 'package:diameter/localization_keys.dart';
|
|
import 'package:diameter/utils/dialog_utils.dart';
|
|
import 'package:diameter/components/forms/auto_complete_dropdown_button.dart';
|
|
import 'package:diameter/components/forms/form_wrapper.dart';
|
|
import 'package:diameter/models/basal_profile.dart';
|
|
import 'package:diameter/models/bolus_profile.dart';
|
|
import 'package:diameter/models/log_event.dart';
|
|
import 'package:diameter/models/log_event_type.dart';
|
|
import 'package:diameter/models/settings.dart';
|
|
import 'package:diameter/navigation.dart';
|
|
import 'package:diameter/screens/basal/basal_profile_detail.dart';
|
|
import 'package:diameter/screens/bolus/bolus_profile_detail.dart';
|
|
import 'package:diameter/utils/date_time_utils.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_translate/flutter_translate.dart';
|
|
|
|
class LogEventDetailScreen extends StatefulWidget {
|
|
static const String routeName = '/log-event';
|
|
|
|
final int logEntryId;
|
|
final int endLogEntryId;
|
|
final int id;
|
|
final DateTime? suggestedDate;
|
|
|
|
const LogEventDetailScreen(
|
|
{Key? key, this.logEntryId = 0, this.endLogEntryId = 0, this.id = 0, this.suggestedDate})
|
|
: super(key: key);
|
|
|
|
@override
|
|
_LogEventDetailScreenState createState() => _LogEventDetailScreenState();
|
|
}
|
|
|
|
class _LogEventDetailScreenState extends State<LogEventDetailScreen> {
|
|
LogEvent? _logEvent;
|
|
bool _isNew = true;
|
|
bool _isSaving = false;
|
|
|
|
List<BolusProfile> _bolusProfiles = [];
|
|
List<BasalProfile> _basalProfiles = [];
|
|
|
|
final GlobalKey<FormState> _logEventForm = GlobalKey<FormState>();
|
|
final ScrollController _scrollController = ScrollController();
|
|
|
|
late DateTime _time;
|
|
DateTime? _endTime;
|
|
|
|
final _timeController = TextEditingController(text: '');
|
|
final _endTimeController = TextEditingController(text: '');
|
|
|
|
final _dateController = TextEditingController(text: '');
|
|
final _endDateController = TextEditingController(text: '');
|
|
|
|
final _reminderDurationController = TextEditingController(text: '');
|
|
final _notesController = TextEditingController(text: '');
|
|
|
|
LogEventType? _eventType;
|
|
final _eventTypeController = TextEditingController(text: '');
|
|
|
|
bool _hasEndTime = false;
|
|
|
|
BolusProfile? _bolusProfile;
|
|
BasalProfile? _basalProfile;
|
|
final _bolusProfileController = TextEditingController(text: '');
|
|
final _basalProfileController = TextEditingController(text: '');
|
|
|
|
List<LogEventType> _logEventTypes = [];
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
|
|
reload();
|
|
|
|
_bolusProfiles = BolusProfile.getAll();
|
|
_basalProfiles = BasalProfile.getAll();
|
|
|
|
if (widget.id != 0) {
|
|
_reminderDurationController.text =
|
|
(_logEvent!.reminderDuration ?? '').toString();
|
|
_hasEndTime = _logEvent!.hasEndTime;
|
|
_notesController.text = _logEvent!.notes ?? '';
|
|
|
|
_eventType = _logEvent!.eventType.target;
|
|
_eventTypeController.text = (_eventType ?? '').toString();
|
|
|
|
_basalProfile = _logEvent!.basalProfile.target;
|
|
_basalProfileController.text = (_basalProfile ?? '').toString();
|
|
|
|
_bolusProfile = _logEvent!.bolusProfile.target;
|
|
_bolusProfileController.text = (_bolusProfile ?? '').toString();
|
|
|
|
_time = _logEvent!.time;
|
|
_endTime = _logEvent!.endTime;
|
|
} else {
|
|
_time = widget.suggestedDate ?? DateTime.now();
|
|
}
|
|
|
|
_logEventTypes = LogEventType.getAll();
|
|
|
|
updateTime();
|
|
updateEndTime();
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
_scrollController.dispose();
|
|
_timeController.dispose();
|
|
_endTimeController.dispose();
|
|
_dateController.dispose();
|
|
_endDateController.dispose();
|
|
_reminderDurationController.dispose();
|
|
_notesController.dispose();
|
|
_eventTypeController.dispose();
|
|
_bolusProfileController.dispose();
|
|
_basalProfileController.dispose();
|
|
super.dispose();
|
|
}
|
|
|
|
void reload({String? message}) {
|
|
if (widget.id != 0) {
|
|
setState(() {
|
|
_logEvent = LogEvent.get(widget.id);
|
|
});
|
|
}
|
|
_isNew = _logEvent == null;
|
|
|
|
setState(() {
|
|
if (message != null) {
|
|
var snackBar = SnackBar(
|
|
content: Text(message),
|
|
duration: const Duration(seconds: 2),
|
|
);
|
|
ScaffoldMessenger.of(context)
|
|
..removeCurrentSnackBar()
|
|
..showSnackBar(snackBar);
|
|
}
|
|
});
|
|
}
|
|
|
|
void updateTime() {
|
|
_timeController.text = DateTimeUtils.displayTime(_time);
|
|
_dateController.text = DateTimeUtils.displayDate(_time);
|
|
}
|
|
|
|
void updateEndTime() {
|
|
_endTimeController.text = DateTimeUtils.displayTime(_endTime);
|
|
_endDateController.text = DateTimeUtils.displayDate(_endTime);
|
|
}
|
|
|
|
void updateBasalProfile(BasalProfile? value) {
|
|
setState(() {
|
|
_basalProfile = value;
|
|
_basalProfileController.text = (_basalProfile ?? '').toString();
|
|
});
|
|
}
|
|
|
|
void updateBolusProfile(BolusProfile? value) {
|
|
setState(() {
|
|
_bolusProfile = value;
|
|
_bolusProfileController.text = (_bolusProfile ?? '').toString();
|
|
});
|
|
}
|
|
|
|
void onSelectEventType(LogEventType? eventType) {
|
|
setState(() {
|
|
_eventType = eventType;
|
|
_eventTypeController.text = (_eventType ?? '').toString();
|
|
});
|
|
|
|
if (eventType != null) {
|
|
setState(() {
|
|
_hasEndTime = eventType.hasEndTime;
|
|
if (eventType.defaultReminderDuration != null) {
|
|
_reminderDurationController.text =
|
|
eventType.defaultReminderDuration.toString();
|
|
}
|
|
});
|
|
|
|
if (eventType.basalProfile.target != null) {
|
|
updateBasalProfile(eventType.basalProfile.target);
|
|
}
|
|
if (eventType.bolusProfile.target != null) {
|
|
updateBolusProfile(eventType.bolusProfile.target);
|
|
}
|
|
}
|
|
}
|
|
|
|
Future<void> checkIfActiveEventOfTypeExistsBeforeSaving() async {
|
|
if (_eventType != null &&
|
|
LogEvent.eventTypeExistsForTime(_eventType!.id, _time)) {
|
|
await showDialog(
|
|
context: context,
|
|
builder: (BuildContext context) {
|
|
return AlertDialog(
|
|
content: Text(translate(LocalizationKeys.event_warnings_duplicate)),
|
|
actions: [
|
|
TextButton(
|
|
onPressed: () => Navigator.pop(context, 'DISCARD'),
|
|
child: Text(translate(LocalizationKeys.general_discard).toUpperCase()),
|
|
),
|
|
TextButton(
|
|
onPressed: () => Navigator.pop(context, 'EDIT'),
|
|
child: Text(translate(LocalizationKeys.general_keepEditing).toUpperCase()),
|
|
),
|
|
ElevatedButton(
|
|
onPressed: () => Navigator.pop(context, 'SAVE'),
|
|
child: Text(translate(LocalizationKeys.general_save).toUpperCase()),
|
|
)
|
|
],
|
|
);
|
|
}).then((value) async {
|
|
if (value == 'DISCARD') {
|
|
Navigator.pop(context);
|
|
} else if (value == 'SAVE') {
|
|
onSave();
|
|
}
|
|
});
|
|
} else {
|
|
onSave();
|
|
}
|
|
}
|
|
|
|
void onSave() {
|
|
LogEvent event = LogEvent(
|
|
id: widget.id,
|
|
time: _time,
|
|
endTime: _endTime,
|
|
hasEndTime: _hasEndTime,
|
|
reminderDuration: int.tryParse(_reminderDurationController.text),
|
|
notes: _notesController.text,
|
|
);
|
|
event.eventType.target = _eventType;
|
|
event.basalProfile.target = _basalProfile;
|
|
event.bolusProfile.target = _bolusProfile;
|
|
LogEvent.put(event);
|
|
Navigator.pop(context, [translate(LocalizationKeys.event_saved, args: {
|
|
"status": _isNew ? LocalizationKeys.event_new : "",
|
|
}), event]);
|
|
}
|
|
|
|
void handleSaveAction() async {
|
|
setState(() {
|
|
_isSaving = true;
|
|
});
|
|
if (_logEventForm.currentState!.validate()) {
|
|
await checkIfActiveEventOfTypeExistsBeforeSaving();
|
|
}
|
|
setState(() {
|
|
_isSaving = false;
|
|
});
|
|
}
|
|
|
|
void handleCancelAction() {
|
|
if (Settings.get().showConfirmationDialogOnCancel &&
|
|
((_isNew &&
|
|
(_notesController.text != '' ||
|
|
_eventType != null ||
|
|
_hasEndTime)) ||
|
|
(!_isNew &&
|
|
(_notesController.text != (_logEvent!.notes ?? '') ||
|
|
_eventType != _logEvent!.eventType.target ||
|
|
_hasEndTime != _logEvent!.hasEndTime)))) {
|
|
DialogUtils.showCancelConfirmationDialog(
|
|
context: context,
|
|
isNew: _isNew,
|
|
onSave: handleSaveAction,
|
|
);
|
|
} else {
|
|
Navigator.pop(context);
|
|
}
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final now = DateTime.now();
|
|
return Scaffold(
|
|
appBar: AppBar(
|
|
title: Text(translate(LocalizationKeys.event_saved, args: {
|
|
"status": _isNew ? LocalizationKeys.event_new : LocalizationKeys.general_edit,
|
|
"name": _isNew ? '' : _logEvent?.eventType.target?.value
|
|
})),
|
|
),
|
|
drawer: const Navigation(currentLocation: LogEventDetailScreen.routeName),
|
|
body: Scrollbar(
|
|
controller: _scrollController,
|
|
child: SingleChildScrollView(
|
|
controller: _scrollController,
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
|
children: <Widget>[
|
|
FormWrapper(
|
|
formState: _logEventForm,
|
|
fields: [
|
|
AutoCompleteDropdownButton<LogEventType>(
|
|
controller: _eventTypeController,
|
|
selectedItem: _eventType,
|
|
label: translate(LocalizationKeys.event_fields_eventType),
|
|
items: _logEventTypes,
|
|
onChanged: onSelectEventType,
|
|
),
|
|
Row(
|
|
children: [
|
|
Expanded(
|
|
child: Padding(
|
|
padding: const EdgeInsets.only(right: 5),
|
|
child: DateTimeFormField(
|
|
date: _time,
|
|
label: translate(_hasEndTime ? LocalizationKeys.event_fields_startDate : LocalizationKeys.event_fields_date),
|
|
controller: _dateController,
|
|
onChanged: (newTime) {
|
|
if (newTime != null) {
|
|
setState(() {
|
|
_time = DateTime(newTime.year, newTime.month,
|
|
newTime.day, _time.hour, _time.minute);
|
|
});
|
|
updateTime();
|
|
}
|
|
},
|
|
),
|
|
),
|
|
),
|
|
Expanded(
|
|
child: Padding(
|
|
padding: const EdgeInsets.only(left: 5),
|
|
child: TimeOfDayFormField(
|
|
time: TimeOfDay.fromDateTime(_time),
|
|
label: translate(_hasEndTime ? LocalizationKeys.event_fields_startTime : LocalizationKeys.event_fields_time),
|
|
controller: _timeController,
|
|
onChanged: (newTime) {
|
|
if (newTime != null) {
|
|
setState(() {
|
|
_time = DateTime(_time.year, _time.month,
|
|
_time.day, newTime.hour, newTime.minute);
|
|
});
|
|
updateTime();
|
|
}
|
|
},
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
BooleanFormField(
|
|
value: _hasEndTime,
|
|
onChanged: (value) {
|
|
setState(() {
|
|
_hasEndTime = value;
|
|
});
|
|
},
|
|
label: translate(LocalizationKeys.event_fields_hasEndTime),
|
|
),
|
|
Column(
|
|
children: _hasEndTime
|
|
? [
|
|
Row(
|
|
children: [
|
|
Expanded(
|
|
child: Padding(
|
|
padding: const EdgeInsets.only(right: 5),
|
|
child: DateTimeFormField(
|
|
date: _endTime ?? now,
|
|
label: translate(LocalizationKeys.event_fields_endDate),
|
|
controller: _endDateController,
|
|
onChanged: (newTime) {
|
|
if (newTime != null) {
|
|
setState(() {
|
|
_endTime = DateTime(
|
|
newTime.year,
|
|
newTime.month,
|
|
newTime.day,
|
|
_endTime?.hour ?? 0,
|
|
_endTime?.minute ?? 0);
|
|
});
|
|
updateEndTime();
|
|
}
|
|
},
|
|
),
|
|
),
|
|
),
|
|
Expanded(
|
|
child: Padding(
|
|
padding: const EdgeInsets.only(left: 5),
|
|
child: TimeOfDayFormField(
|
|
time: TimeOfDay.fromDateTime(
|
|
_endTime ?? now),
|
|
label: translate(LocalizationKeys.event_fields_endTime),
|
|
controller: _endTimeController,
|
|
onChanged: (newTime) {
|
|
if (newTime != null) {
|
|
setState(() {
|
|
_endTime = DateTime(
|
|
_endTime?.year ?? now.year,
|
|
_endTime?.month ?? now.month,
|
|
_endTime?.day ?? now.day,
|
|
newTime.hour,
|
|
newTime.minute);
|
|
});
|
|
updateEndTime();
|
|
}
|
|
},
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
Padding(
|
|
padding: const EdgeInsets.symmetric(vertical: 10.0),
|
|
child: TextFormField(
|
|
controller: _reminderDurationController,
|
|
keyboardType:
|
|
const TextInputType.numberWithOptions(),
|
|
decoration: InputDecoration(
|
|
labelText: translate(LocalizationKeys.event_fields_reminderDuration),
|
|
suffixText: translate(LocalizationKeys.general_suffixes_mins),
|
|
enabled: _hasEndTime,
|
|
),
|
|
),
|
|
),
|
|
Row(
|
|
children: [
|
|
Expanded(
|
|
child: AutoCompleteDropdownButton<
|
|
BolusProfile>(
|
|
controller: _bolusProfileController,
|
|
selectedItem: _bolusProfile,
|
|
label: translate(LocalizationKeys.event_fields_bolusProfile),
|
|
items: _bolusProfiles,
|
|
onChanged: updateBolusProfile,
|
|
),
|
|
),
|
|
IconButton(
|
|
onPressed: () {
|
|
Navigator.push(
|
|
context,
|
|
MaterialPageRoute(
|
|
builder: (context) => _bolusProfile ==
|
|
null
|
|
? const BolusProfileDetailScreen()
|
|
: BolusProfileDetailScreen(
|
|
id: _basalProfile!.id),
|
|
),
|
|
).then((result) {
|
|
updateBolusProfile(result?[1]);
|
|
reload(message: result?[0]);
|
|
});
|
|
},
|
|
icon: Icon(_bolusProfile == null
|
|
? Icons.add
|
|
: Icons.edit),
|
|
),
|
|
],
|
|
),
|
|
Padding(
|
|
padding: const EdgeInsets.only(top: 10.0),
|
|
child: Row(
|
|
children: [
|
|
Expanded(
|
|
child: AutoCompleteDropdownButton<
|
|
BasalProfile>(
|
|
controller: _basalProfileController,
|
|
selectedItem: _basalProfile,
|
|
label: translate(LocalizationKeys.event_fields_basalProfile),
|
|
items: _basalProfiles,
|
|
onChanged: updateBasalProfile,
|
|
),
|
|
),
|
|
IconButton(
|
|
onPressed: () {
|
|
Navigator.push(
|
|
context,
|
|
MaterialPageRoute(
|
|
builder: (context) => _basalProfile ==
|
|
null
|
|
? const BasalProfileDetailScreen()
|
|
: BasalProfileDetailScreen(
|
|
id: _basalProfile!.id),
|
|
),
|
|
).then((result) {
|
|
updateBasalProfile(result?[1]);
|
|
reload(message: result?[0]);
|
|
});
|
|
},
|
|
icon: Icon(_basalProfile == null
|
|
? Icons.add
|
|
: Icons.edit),
|
|
),
|
|
],
|
|
),
|
|
)
|
|
]
|
|
: []),
|
|
TextFormField(
|
|
controller: _notesController,
|
|
decoration: InputDecoration(
|
|
labelText: translate(LocalizationKeys.event_fields_notes),
|
|
),
|
|
keyboardType: TextInputType.multiline,
|
|
minLines: 2,
|
|
maxLines: 5,
|
|
),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
bottomNavigationBar: DetailBottomRow(
|
|
onCancel: handleCancelAction,
|
|
onAction: _isSaving ? null : handleSaveAction,
|
|
),
|
|
);
|
|
}
|
|
}
|