improve screens using tabs, disable save buttons while saving, redo log meal list screen, log meal model fixes

This commit is contained in:
spinel 2021-10-27 21:00:28 +02:00
parent 53fb4ac7fc
commit b4db712719
18 changed files with 213 additions and 113 deletions

19
.vscode/launch.json vendored Normal file
View File

@ -0,0 +1,19 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "diameter",
"request": "launch",
"type": "dart"
},
{
"name": "diameter (profile mode)",
"request": "launch",
"type": "dart",
"flutterMode": "profile"
}
]
}

3
TODO Normal file
View File

@ -0,0 +1,3 @@
Todo:
☐ Item

View File

@ -1,8 +1,8 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class DetailBottomRow extends StatefulWidget { class DetailBottomRow extends StatefulWidget {
final void Function() onCancel; final void Function()? onCancel;
final void Function() onSave; final void Function()? onSave;
const DetailBottomRow( const DetailBottomRow(
{Key? key, required this.onCancel, required this.onSave}) {Key? key, required this.onCancel, required this.onSave})

View File

@ -33,6 +33,7 @@ class _ViewWithProgressIndicatorState extends State<ViewWithProgressIndicator> {
child: SizedBox( child: SizedBox(
width: widget.progressIndicatorSize, width: widget.progressIndicatorSize,
height: widget.progressIndicatorSize, height: widget.progressIndicatorSize,
// TODO: only show if loading takes longer than 30ms
child: const CircularProgressIndicator(), child: const CircularProgressIndicator(),
), ),
), ),

View File

@ -23,7 +23,6 @@ import 'package:diameter/config.dart';
import 'package:diameter/screens/basal/basal_profiles_list.dart'; import 'package:diameter/screens/basal/basal_profiles_list.dart';
import 'package:diameter/screens/bolus/bolus_profile_list.dart'; import 'package:diameter/screens/bolus/bolus_profile_list.dart';
import 'package:diameter/navigation.dart'; import 'package:diameter/navigation.dart';
import 'package:flex_color_scheme/flex_color_scheme.dart';
void main() async { void main() async {
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();

View File

@ -24,39 +24,19 @@ class LogMeal extends DataTableContent {
LogMeal(ParseObject object) { LogMeal(ParseObject object) {
objectId = object.get<String>('objectId'); objectId = object.get<String>('objectId');
logEntry = object.get<ParseObject>('logEntry')!.get<String>('objectId')!; logEntry = object.get<ParseObject>('logEntry')!.get<String>('objectId')!;
meal = object.get<ParseObject>('meal') != null meal = object.get<ParseObject>('meal')?.get<String>('objectId');
? object.get<ParseObject>('meal')!.get<String>('objectId')
: null;
value = object.get<String>('value')!; value = object.get<String>('value')!;
source = object.get<ParseObject>('source') != null source = object.get<ParseObject>('source')?.get<String>('objectId');
? object.get<ParseObject>('source')!.get<String>('objectId') category = object.get<ParseObject>('category')?.get<String>('objectId');
: null; portionType = object.get<ParseObject>('portionType')?.get<String>('objectId');
category = object.get<ParseObject>('category') != null carbsRatio = object.get<num>('carbsRatio')?.toDouble();
? object.get<ParseObject>('category')!.get<String>('objectId') portionSize = object.get<num>('portionSize')?.toDouble();
: null; carbsPerPortion = object.get<num>('carbsPerPortion')?.toDouble();
portionType = object.get<ParseObject>('portionType') != null portionSizeAccuracy = object.get<ParseObject>('portionSizeAccuracy')?.get<String>('objectId');
? object.get<ParseObject>('portionType')!.get<String>('objectId') carbsRatioAccuracy = object.get<ParseObject>('carbsRatioAccuracy')?.get<String>('objectId');
: null; bolus = object.get<num>('bolus')?.toDouble();
carbsRatio = object.get<num>('carbsRatio')!.toDouble(); delayedBolusDuration = object.get<num>('delayedBolusDuration')?.toInt();
portionSize = object.get<num>('portionSize')!.toDouble(); delayedBolusRate = object.get<num>('delayedBolusRate')?.toDouble();
carbsPerPortion = object.get<num>('carbsPerPortion')!.toDouble();
portionSizeAccuracy = object.get<ParseObject>('portionSizeAccuracy') != null
? object
.get<ParseObject>('portionSizeAccuracy')!
.get<String>('objectId')
: null;
carbsRatioAccuracy = object.get<ParseObject>('carbsRatioAccuracy') != null
? object.get<ParseObject>('carbsRatioAccuracy')!.get<String>('objectId')
: null;
bolus = object.get<num>('bolus') != null
? object.get<num>('bolus')!.toDouble()
: null;
delayedBolusDuration = object.get<num>('delayedBolusDuration') != null
? object.get<num>('delayedBolusDuration')!.toInt()
: null;
delayedBolusRate = object.get<num>('delayedBolusRate') != null
? object.get<num>('delayedBolusRate')!.toDouble()
: null;
notes = object.get<String>('notes'); notes = object.get<String>('notes');
} }
@ -104,7 +84,7 @@ class LogMeal extends DataTableContent {
double? delayedBolusRate, double? delayedBolusRate,
String? notes, String? notes,
}) async { }) async {
final logMeal = ParseObject('Meal') final logMeal = ParseObject('LogMeal')
..set('value', value) ..set('value', value)
..set('logEntry', ..set('logEntry',
(ParseObject('LogEntry')..objectId = logEntry).toPointer()) (ParseObject('LogEntry')..objectId = logEntry).toPointer())
@ -161,7 +141,7 @@ class LogMeal extends DataTableContent {
double? delayedBolusRate, double? delayedBolusRate,
String? notes, String? notes,
}) async { }) async {
var logMeal = ParseObject('Meal')..objectId = objectId; var logMeal = ParseObject('LogMeal')..objectId = objectId;
if (value != null) { if (value != null) {
logMeal.set('value', value); logMeal.set('value', value);
} }

View File

@ -24,6 +24,8 @@ class _AccuracyDetailScreenState extends State<AccuracyDetailScreen> {
bool _forCarbsRatio = false; bool _forCarbsRatio = false;
bool _forPortionSize = false; bool _forPortionSize = false;
bool _isSaving = false;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
@ -38,6 +40,9 @@ class _AccuracyDetailScreenState extends State<AccuracyDetailScreen> {
} }
void handleSaveAction() async { void handleSaveAction() async {
setState(() {
_isSaving = true;
});
if (_accuracyForm.currentState!.validate()) { if (_accuracyForm.currentState!.validate()) {
bool isNew = widget.accuracy == null; bool isNew = widget.accuracy == null;
isNew isNew
@ -58,6 +63,9 @@ class _AccuracyDetailScreenState extends State<AccuracyDetailScreen> {
); );
Navigator.pop(context, '${isNew ? 'New' : ''} Accuracy saved'); Navigator.pop(context, '${isNew ? 'New' : ''} Accuracy saved');
} }
setState(() {
_isSaving = false;
});
} }
void handleCancelAction() { void handleCancelAction() {
@ -154,7 +162,7 @@ class _AccuracyDetailScreenState extends State<AccuracyDetailScreen> {
), ),
bottomNavigationBar: DetailBottomRow( bottomNavigationBar: DetailBottomRow(
onCancel: handleCancelAction, onCancel: handleCancelAction,
onSave: handleSaveAction, onSave: _isSaving ? null : handleSaveAction,
), ),
); );
} }

