diameter/lib/screens/log/log_entry/log_entry.dart

459 lines
17 KiB
Dart
Raw Normal View History

2021-10-22 23:08:09 +00:00
import 'package:diameter/components/detail.dart';
import 'package:diameter/components/dialogs.dart';
import 'package:diameter/components/forms.dart';
import 'package:diameter/models/log_bolus.dart';
2021-10-22 23:08:09 +00:00
import 'package:diameter/models/log_entry.dart';
import 'package:diameter/models/log_meal.dart';
import 'package:diameter/models/settings.dart';
2021-10-22 23:08:09 +00:00
import 'package:diameter/navigation.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_meal_detail.dart';
import 'package:diameter/screens/log/log_entry/log_meal_list.dart';
import 'package:diameter/utils/date_time_utils.dart';
import 'package:diameter/utils/utils.dart';
2021-10-22 23:08:09 +00:00
import 'package:flutter/material.dart';
2021-12-09 05:14:55 +00:00
import 'dart:math' as math;
2021-10-22 23:08:09 +00:00
class LogEntryScreen extends StatefulWidget {
static const String routeName = '/log-entry';
final int id;
const LogEntryScreen({Key? key, this.id = 0}) : super(key: key);
2021-10-22 23:08:09 +00:00
@override
_LogEntryScreenState createState() => _LogEntryScreenState();
}
class _LogEntryScreenState extends State<LogEntryScreen> {
LogEntry? _logEntry;
List<LogMeal> _logMeals = [];
List<LogBolus> _logBoli = [];
2021-12-09 05:14:55 +00:00
bool _isNew = true;
bool _isSaving = false;
2021-10-22 23:08:09 +00:00
final GlobalKey<FormState> logEntryForm = GlobalKey<FormState>();
2021-12-09 05:14:55 +00:00
final ScrollController _scrollController = ScrollController();
2021-10-22 23:08:09 +00:00
late DateTime _time;
2021-12-09 05:14:55 +00:00
double? _glucoseTrend;
final _timeController = TextEditingController(text: '');
final _dateController = TextEditingController(text: '');
final _mgPerDlController = TextEditingController(text: '');
final _mmolPerLController = TextEditingController(text: '');
final _notesController = TextEditingController(text: '');
2021-10-22 23:08:09 +00:00
late FloatingActionButton addMealButton;
late FloatingActionButton addBolusButton;
2021-10-22 23:08:09 +00:00
late IconButton refreshButton;
late IconButton closeButton;
late DetailBottomRow detailBottomRow;
2021-10-22 23:08:09 +00:00
FloatingActionButton? actionButton;
List<Widget> appBarActions = [];
DetailBottomRow? bottomNav;
@override
void initState() {
super.initState();
reload();
addMealButton = FloatingActionButton(
onPressed: handleAddNewMeal,
child: const Icon(Icons.add),
);
addBolusButton = FloatingActionButton(
onPressed: handleAddNewBolus,
child: const Icon(Icons.add),
);
refreshButton = IconButton(
icon: const Icon(Icons.refresh),
onPressed: reload,
);
closeButton = IconButton(
onPressed: handleCancelAction,
icon: const Icon(Icons.close),
);
detailBottomRow = DetailBottomRow(
onCancel: handleCancelAction,
onSave: _isSaving ? null : handleSaveAction,
);
actionButton = null;
appBarActions = [closeButton];
bottomNav = detailBottomRow;
if (_logEntry != null) {
_time = _logEntry!.time;
_mgPerDlController.text = (_logEntry!.mgPerDl ?? '').toString();
_mmolPerLController.text = (_logEntry!.mmolPerL ?? '').toString();
2021-12-09 05:14:55 +00:00
_glucoseTrend = _logEntry!.glucoseTrend;
_notesController.text = _logEntry!.notes ?? '';
} else {
_time = DateTime.now();
}
updateTime();
}
void reload({String? message}) {
if (widget.id != 0) {
2021-10-22 23:08:09 +00:00
setState(() {
_logEntry = LogEntry.get(widget.id);
_logMeals = LogMeal.getAllForEntry(widget.id);
_logBoli = LogBolus.getAllForEntry(widget.id);
2021-10-22 23:08:09 +00:00
});
_isNew = _logEntry == null;
2021-10-22 23:08:09 +00:00
}
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);
}
2021-12-09 05:14:55 +00:00
void convertBetweenMgPerDlAndMmolPerL() {
int? mgPerDl;
double? mmolPerL;
2021-12-09 05:14:55 +00:00
if (Settings.glucoseMeasurement != GlucoseMeasurement.mmolPerL &&
_mgPerDlController.text != '') {
mgPerDl = int.tryParse(_mgPerDlController.text);
setState(() {
_mmolPerLController.text =
Utils.convertMgPerDlToMmolPerL(mgPerDl!).toString();
});
}
2021-12-09 05:14:55 +00:00
if (Settings.glucoseMeasurement != GlucoseMeasurement.mgPerDl &&
_mmolPerLController.text != '') {
mmolPerL = double.tryParse(_mmolPerLController.text);
setState(() {
_mgPerDlController.text =
Utils.convertMmolPerLToMgPerDl(mmolPerL!).toString();
});
}
2021-10-22 23:08:09 +00:00
}
void handleSaveAction() async {
setState(() {
_isSaving = true;
});
2021-10-22 23:08:09 +00:00
if (logEntryForm.currentState!.validate()) {
2021-12-09 05:14:55 +00:00
LogEntry logEntry = LogEntry(
id: widget.id,
time: _time,
mgPerDl: int.tryParse(_mgPerDlController.text),
mmolPerL: double.tryParse(_mmolPerLController.text),
2021-12-09 05:14:55 +00:00
glucoseTrend: _glucoseTrend,
notes: _notesController.text,
2021-12-09 05:14:55 +00:00
);
LogEntry.put(logEntry);
2021-10-22 23:08:09 +00:00
Navigator.pushReplacementNamed(context, '/log',
2021-12-09 05:14:55 +00:00
arguments: ['${_isNew ? 'New' : ''} Log Entry Saved', logEntry]);
2021-10-22 23:08:09 +00:00
}
setState(() {
_isSaving = false;
});
2021-10-22 23:08:09 +00:00
}
void handleCancelAction() {
if (Settings.get().showConfirmationDialogOnCancel &&
((_isNew &&
(int.tryParse(_mgPerDlController.text) != null ||
double.tryParse(_mmolPerLController.text) != null ||
_notesController.text != '')) ||
(!_isNew &&
(int.tryParse(_mgPerDlController.text) != _logEntry!.mgPerDl ||
double.tryParse(_mmolPerLController.text) !=
_logEntry!.mmolPerL ||
_notesController.text != (_logEntry!.notes ?? ''))))) {
2021-10-22 23:08:09 +00:00
Dialogs.showCancelConfirmationDialog(
context: context,
isNew: _isNew,
2021-10-22 23:08:09 +00:00
onSave: handleSaveAction,
onDiscard: (context) => Navigator.pushReplacementNamed(context, '/log'),
);
} else {
Navigator.pushReplacementNamed(context, '/log',
arguments: '${_isNew ? 'New' : ''} Log Entry Saved');
2021-10-22 23:08:09 +00:00
}
}
void handleAddNewMeal() async {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) {
return LogMealDetailScreen(logEntryId: _logEntry!.id);
},
),
2021-12-09 05:14:55 +00:00
).then((result) => reload(message: result?[0]));
}
void handleAddNewBolus() async {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) {
return LogBolusDetailScreen(logEntryId: _logEntry!.id);
},
),
2021-12-09 05:14:55 +00:00
).then((result) => reload(message: result?[0]));
}
2021-10-22 23:08:09 +00:00
void renderTabButtons(index) {
if (_logEntry != null) {
2021-10-22 23:08:09 +00:00
setState(() {
switch (index) {
case 1:
actionButton = addMealButton;
appBarActions = [refreshButton, closeButton];
bottomNav = null;
2021-10-22 23:08:09 +00:00
break;
case 2:
actionButton = addBolusButton;
appBarActions = [refreshButton, closeButton];
bottomNav = null;
break;
2021-10-22 23:08:09 +00:00
default:
actionButton = null;
appBarActions = [closeButton];
bottomNav = detailBottomRow;
2021-10-22 23:08:09 +00:00
}
});
}
}
@override
Widget build(BuildContext context) {
return DefaultTabController(
length: _isNew ? 1 : 3,
2021-10-22 23:08:09 +00:00
child: Builder(builder: (BuildContext context) {
final TabController tabController = DefaultTabController.of(context)!;
tabController.addListener(() {
renderTabButtons(tabController.index);
2021-10-22 23:08:09 +00:00
});
List<Widget> tabs = [
2021-12-09 05:14:55 +00:00
Scrollbar(
controller: _scrollController,
child: SingleChildScrollView(
controller: _scrollController,
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
FormWrapper(
formState: logEntryForm,
fields: [
Row(
children: [
Expanded(
child: Padding(
padding: const EdgeInsets.only(right: 5),
child: DateTimeFormField(
date: _time,
label: 'Date',
controller: _dateController,
onChanged: (newTime) {
if (newTime != null) {
setState(() {
_time = DateTime(
newTime.year,
newTime.month,
newTime.day,
_time.hour,
_time.minute);
});
updateTime();
}
},
),
),
),
2021-12-09 05:14:55 +00:00
Expanded(
child: Padding(
padding: const EdgeInsets.only(left: 5),
child: TimeOfDayFormField(
time: TimeOfDay.fromDateTime(_time),
label: 'Time',
controller: _timeController,
onChanged: (newTime) {
if (newTime != null) {
setState(() {
_time = DateTime(
_time.year,
_time.month,
_time.day,
newTime.hour,
newTime.minute);
});
updateTime();
}
},
),
),
),
2021-12-09 05:14:55 +00:00
],
),
Row(
children: [
Settings.glucoseMeasurement ==
GlucoseMeasurement.mgPerDl ||
[
GlucoseDisplayMode.both,
GlucoseDisplayMode.bothForDetail
2021-12-09 05:14:55 +00:00
].contains(Settings.glucoseDisplayMode)
? Expanded(
child: TextFormField(
decoration: const InputDecoration(
labelText: 'mg/dl',
suffixText: 'mg/dl',
),
readOnly: Settings.glucoseMeasurement ==
GlucoseMeasurement.mmolPerL,
controller: _mgPerDlController,
onChanged: (_) async {
await Future.delayed(
const Duration(seconds: 1));
convertBetweenMgPerDlAndMmolPerL();
},
keyboardType: const TextInputType
.numberWithOptions(),
validator: (value) {
if (value!.trim().isEmpty &&
_mmolPerLController.text
.trim()
.isEmpty) {
return 'How high is your blood sugar?';
}
return null;
},
),
2021-12-09 05:14:55 +00:00
)
: Container(),
[
GlucoseDisplayMode.both,
GlucoseDisplayMode.bothForDetail
].contains(Settings.glucoseDisplayMode)
? const SizedBox(width: 10.0)
: Container(),
Settings.glucoseMeasurement ==
GlucoseMeasurement.mmolPerL ||
Settings.glucoseDisplayMode ==
GlucoseDisplayMode.both ||
Settings.glucoseDisplayMode ==
GlucoseDisplayMode.bothForDetail
? Expanded(
child: TextFormField(
decoration: const InputDecoration(
labelText: 'mmol/l',
suffixText: 'mmol/l',
),
readOnly: Settings.glucoseMeasurement ==
GlucoseMeasurement.mgPerDl,
controller: _mmolPerLController,
onChanged: (_) async {
await Future.delayed(
const Duration(seconds: 1));
convertBetweenMgPerDlAndMmolPerL();
},
keyboardType:
const TextInputType.numberWithOptions(
decimal: true),
validator: (value) {
if (value!.trim().isEmpty &&
_mgPerDlController.text
.trim()
.isEmpty) {
return 'How high is your blood sugar?';
}
return null;
},
),
)
: Container(),
Transform.rotate(
angle: (_glucoseTrend ?? 90) * math.pi / 180,
child: IconButton(
onPressed: () => setState(() {
_glucoseTrend = (_glucoseTrend ?? -45) + 45;
if (_glucoseTrend! > 180) {
_glucoseTrend = null;
}
}),
icon: Icon(Icons.arrow_upward,
color: _glucoseTrend != null
? Theme.of(context).iconTheme.color
: Theme.of(context).disabledColor),
),
),
],
),
TextFormField(
controller: _notesController,
decoration: const InputDecoration(
labelText: 'Notes',
),
keyboardType: TextInputType.multiline,
minLines: 2,
maxLines: 5,
),
2021-12-09 05:14:55 +00:00
],
),
]),
),
),
];
if (!_isNew) {
tabs.add(LogMealListScreen(
logEntry: _logEntry!, logMeals: _logMeals, reload: reload));
tabs.add(LogBolusListScreen(
logEntry: _logEntry!, logBoli: _logBoli, reload: reload));
}
2021-10-22 23:08:09 +00:00
return Scaffold(
appBar: AppBar(
title: Text(_isNew ? 'New Log Entry' : 'Edit Log Entry'),
bottom: _isNew
2021-10-22 23:08:09 +00:00
? PreferredSize(child: Container(), preferredSize: Size.zero)
: const TabBar(
tabs: [
Tab(text: 'GENERAL'),
Tab(text: 'MEALS'),
Tab(text: 'BOLI'),
2021-10-22 23:08:09 +00:00
],
),
actions: appBarActions,
),
drawer: const Navigation(currentLocation: LogEntryScreen.routeName),
body: TabBarView(
children: tabs,
2021-10-22 23:08:09 +00:00
),
bottomNavigationBar: bottomNav,
2021-10-22 23:08:09 +00:00
floatingActionButton: actionButton,
floatingActionButtonLocation: FloatingActionButtonLocation.endFloat,
2021-10-22 23:08:09 +00:00
);
}),
);
}
}