View File

@ -37,6 +37,8 @@ class _BasalDetailScreenState extends State<BasalDetailScreen> {
final _endTimeController = TextEditingController(text: ''); final _endTimeController = TextEditingController(text: '');
final _unitsController = TextEditingController(text: ''); final _unitsController = TextEditingController(text: '');
bool _isSaving = false;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
@ -114,6 +116,9 @@ class _BasalDetailScreenState extends State<BasalDetailScreen> {
} }
void handleSaveAction() async { void handleSaveAction() async {
setState(() {
_isSaving = true;
});
if (_basalForm.currentState!.validate()) { if (_basalForm.currentState!.validate()) {
await validateTimePeriod().then((value) async { await validateTimePeriod().then((value) async {
if (value != 'CANCEL') { if (value != 'CANCEL') {
@ -137,6 +142,9 @@ class _BasalDetailScreenState extends State<BasalDetailScreen> {
} }
}); });
} }
setState(() {
_isSaving = false;
});
} }
void handleCancelAction() { void handleCancelAction() {
@ -241,7 +249,7 @@ class _BasalDetailScreenState extends State<BasalDetailScreen> {
), ),
bottomNavigationBar: DetailBottomRow( bottomNavigationBar: DetailBottomRow(
onCancel: handleCancelAction, onCancel: handleCancelAction,
onSave: handleSaveAction, onSave: _isSaving ? null : handleSaveAction,
), ),
); );
} }

View File

@ -30,11 +30,18 @@ class _BasalProfileDetailScreenState extends State<BasalProfileDetailScreen> {
late FloatingActionButton addBasalButton; late FloatingActionButton addBasalButton;
late IconButton refreshButton; late IconButton refreshButton;
late IconButton closeButton; late IconButton closeButton;
late DetailBottomRow detailBottomRow;
FloatingActionButton? actionButton;
List<Widget> appBarActions = [];
DetailBottomRow? bottomNav;
final _nameController = TextEditingController(text: ''); final _nameController = TextEditingController(text: '');
final _notesController = TextEditingController(text: ''); final _notesController = TextEditingController(text: '');
bool _active = false; bool _active = false;
bool _isSaving = false;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
@ -62,8 +69,14 @@ class _BasalProfileDetailScreenState extends State<BasalProfileDetailScreen> {
icon: const Icon(Icons.close), icon: const Icon(Icons.close),
); );
detailBottomRow = DetailBottomRow(
onCancel: handleCancelAction,
onSave: _isSaving ? null : handleSaveAction,
);
actionButton = null; actionButton = null;
appBarActions = [closeButton]; appBarActions = [closeButton];
bottomNav = detailBottomRow;
} }
void refresh({String? message}) { void refresh({String? message}) {
@ -195,6 +208,9 @@ class _BasalProfileDetailScreenState extends State<BasalProfileDetailScreen> {
} }
void handleSaveAction() async { void handleSaveAction() async {
setState(() {
_isSaving = true;
});
if (_basalProfileForm.currentState!.validate()) { if (_basalProfileForm.currentState!.validate()) {
await checkActiveProfiles(); await checkActiveProfiles();
bool isNew = widget.basalProfile == null; bool isNew = widget.basalProfile == null;
@ -211,6 +227,9 @@ class _BasalProfileDetailScreenState extends State<BasalProfileDetailScreen> {
); );
Navigator.pop(context, '${isNew ? 'New' : ''} Basal Profile saved'); Navigator.pop(context, '${isNew ? 'New' : ''} Basal Profile saved');
} }
setState(() {
_isSaving = false;
});
} }
void handleCancelAction() { void handleCancelAction() {
@ -235,9 +254,6 @@ class _BasalProfileDetailScreenState extends State<BasalProfileDetailScreen> {
} }
} }
FloatingActionButton? actionButton;
List<Widget> appBarActions = [];
void renderTabButtons(index) { void renderTabButtons(index) {
if (widget.basalProfile != null) { if (widget.basalProfile != null) {
setState(() { setState(() {
@ -245,10 +261,12 @@ class _BasalProfileDetailScreenState extends State<BasalProfileDetailScreen> {
case 1: case 1:
actionButton = addBasalButton; actionButton = addBasalButton;
appBarActions = [refreshButton, closeButton]; appBarActions = [refreshButton, closeButton];
bottomNav = null;
break; break;
default: default:
actionButton = null; actionButton = null;
appBarActions = [closeButton]; appBarActions = [closeButton];
bottomNav = detailBottomRow;
} }
}); });
} }
@ -262,9 +280,7 @@ class _BasalProfileDetailScreenState extends State<BasalProfileDetailScreen> {
child: Builder(builder: (BuildContext context) { child: Builder(builder: (BuildContext context) {
final TabController tabController = DefaultTabController.of(context)!; final TabController tabController = DefaultTabController.of(context)!;
tabController.addListener(() { tabController.addListener(() {
if (tabController.indexIsChanging) {
renderTabButtons(tabController.index); renderTabButtons(tabController.index);
}
}); });
List<Widget> tabs = [ List<Widget> tabs = [
SingleChildScrollView( SingleChildScrollView(
@ -332,13 +348,10 @@ class _BasalProfileDetailScreenState extends State<BasalProfileDetailScreen> {
drawer: const Navigation( drawer: const Navigation(
currentLocation: BasalProfileDetailScreen.routeName), currentLocation: BasalProfileDetailScreen.routeName),
body: TabBarView(children: tabs), body: TabBarView(children: tabs),
bottomNavigationBar: DetailBottomRow( bottomNavigationBar: bottomNav,
onCancel: handleCancelAction,
onSave: handleSaveAction,
),
floatingActionButton: actionButton, floatingActionButton: actionButton,
floatingActionButtonLocation: floatingActionButtonLocation:
FloatingActionButtonLocation.centerDocked, FloatingActionButtonLocation.endFloat,
); );
}), }),
); );

View File

@ -43,6 +43,8 @@ class _BolusDetailScreenState extends State<BolusDetailScreen> {
final _mgPerDlController = TextEditingController(text: ''); final _mgPerDlController = TextEditingController(text: '');
final _mmolPerLController = TextEditingController(text: ''); final _mmolPerLController = TextEditingController(text: '');
bool _isSaving = false;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
@ -123,6 +125,9 @@ class _BolusDetailScreenState extends State<BolusDetailScreen> {
} }
void handleSaveAction() async { void handleSaveAction() async {
setState(() {
_isSaving = true;
});
if (_bolusForm.currentState!.validate()) { if (_bolusForm.currentState!.validate()) {
await validateTimePeriod().then((value) async { await validateTimePeriod().then((value) async {
if (value != 'CANCEL') { if (value != 'CANCEL') {
@ -152,6 +157,9 @@ class _BolusDetailScreenState extends State<BolusDetailScreen> {
} }
}); });
} }
setState(() {
_isSaving = false;
});
} }
void handleCancelAction() { void handleCancelAction() {
@ -391,7 +399,7 @@ class _BolusDetailScreenState extends State<BolusDetailScreen> {
), ),
bottomNavigationBar: DetailBottomRow( bottomNavigationBar: DetailBottomRow(
onCancel: handleCancelAction, onCancel: handleCancelAction,
onSave: handleSaveAction, onSave: _isSaving ? null : handleSaveAction,
), ),
); );
} }

View File

@ -30,11 +30,18 @@ class _BolusProfileDetailScreenState extends State<BolusProfileDetailScreen> {
late FloatingActionButton addBolusButton; late FloatingActionButton addBolusButton;
late IconButton refreshButton; late IconButton refreshButton;
late IconButton closeButton; late IconButton closeButton;
late DetailBottomRow detailBottomRow;
FloatingActionButton? actionButton;
List<Widget> appBarActions = [];
DetailBottomRow? bottomNav;
final _nameController = TextEditingController(text: ''); final _nameController = TextEditingController(text: '');
final _notesController = TextEditingController(text: ''); final _notesController = TextEditingController(text: '');
bool _active = false; bool _active = false;
bool _isSaving = false;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
@ -62,8 +69,15 @@ class _BolusProfileDetailScreenState extends State<BolusProfileDetailScreen> {
icon: const Icon(Icons.close), icon: const Icon(Icons.close),
); );
// TODO: fix (saving button doesnt get disabled)
detailBottomRow = DetailBottomRow(
onCancel: handleCancelAction,
onSave: _isSaving ? null : handleSaveAction,
);
actionButton = null; actionButton = null;
appBarActions = [closeButton]; appBarActions = [closeButton];
bottomNav = detailBottomRow;
} }
void refresh({String? message}) { void refresh({String? message}) {
@ -195,6 +209,9 @@ class _BolusProfileDetailScreenState extends State<BolusProfileDetailScreen> {
} }
void handleSaveAction() async { void handleSaveAction() async {
setState(() {
_isSaving = true;
});
if (_bolusProfileForm.currentState!.validate()) { if (_bolusProfileForm.currentState!.validate()) {
await checkActiveProfiles(); await checkActiveProfiles();
bool isNew = widget.bolusProfile == null; bool isNew = widget.bolusProfile == null;
@ -211,6 +228,9 @@ class _BolusProfileDetailScreenState extends State<BolusProfileDetailScreen> {
); );
Navigator.pop(context, '${isNew ? 'New' : ''} Bolus Profile saved'); Navigator.pop(context, '${isNew ? 'New' : ''} Bolus Profile saved');
} }
setState(() {
_isSaving = false;
});
} }
void handleCancelAction() { void handleCancelAction() {
@ -235,9 +255,6 @@ class _BolusProfileDetailScreenState extends State<BolusProfileDetailScreen> {
} }
} }
FloatingActionButton? actionButton;
List<Widget> appBarActions = [];
void renderTabButtons(index) { void renderTabButtons(index) {
if (widget.bolusProfile != null) { if (widget.bolusProfile != null) {
setState(() { setState(() {
@ -245,10 +262,12 @@ class _BolusProfileDetailScreenState extends State<BolusProfileDetailScreen> {
case 1: case 1:
actionButton = addBolusButton; actionButton = addBolusButton;
appBarActions = [refreshButton, closeButton]; appBarActions = [refreshButton, closeButton];
bottomNav = null;
break; break;
default: default:
actionButton = null; actionButton = null;
appBarActions = [closeButton]; appBarActions = [closeButton];
bottomNav = detailBottomRow;
} }
}); });
} }
@ -262,9 +281,7 @@ class _BolusProfileDetailScreenState extends State<BolusProfileDetailScreen> {
child: Builder(builder: (BuildContext context) { child: Builder(builder: (BuildContext context) {
final TabController tabController = DefaultTabController.of(context)!; final TabController tabController = DefaultTabController.of(context)!;
tabController.addListener(() { tabController.addListener(() {
if (tabController.indexIsChanging) {
renderTabButtons(tabController.index); renderTabButtons(tabController.index);
}
}); });
List<Widget> tabs = [ List<Widget> tabs = [
@ -334,13 +351,10 @@ class _BolusProfileDetailScreenState extends State<BolusProfileDetailScreen> {
body: TabBarView( body: TabBarView(
children: tabs, children: tabs,
), ),
bottomNavigationBar: DetailBottomRow( bottomNavigationBar: bottomNav,
onCancel: handleCancelAction,
onSave: handleSaveAction,
),
floatingActionButton: actionButton, floatingActionButton: actionButton,
floatingActionButtonLocation: floatingActionButtonLocation:
FloatingActionButtonLocation.centerDocked, FloatingActionButtonLocation.endFloat,
); );
}), }),
); );

View File

@ -28,9 +28,13 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
late FloatingActionButton addEventButton; late FloatingActionButton addEventButton;
late IconButton refreshButton; late IconButton refreshButton;
late IconButton closeButton; late IconButton closeButton;
late DetailBottomRow detailBottomRow;
static FloatingActionButton? actionButton; FloatingActionButton? actionButton;
static List<Widget> appBarActions = []; List<Widget> appBarActions = [];
DetailBottomRow? bottomNav;
bool _isSaving = false;
final formDataControllers = <String, TextEditingController>{ final formDataControllers = <String, TextEditingController>{
'time': TextEditingController(text: ''), 'time': TextEditingController(text: ''),
@ -54,6 +58,9 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
} }
void handleSaveAction() async { void handleSaveAction() async {
setState(() {
_isSaving = true;
});
if (logEntryForm.currentState!.validate()) { if (logEntryForm.currentState!.validate()) {
bool isNew = widget.entry == null; bool isNew = widget.entry == null;
isNew isNew
@ -85,6 +92,9 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
Navigator.pushReplacementNamed(context, '/log', Navigator.pushReplacementNamed(context, '/log',
arguments: '${isNew ? 'New' : ''} Log Entry Saved'); arguments: '${isNew ? 'New' : ''} Log Entry Saved');
} }
setState(() {
_isSaving = false;
});
} }
void handleCancelAction() { void handleCancelAction() {
@ -188,8 +198,14 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
icon: const Icon(Icons.close), icon: const Icon(Icons.close),
); );
detailBottomRow = DetailBottomRow(
onCancel: handleCancelAction,
onSave: _isSaving ? null : handleSaveAction,
);
actionButton = null; actionButton = null;
appBarActions = [closeButton]; appBarActions = [closeButton];
bottomNav = detailBottomRow;
} }
void renderTabButtons(index) { void renderTabButtons(index) {
@ -199,14 +215,17 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
case 1: case 1:
actionButton = addMealButton; actionButton = addMealButton;
appBarActions = [refreshButton, closeButton]; appBarActions = [refreshButton, closeButton];
bottomNav = null;
break; break;
case 2: case 2:
actionButton = addEventButton; actionButton = addEventButton;
appBarActions = [refreshButton, closeButton]; appBarActions = [refreshButton, closeButton];
bottomNav = null;
break; break;
default: default:
actionButton = null; actionButton = null;
appBarActions = [closeButton]; appBarActions = [closeButton];
bottomNav = detailBottomRow;
} }
}); });
} }
@ -221,9 +240,7 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
child: Builder(builder: (BuildContext context) { child: Builder(builder: (BuildContext context) {
final TabController tabController = DefaultTabController.of(context)!; final TabController tabController = DefaultTabController.of(context)!;
tabController.addListener(() { tabController.addListener(() {
if (tabController.indexIsChanging) {
renderTabButtons(tabController.index); renderTabButtons(tabController.index);
}
}); });
List<Widget> tabs = [ List<Widget> tabs = [
LogEntryForm( LogEntryForm(
@ -253,13 +270,10 @@ class _LogEntryScreenState extends State<LogEntryScreen> {
body: TabBarView( body: TabBarView(
children: tabs, children: tabs,
), ),
bottomNavigationBar: DetailBottomRow( bottomNavigationBar: bottomNav,
onCancel: handleCancelAction,
onSave: handleSaveAction,
),
floatingActionButton: actionButton, floatingActionButton: actionButton,
floatingActionButtonLocation: floatingActionButtonLocation:
FloatingActionButtonLocation.centerDocked, FloatingActionButtonLocation.centerDocked,
); );
}), }),
); );

View File

@ -30,6 +30,8 @@ class _LogEventDetailScreenState extends State<LogEventDetailScreen> {
late Future<List<LogEventType>> _logEventTypes; late Future<List<LogEventType>> _logEventTypes;
bool _isSaving = false;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
@ -44,6 +46,9 @@ class _LogEventDetailScreenState extends State<LogEventDetailScreen> {
} }
void handleSaveAction() async { void handleSaveAction() async {
setState(() {
_isSaving = true;
});
if (_logEventForm.currentState!.validate()) { if (_logEventForm.currentState!.validate()) {
bool isNew = widget.logEvent == null; bool isNew = widget.logEvent == null;
isNew isNew
@ -63,6 +68,9 @@ class _LogEventDetailScreenState extends State<LogEventDetailScreen> {
); );
Navigator.pop(context, '${isNew ? 'New' : ''} Event Saved'); Navigator.pop(context, '${isNew ? 'New' : ''} Event Saved');
} }
setState(() {
_isSaving = false;
});
} }
void handleCancelAction() { void handleCancelAction() {
@ -138,7 +146,7 @@ class _LogEventDetailScreenState extends State<LogEventDetailScreen> {
), ),
bottomNavigationBar: DetailBottomRow( bottomNavigationBar: DetailBottomRow(
onCancel: handleCancelAction, onCancel: handleCancelAction,
onSave: handleSaveAction, onSave: _isSaving ? null : handleSaveAction,
), ),
); );
} }

View File

@ -24,6 +24,8 @@ class _LogEventTypeDetailScreenState extends State<LogEventTypeDetailScreen> {
final _notesController = TextEditingController(text: ''); final _notesController = TextEditingController(text: '');
bool _hasEndTime = false; bool _hasEndTime = false;
bool _isSaving = false;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
@ -38,6 +40,9 @@ class _LogEventTypeDetailScreenState extends State<LogEventTypeDetailScreen> {
} }
void handleSaveAction() async { void handleSaveAction() async {
setState(() {
_isSaving = true;
});
if (_logEventTypeForm.currentState!.validate()) { if (_logEventTypeForm.currentState!.validate()) {
bool isNew = widget.logEventType == null; bool isNew = widget.logEventType == null;
isNew isNew
@ -58,6 +63,9 @@ class _LogEventTypeDetailScreenState extends State<LogEventTypeDetailScreen> {
); );
Navigator.pop(context, '${isNew ? 'New' : ''} Log Event Type Saved'); Navigator.pop(context, '${isNew ? 'New' : ''} Log Event Type Saved');
} }
setState(() {
_isSaving = false;
});
} }
void handleCancelAction() { void handleCancelAction() {
@ -148,7 +156,7 @@ class _LogEventTypeDetailScreenState extends State<LogEventTypeDetailScreen> {
), ),
bottomNavigationBar: DetailBottomRow( bottomNavigationBar: DetailBottomRow(
onCancel: handleCancelAction, onCancel: handleCancelAction,
onSave: handleSaveAction, onSave: _isSaving ? null : handleSaveAction,
), ),
); );
} }

View File

@ -49,6 +49,8 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
late Future<List<Accuracy>> _portionSizeAccuracies; late Future<List<Accuracy>> _portionSizeAccuracies;
late Future<List<Accuracy>> _carbsRatioAccuracies; late Future<List<Accuracy>> _carbsRatioAccuracies;
bool _isSaving = false;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
@ -128,6 +130,9 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
} }
void handleSaveAction() async { void handleSaveAction() async {
setState(() {
_isSaving = true;
});
if (_logMealForm.currentState!.validate()) { if (_logMealForm.currentState!.validate()) {
bool isNew = widget.logMeal == null; bool isNew = widget.logMeal == null;
isNew isNew
@ -143,6 +148,7 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
carbsPerPortion: double.tryParse(_carbsPerPortionController.text), carbsPerPortion: double.tryParse(_carbsPerPortionController.text),
portionSizeAccuracy: _portionSizeAccuracy, portionSizeAccuracy: _portionSizeAccuracy,
carbsRatioAccuracy: _carbsRatioAccuracy, carbsRatioAccuracy: _carbsRatioAccuracy,
bolus: double.tryParse(_bolusController.text),
delayedBolusDuration: delayedBolusDuration:
int.tryParse(_delayedBolusDurationController.text), int.tryParse(_delayedBolusDurationController.text),
delayedBolusRate: delayedBolusRate:
@ -161,6 +167,7 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
carbsPerPortion: double.tryParse(_carbsPerPortionController.text), carbsPerPortion: double.tryParse(_carbsPerPortionController.text),
portionSizeAccuracy: _portionSizeAccuracy, portionSizeAccuracy: _portionSizeAccuracy,
carbsRatioAccuracy: _carbsRatioAccuracy, carbsRatioAccuracy: _carbsRatioAccuracy,
bolus: double.tryParse(_bolusController.text),
delayedBolusDuration: delayedBolusDuration:
int.tryParse(_delayedBolusDurationController.text), int.tryParse(_delayedBolusDurationController.text),
delayedBolusRate: delayedBolusRate:
@ -169,6 +176,9 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
); );
Navigator.pop(context, '${isNew ? 'New' : ''} Meal Saved'); Navigator.pop(context, '${isNew ? 'New' : ''} Meal Saved');
} }
setState(() {
_isSaving = false;
});
} }
void handleCancelAction() { void handleCancelAction() {
@ -185,6 +195,7 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
double.tryParse(_carbsPerPortionController.text) != null || double.tryParse(_carbsPerPortionController.text) != null ||
_carbsRatioAccuracy != null || _carbsRatioAccuracy != null ||
_portionSizeAccuracy != null || _portionSizeAccuracy != null ||
double.tryParse(_bolusController.text) != null ||
int.tryParse(_delayedBolusDurationController.text) != int.tryParse(_delayedBolusDurationController.text) !=
null || null ||
double.tryParse(_delayedBolusRateController.text) != null || double.tryParse(_delayedBolusRateController.text) != null ||
@ -204,6 +215,7 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
_carbsRatioAccuracy != widget.logMeal!.carbsRatioAccuracy || _carbsRatioAccuracy != widget.logMeal!.carbsRatioAccuracy ||
_portionSizeAccuracy != _portionSizeAccuracy !=
widget.logMeal!.portionSizeAccuracy || widget.logMeal!.portionSizeAccuracy ||
double.tryParse(_bolusController.text) != widget.logMeal!.bolus ||
int.tryParse(_delayedBolusDurationController.text) != int.tryParse(_delayedBolusDurationController.text) !=
widget.logMeal!.delayedBolusDuration || widget.logMeal!.delayedBolusDuration ||
double.tryParse(_delayedBolusRateController.text) != double.tryParse(_delayedBolusRateController.text) !=
@ -486,7 +498,7 @@ class _LogMealDetailScreenState extends State<LogMealDetailScreen> {
), ),
bottomNavigationBar: DetailBottomRow( bottomNavigationBar: DetailBottomRow(
onCancel: handleCancelAction, onCancel: handleCancelAction,
onSave: handleSaveAction, onSave: _isSaving ? null : handleSaveAction,
), ),
); );
} }

View File

@ -86,35 +86,32 @@ class _LogMealListScreenState extends State<LogMealListScreen> {
padding: EdgeInsets.all(10.0), padding: EdgeInsets.all(10.0),
child: Text('No Meals for this Log Entry'), child: Text('No Meals for this Log Entry'),
) )
: ListBody( : ListView.builder(
children: [ shrinkWrap: true,
DataTable( itemCount: snapshot.data != null ? snapshot.data!.length : 0,
columnSpacing: 10.0, itemBuilder: (context, index) {
showCheckboxColumn: false, final meal = snapshot.data![index];
rows: snapshot.data != null return ListTile(
? snapshot.data!.map((meal) { onTap: () => handleEditAction(meal),
return DataRow( title: Row(
cells: meal.asDataTableCells( children: [
[ Expanded(
IconButton( child: Text(meal.value)),
icon: const Icon(Icons.edit), Expanded(
iconSize: 16.0, child: Text(meal.carbsPerPortion != null ? '${meal.carbsPerPortion} g carbs' : '')),
onPressed: () => Expanded(child: Text(meal.bolus != null ? '${meal.bolus} U' : ''))
handleEditAction(meal)), ],
IconButton(
icon: const Icon(Icons.delete),
iconSize: 16.0,
onPressed: () =>
handleDeleteAction(meal)),
],
),
);
}).toList()
: [],
columns: LogMeal.asDataTableColumns(),
), ),
], trailing: IconButton(
), icon: const Icon(
Icons.delete,
color: Colors.blue,
),
onPressed: () => handleDeleteAction(meal),
),
);
},
),
); );
}, },
), ),

View File

@ -43,6 +43,8 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
late Future<List<Accuracy>> _portionSizeAccuracies; late Future<List<Accuracy>> _portionSizeAccuracies;
late Future<List<Accuracy>> _carbsRatioAccuracies; late Future<List<Accuracy>> _carbsRatioAccuracies;
bool isSaving = false;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
@ -74,6 +76,9 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
} }
void handleSaveAction() async { void handleSaveAction() async {
setState(() {
isSaving = true;
});
if (_mealForm.currentState!.validate()) { if (_mealForm.currentState!.validate()) {
bool isNew = widget.meal == null; bool isNew = widget.meal == null;
isNew isNew
@ -112,6 +117,9 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
); );
Navigator.pop(context, '${isNew ? 'New' : ''} Meal Saved'); Navigator.pop(context, '${isNew ? 'New' : ''} Meal Saved');
} }
setState(() {
isSaving = false;
});
} }
void handleCancelAction() { void handleCancelAction() {
@ -430,7 +438,7 @@ class _MealDetailScreenState extends State<MealDetailScreen> {
), ),
bottomNavigationBar: DetailBottomRow( bottomNavigationBar: DetailBottomRow(
onCancel: handleCancelAction, onCancel: handleCancelAction,
onSave: handleSaveAction, onSave: isSaving ? null : handleSaveAction,
), ),
); );
} }

View File

@ -14,7 +14,7 @@ packages:
name: async name: async
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.8.2" version: "2.8.1"
boolean_selector: boolean_selector:
dependency: transitive dependency: transitive
description: description:
@ -28,7 +28,7 @@ packages:
name: characters name: characters
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.2.0" version: "1.1.0"
charcode: charcode:
dependency: transitive dependency: transitive
description: description:
@ -70,7 +70,7 @@ packages:
name: connectivity_plus_macos name: connectivity_plus_macos
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.2.0" version: "1.2.1"
connectivity_plus_platform_interface: connectivity_plus_platform_interface:
dependency: transitive dependency: transitive
description: description:
@ -119,7 +119,7 @@ packages:
name: dio name: dio
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "4.0.0" version: "4.0.1"
fake_async: fake_async:
dependency: transitive dependency: transitive
description: description:
@ -218,7 +218,7 @@ packages:
name: matcher name: matcher
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.12.11" version: "0.12.10"
meta: meta:
dependency: transitive dependency: transitive
description: description:
@ -267,7 +267,7 @@ packages:
name: package_info_plus_macos name: package_info_plus_macos
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.2.0" version: "1.3.0"
package_info_plus_platform_interface: package_info_plus_platform_interface:
dependency: transitive dependency: transitive
description: description:
@ -358,7 +358,7 @@ packages:
name: petitparser name: petitparser
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "4.3.0" version: "4.4.0"
platform: platform:
dependency: transitive dependency: transitive
description: description:
@ -379,7 +379,7 @@ packages:
name: process name: process
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "4.2.3" version: "4.2.4"
provider: provider:
dependency: "direct dev" dependency: "direct dev"
description: description:
@ -510,7 +510,7 @@ packages:
name: test_api name: test_api
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.4.3" version: "0.4.2"
typed_data: typed_data:
dependency: transitive dependency: transitive
description: description:
@ -531,7 +531,7 @@ packages:
name: vector_math name: vector_math
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.1.1" version: "2.1.0"
web_socket_channel: web_socket_channel:
dependency: transitive dependency: transitive
description: description:
@ -545,7 +545,7 @@ packages:
name: win32 name: win32
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.2.9" version: "2.2.10"
xdg_directories: xdg_directories:
dependency: transitive dependency: transitive
description: description:
@ -559,7 +559,7 @@ packages:
name: xml name: xml
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "5.3.0" version: "5.3.1"
xxtea: xxtea:
dependency: transitive dependency: transitive
description: description: