diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..378f598 --- /dev/null +++ b/.vscode/launch.json @@ -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" + } + ] +} \ No newline at end of file diff --git a/TODO b/TODO new file mode 100644 index 0000000..a0814df --- /dev/null +++ b/TODO @@ -0,0 +1,3 @@ + +Todo: + ☐ Item diff --git a/lib/components/detail.dart b/lib/components/detail.dart index 2f1c56d..160a853 100644 --- a/lib/components/detail.dart +++ b/lib/components/detail.dart @@ -1,8 +1,8 @@ import 'package:flutter/material.dart'; class DetailBottomRow extends StatefulWidget { - final void Function() onCancel; - final void Function() onSave; + final void Function()? onCancel; + final void Function()? onSave; const DetailBottomRow( {Key? key, required this.onCancel, required this.onSave}) diff --git a/lib/components/progress_indicator.dart b/lib/components/progress_indicator.dart index 69e18e5..9544161 100644 --- a/lib/components/progress_indicator.dart +++ b/lib/components/progress_indicator.dart @@ -33,6 +33,7 @@ class _ViewWithProgressIndicatorState extends State { child: SizedBox( width: widget.progressIndicatorSize, height: widget.progressIndicatorSize, + // TODO: only show if loading takes longer than 30ms child: const CircularProgressIndicator(), ), ), diff --git a/lib/main.dart b/lib/main.dart index e7afb58..6a024a0 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -23,7 +23,6 @@ import 'package:diameter/config.dart'; import 'package:diameter/screens/basal/basal_profiles_list.dart'; import 'package:diameter/screens/bolus/bolus_profile_list.dart'; import 'package:diameter/navigation.dart'; -import 'package:flex_color_scheme/flex_color_scheme.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); diff --git a/lib/models/log_meal.dart b/lib/models/log_meal.dart index 46ce504..99b40c9 100644 --- a/lib/models/log_meal.dart +++ b/lib/models/log_meal.dart @@ -24,39 +24,19 @@ class LogMeal extends DataTableContent { LogMeal(ParseObject object) { objectId = object.get('objectId'); logEntry = object.get('logEntry')!.get('objectId')!; - meal = object.get('meal') != null - ? object.get('meal')!.get('objectId') - : null; + meal = object.get('meal')?.get('objectId'); value = object.get('value')!; - source = object.get('source') != null - ? object.get('source')!.get('objectId') - : null; - category = object.get('category') != null - ? object.get('category')!.get('objectId') - : null; - portionType = object.get('portionType') != null - ? object.get('portionType')!.get('objectId') - : null; - carbsRatio = object.get('carbsRatio')!.toDouble(); - portionSize = object.get('portionSize')!.toDouble(); - carbsPerPortion = object.get('carbsPerPortion')!.toDouble(); - portionSizeAccuracy = object.get('portionSizeAccuracy') != null - ? object - .get('portionSizeAccuracy')! - .get('objectId') - : null; - carbsRatioAccuracy = object.get('carbsRatioAccuracy') != null - ? object.get('carbsRatioAccuracy')!.get('objectId') - : null; - bolus = object.get('bolus') != null - ? object.get('bolus')!.toDouble() - : null; - delayedBolusDuration = object.get('delayedBolusDuration') != null - ? object.get('delayedBolusDuration')!.toInt() - : null; - delayedBolusRate = object.get('delayedBolusRate') != null - ? object.get('delayedBolusRate')!.toDouble() - : null; + source = object.get('source')?.get('objectId'); + category = object.get('category')?.get('objectId'); + portionType = object.get('portionType')?.get('objectId'); + carbsRatio = object.get('carbsRatio')?.toDouble(); + portionSize = object.get('portionSize')?.toDouble(); + carbsPerPortion = object.get('carbsPerPortion')?.toDouble(); + portionSizeAccuracy = object.get('portionSizeAccuracy')?.get('objectId'); + carbsRatioAccuracy = object.get('carbsRatioAccuracy')?.get('objectId'); + bolus = object.get('bolus')?.toDouble(); + delayedBolusDuration = object.get('delayedBolusDuration')?.toInt(); + delayedBolusRate = object.get('delayedBolusRate')?.toDouble(); notes = object.get('notes'); } @@ -104,7 +84,7 @@ class LogMeal extends DataTableContent { double? delayedBolusRate, String? notes, }) async { - final logMeal = ParseObject('Meal') + final logMeal = ParseObject('LogMeal') ..set('value', value) ..set('logEntry', (ParseObject('LogEntry')..objectId = logEntry).toPointer()) @@ -161,7 +141,7 @@ class LogMeal extends DataTableContent { double? delayedBolusRate, String? notes, }) async { - var logMeal = ParseObject('Meal')..objectId = objectId; + var logMeal = ParseObject('LogMeal')..objectId = objectId; if (value != null) { logMeal.set('value', value); } diff --git a/lib/screens/accuracy_detail.dart b/lib/screens/accuracy_detail.dart index 370764f..ac2bfac 100644 --- a/lib/screens/accuracy_detail.dart +++ b/lib/screens/accuracy_detail.dart @@ -24,6 +24,8 @@ class _AccuracyDetailScreenState extends State { bool _forCarbsRatio = false; bool _forPortionSize = false; + bool _isSaving = false; + @override void initState() { super.initState(); @@ -38,6 +40,9 @@ class _AccuracyDetailScreenState extends State { } void handleSaveAction() async { + setState(() { + _isSaving = true; + }); if (_accuracyForm.currentState!.validate()) { bool isNew = widget.accuracy == null; isNew @@ -58,6 +63,9 @@ class _AccuracyDetailScreenState extends State { ); Navigator.pop(context, '${isNew ? 'New' : ''} Accuracy saved'); } + setState(() { + _isSaving = false; + }); } void handleCancelAction() { @@ -154,7 +162,7 @@ class _AccuracyDetailScreenState extends State { ), bottomNavigationBar: DetailBottomRow( onCancel: handleCancelAction, - onSave: handleSaveAction, + onSave: _isSaving ? null : handleSaveAction, ), ); } diff --git a/lib/screens/basal/basal_detail.dart b/lib/screens/basal/basal_detail.dart index c632d76..be9a931 100644 --- a/lib/screens/basal/basal_detail.dart +++ b/lib/screens/basal/basal_detail.dart @@ -37,6 +37,8 @@ class _BasalDetailScreenState extends State { final _endTimeController = TextEditingController(text: ''); final _unitsController = TextEditingController(text: ''); + bool _isSaving = false; + @override void initState() { super.initState(); @@ -114,6 +116,9 @@ class _BasalDetailScreenState extends State { } void handleSaveAction() async { + setState(() { + _isSaving = true; + }); if (_basalForm.currentState!.validate()) { await validateTimePeriod().then((value) async { if (value != 'CANCEL') { @@ -137,6 +142,9 @@ class _BasalDetailScreenState extends State { } }); } + setState(() { + _isSaving = false; + }); } void handleCancelAction() { @@ -241,7 +249,7 @@ class _BasalDetailScreenState extends State { ), bottomNavigationBar: DetailBottomRow( onCancel: handleCancelAction, - onSave: handleSaveAction, + onSave: _isSaving ? null : handleSaveAction, ), ); } diff --git a/lib/screens/basal/basal_profile_detail.dart b/lib/screens/basal/basal_profile_detail.dart index d0ce9b1..08872a4 100644 --- a/lib/screens/basal/basal_profile_detail.dart +++ b/lib/screens/basal/basal_profile_detail.dart @@ -30,11 +30,18 @@ class _BasalProfileDetailScreenState extends State { late FloatingActionButton addBasalButton; late IconButton refreshButton; late IconButton closeButton; + late DetailBottomRow detailBottomRow; + + FloatingActionButton? actionButton; + List appBarActions = []; + DetailBottomRow? bottomNav; final _nameController = TextEditingController(text: ''); final _notesController = TextEditingController(text: ''); bool _active = false; + bool _isSaving = false; + @override void initState() { super.initState(); @@ -62,8 +69,14 @@ class _BasalProfileDetailScreenState extends State { icon: const Icon(Icons.close), ); + detailBottomRow = DetailBottomRow( + onCancel: handleCancelAction, + onSave: _isSaving ? null : handleSaveAction, + ); + actionButton = null; appBarActions = [closeButton]; + bottomNav = detailBottomRow; } void refresh({String? message}) { @@ -195,6 +208,9 @@ class _BasalProfileDetailScreenState extends State { } void handleSaveAction() async { + setState(() { + _isSaving = true; + }); if (_basalProfileForm.currentState!.validate()) { await checkActiveProfiles(); bool isNew = widget.basalProfile == null; @@ -211,6 +227,9 @@ class _BasalProfileDetailScreenState extends State { ); Navigator.pop(context, '${isNew ? 'New' : ''} Basal Profile saved'); } + setState(() { + _isSaving = false; + }); } void handleCancelAction() { @@ -235,9 +254,6 @@ class _BasalProfileDetailScreenState extends State { } } - FloatingActionButton? actionButton; - List appBarActions = []; - void renderTabButtons(index) { if (widget.basalProfile != null) { setState(() { @@ -245,10 +261,12 @@ class _BasalProfileDetailScreenState extends State { case 1: actionButton = addBasalButton; appBarActions = [refreshButton, closeButton]; + bottomNav = null; break; default: actionButton = null; appBarActions = [closeButton]; + bottomNav = detailBottomRow; } }); } @@ -262,9 +280,7 @@ class _BasalProfileDetailScreenState extends State { child: Builder(builder: (BuildContext context) { final TabController tabController = DefaultTabController.of(context)!; tabController.addListener(() { - if (tabController.indexIsChanging) { renderTabButtons(tabController.index); - } }); List tabs = [ SingleChildScrollView( @@ -332,13 +348,10 @@ class _BasalProfileDetailScreenState extends State { drawer: const Navigation( currentLocation: BasalProfileDetailScreen.routeName), body: TabBarView(children: tabs), - bottomNavigationBar: DetailBottomRow( - onCancel: handleCancelAction, - onSave: handleSaveAction, - ), + bottomNavigationBar: bottomNav, floatingActionButton: actionButton, floatingActionButtonLocation: - FloatingActionButtonLocation.centerDocked, + FloatingActionButtonLocation.endFloat, ); }), ); diff --git a/lib/screens/bolus/bolus_detail.dart b/lib/screens/bolus/bolus_detail.dart index 0c044f6..678e715 100644 --- a/lib/screens/bolus/bolus_detail.dart +++ b/lib/screens/bolus/bolus_detail.dart @@ -43,6 +43,8 @@ class _BolusDetailScreenState extends State { final _mgPerDlController = TextEditingController(text: ''); final _mmolPerLController = TextEditingController(text: ''); + bool _isSaving = false; + @override void initState() { super.initState(); @@ -123,6 +125,9 @@ class _BolusDetailScreenState extends State { } void handleSaveAction() async { + setState(() { + _isSaving = true; + }); if (_bolusForm.currentState!.validate()) { await validateTimePeriod().then((value) async { if (value != 'CANCEL') { @@ -152,6 +157,9 @@ class _BolusDetailScreenState extends State { } }); } + setState(() { + _isSaving = false; + }); } void handleCancelAction() { @@ -391,7 +399,7 @@ class _BolusDetailScreenState extends State { ), bottomNavigationBar: DetailBottomRow( onCancel: handleCancelAction, - onSave: handleSaveAction, + onSave: _isSaving ? null : handleSaveAction, ), ); } diff --git a/lib/screens/bolus/bolus_profile_detail.dart b/lib/screens/bolus/bolus_profile_detail.dart index 27652b8..1cc6fc0 100644 --- a/lib/screens/bolus/bolus_profile_detail.dart +++ b/lib/screens/bolus/bolus_profile_detail.dart @@ -30,11 +30,18 @@ class _BolusProfileDetailScreenState extends State { late FloatingActionButton addBolusButton; late IconButton refreshButton; late IconButton closeButton; + late DetailBottomRow detailBottomRow; + + FloatingActionButton? actionButton; + List appBarActions = []; + DetailBottomRow? bottomNav; final _nameController = TextEditingController(text: ''); final _notesController = TextEditingController(text: ''); bool _active = false; + bool _isSaving = false; + @override void initState() { super.initState(); @@ -62,8 +69,15 @@ class _BolusProfileDetailScreenState extends State { icon: const Icon(Icons.close), ); + // TODO: fix (saving button doesnt get disabled) + detailBottomRow = DetailBottomRow( + onCancel: handleCancelAction, + onSave: _isSaving ? null : handleSaveAction, + ); + actionButton = null; appBarActions = [closeButton]; + bottomNav = detailBottomRow; } void refresh({String? message}) { @@ -195,6 +209,9 @@ class _BolusProfileDetailScreenState extends State { } void handleSaveAction() async { + setState(() { + _isSaving = true; + }); if (_bolusProfileForm.currentState!.validate()) { await checkActiveProfiles(); bool isNew = widget.bolusProfile == null; @@ -211,6 +228,9 @@ class _BolusProfileDetailScreenState extends State { ); Navigator.pop(context, '${isNew ? 'New' : ''} Bolus Profile saved'); } + setState(() { + _isSaving = false; + }); } void handleCancelAction() { @@ -235,9 +255,6 @@ class _BolusProfileDetailScreenState extends State { } } - FloatingActionButton? actionButton; - List appBarActions = []; - void renderTabButtons(index) { if (widget.bolusProfile != null) { setState(() { @@ -245,10 +262,12 @@ class _BolusProfileDetailScreenState extends State { case 1: actionButton = addBolusButton; appBarActions = [refreshButton, closeButton]; + bottomNav = null; break; default: actionButton = null; appBarActions = [closeButton]; + bottomNav = detailBottomRow; } }); } @@ -262,9 +281,7 @@ class _BolusProfileDetailScreenState extends State { child: Builder(builder: (BuildContext context) { final TabController tabController = DefaultTabController.of(context)!; tabController.addListener(() { - if (tabController.indexIsChanging) { renderTabButtons(tabController.index); - } }); List tabs = [ @@ -334,13 +351,10 @@ class _BolusProfileDetailScreenState extends State { body: TabBarView( children: tabs, ), - bottomNavigationBar: DetailBottomRow( - onCancel: handleCancelAction, - onSave: handleSaveAction, - ), + bottomNavigationBar: bottomNav, floatingActionButton: actionButton, floatingActionButtonLocation: - FloatingActionButtonLocation.centerDocked, + FloatingActionButtonLocation.endFloat, ); }), ); diff --git a/lib/screens/log/log_entry.dart b/lib/screens/log/log_entry.dart index 08dda05..f1fadf3 100644 --- a/lib/screens/log/log_entry.dart +++ b/lib/screens/log/log_entry.dart @@ -28,9 +28,13 @@ class _LogEntryScreenState extends State { late FloatingActionButton addEventButton; late IconButton refreshButton; late IconButton closeButton; + late DetailBottomRow detailBottomRow; - static FloatingActionButton? actionButton; - static List appBarActions = []; + FloatingActionButton? actionButton; + List appBarActions = []; + DetailBottomRow? bottomNav; + + bool _isSaving = false; final formDataControllers = { 'time': TextEditingController(text: ''), @@ -54,6 +58,9 @@ class _LogEntryScreenState extends State { } void handleSaveAction() async { + setState(() { + _isSaving = true; + }); if (logEntryForm.currentState!.validate()) { bool isNew = widget.entry == null; isNew @@ -85,6 +92,9 @@ class _LogEntryScreenState extends State { Navigator.pushReplacementNamed(context, '/log', arguments: '${isNew ? 'New' : ''} Log Entry Saved'); } + setState(() { + _isSaving = false; + }); } void handleCancelAction() { @@ -188,8 +198,14 @@ class _LogEntryScreenState extends State { icon: const Icon(Icons.close), ); + detailBottomRow = DetailBottomRow( + onCancel: handleCancelAction, + onSave: _isSaving ? null : handleSaveAction, + ); + actionButton = null; appBarActions = [closeButton]; + bottomNav = detailBottomRow; } void renderTabButtons(index) { @@ -199,14 +215,17 @@ class _LogEntryScreenState extends State { case 1: actionButton = addMealButton; appBarActions = [refreshButton, closeButton]; + bottomNav = null; break; case 2: actionButton = addEventButton; appBarActions = [refreshButton, closeButton]; + bottomNav = null; break; default: actionButton = null; appBarActions = [closeButton]; + bottomNav = detailBottomRow; } }); } @@ -221,9 +240,7 @@ class _LogEntryScreenState extends State { child: Builder(builder: (BuildContext context) { final TabController tabController = DefaultTabController.of(context)!; tabController.addListener(() { - if (tabController.indexIsChanging) { renderTabButtons(tabController.index); - } }); List tabs = [ LogEntryForm( @@ -253,13 +270,10 @@ class _LogEntryScreenState extends State { body: TabBarView( children: tabs, ), - bottomNavigationBar: DetailBottomRow( - onCancel: handleCancelAction, - onSave: handleSaveAction, - ), + bottomNavigationBar: bottomNav, floatingActionButton: actionButton, floatingActionButtonLocation: - FloatingActionButtonLocation.centerDocked, + FloatingActionButtonLocation.centerDocked, ); }), ); diff --git a/lib/screens/log/log_event_detail.dart b/lib/screens/log/log_event_detail.dart index b9e50a7..632d8a6 100644 --- a/lib/screens/log/log_event_detail.dart +++ b/lib/screens/log/log_event_detail.dart @@ -30,6 +30,8 @@ class _LogEventDetailScreenState extends State { late Future> _logEventTypes; + bool _isSaving = false; + @override void initState() { super.initState(); @@ -44,6 +46,9 @@ class _LogEventDetailScreenState extends State { } void handleSaveAction() async { + setState(() { + _isSaving = true; + }); if (_logEventForm.currentState!.validate()) { bool isNew = widget.logEvent == null; isNew @@ -63,6 +68,9 @@ class _LogEventDetailScreenState extends State { ); Navigator.pop(context, '${isNew ? 'New' : ''} Event Saved'); } + setState(() { + _isSaving = false; + }); } void handleCancelAction() { @@ -138,7 +146,7 @@ class _LogEventDetailScreenState extends State { ), bottomNavigationBar: DetailBottomRow( onCancel: handleCancelAction, - onSave: handleSaveAction, + onSave: _isSaving ? null : handleSaveAction, ), ); } diff --git a/lib/screens/log/log_event_type_detail.dart b/lib/screens/log/log_event_type_detail.dart index 3aa973b..367eed3 100644 --- a/lib/screens/log/log_event_type_detail.dart +++ b/lib/screens/log/log_event_type_detail.dart @@ -24,6 +24,8 @@ class _LogEventTypeDetailScreenState extends State { final _notesController = TextEditingController(text: ''); bool _hasEndTime = false; + bool _isSaving = false; + @override void initState() { super.initState(); @@ -38,6 +40,9 @@ class _LogEventTypeDetailScreenState extends State { } void handleSaveAction() async { + setState(() { + _isSaving = true; + }); if (_logEventTypeForm.currentState!.validate()) { bool isNew = widget.logEventType == null; isNew @@ -58,6 +63,9 @@ class _LogEventTypeDetailScreenState extends State { ); Navigator.pop(context, '${isNew ? 'New' : ''} Log Event Type Saved'); } + setState(() { + _isSaving = false; + }); } void handleCancelAction() { @@ -148,7 +156,7 @@ class _LogEventTypeDetailScreenState extends State { ), bottomNavigationBar: DetailBottomRow( onCancel: handleCancelAction, - onSave: handleSaveAction, + onSave: _isSaving ? null : handleSaveAction, ), ); } diff --git a/lib/screens/log/log_meal_detail.dart b/lib/screens/log/log_meal_detail.dart index d811209..b0da88f 100644 --- a/lib/screens/log/log_meal_detail.dart +++ b/lib/screens/log/log_meal_detail.dart @@ -49,6 +49,8 @@ class _LogMealDetailScreenState extends State { late Future> _portionSizeAccuracies; late Future> _carbsRatioAccuracies; + bool _isSaving = false; + @override void initState() { super.initState(); @@ -128,6 +130,9 @@ class _LogMealDetailScreenState extends State { } void handleSaveAction() async { + setState(() { + _isSaving = true; + }); if (_logMealForm.currentState!.validate()) { bool isNew = widget.logMeal == null; isNew @@ -143,6 +148,7 @@ class _LogMealDetailScreenState extends State { carbsPerPortion: double.tryParse(_carbsPerPortionController.text), portionSizeAccuracy: _portionSizeAccuracy, carbsRatioAccuracy: _carbsRatioAccuracy, + bolus: double.tryParse(_bolusController.text), delayedBolusDuration: int.tryParse(_delayedBolusDurationController.text), delayedBolusRate: @@ -161,6 +167,7 @@ class _LogMealDetailScreenState extends State { carbsPerPortion: double.tryParse(_carbsPerPortionController.text), portionSizeAccuracy: _portionSizeAccuracy, carbsRatioAccuracy: _carbsRatioAccuracy, + bolus: double.tryParse(_bolusController.text), delayedBolusDuration: int.tryParse(_delayedBolusDurationController.text), delayedBolusRate: @@ -169,6 +176,9 @@ class _LogMealDetailScreenState extends State { ); Navigator.pop(context, '${isNew ? 'New' : ''} Meal Saved'); } + setState(() { + _isSaving = false; + }); } void handleCancelAction() { @@ -185,6 +195,7 @@ class _LogMealDetailScreenState extends State { double.tryParse(_carbsPerPortionController.text) != null || _carbsRatioAccuracy != null || _portionSizeAccuracy != null || + double.tryParse(_bolusController.text) != null || int.tryParse(_delayedBolusDurationController.text) != null || double.tryParse(_delayedBolusRateController.text) != null || @@ -204,6 +215,7 @@ class _LogMealDetailScreenState extends State { _carbsRatioAccuracy != widget.logMeal!.carbsRatioAccuracy || _portionSizeAccuracy != widget.logMeal!.portionSizeAccuracy || + double.tryParse(_bolusController.text) != widget.logMeal!.bolus || int.tryParse(_delayedBolusDurationController.text) != widget.logMeal!.delayedBolusDuration || double.tryParse(_delayedBolusRateController.text) != @@ -486,7 +498,7 @@ class _LogMealDetailScreenState extends State { ), bottomNavigationBar: DetailBottomRow( onCancel: handleCancelAction, - onSave: handleSaveAction, + onSave: _isSaving ? null : handleSaveAction, ), ); } diff --git a/lib/screens/log/log_meal_list.dart b/lib/screens/log/log_meal_list.dart index 072280f..05945e1 100644 --- a/lib/screens/log/log_meal_list.dart +++ b/lib/screens/log/log_meal_list.dart @@ -86,35 +86,32 @@ class _LogMealListScreenState extends State { padding: EdgeInsets.all(10.0), child: Text('No Meals for this Log Entry'), ) - : ListBody( - children: [ - DataTable( - columnSpacing: 10.0, - showCheckboxColumn: false, - rows: snapshot.data != null - ? snapshot.data!.map((meal) { - return DataRow( - cells: meal.asDataTableCells( - [ - IconButton( - icon: const Icon(Icons.edit), - iconSize: 16.0, - onPressed: () => - handleEditAction(meal)), - IconButton( - icon: const Icon(Icons.delete), - iconSize: 16.0, - onPressed: () => - handleDeleteAction(meal)), - ], - ), - ); - }).toList() - : [], - columns: LogMeal.asDataTableColumns(), + : ListView.builder( + shrinkWrap: true, + itemCount: snapshot.data != null ? snapshot.data!.length : 0, + itemBuilder: (context, index) { + final meal = snapshot.data![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), + ), + ); + }, + ), ); }, ), diff --git a/lib/screens/meal/meal_detail.dart b/lib/screens/meal/meal_detail.dart index 7f4e9fd..97f56f0 100644 --- a/lib/screens/meal/meal_detail.dart +++ b/lib/screens/meal/meal_detail.dart @@ -43,6 +43,8 @@ class _MealDetailScreenState extends State { late Future> _portionSizeAccuracies; late Future> _carbsRatioAccuracies; + bool isSaving = false; + @override void initState() { super.initState(); @@ -74,6 +76,9 @@ class _MealDetailScreenState extends State { } void handleSaveAction() async { + setState(() { + isSaving = true; + }); if (_mealForm.currentState!.validate()) { bool isNew = widget.meal == null; isNew @@ -112,6 +117,9 @@ class _MealDetailScreenState extends State { ); Navigator.pop(context, '${isNew ? 'New' : ''} Meal Saved'); } + setState(() { + isSaving = false; + }); } void handleCancelAction() { @@ -430,7 +438,7 @@ class _MealDetailScreenState extends State { ), bottomNavigationBar: DetailBottomRow( onCancel: handleCancelAction, - onSave: handleSaveAction, + onSave: isSaving ? null : handleSaveAction, ), ); } diff --git a/pubspec.lock b/pubspec.lock index 975a498..9863a55 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -14,7 +14,7 @@ packages: name: async url: "https://pub.dartlang.org" source: hosted - version: "2.8.2" + version: "2.8.1" boolean_selector: dependency: transitive description: @@ -28,7 +28,7 @@ packages: name: characters url: "https://pub.dartlang.org" source: hosted - version: "1.2.0" + version: "1.1.0" charcode: dependency: transitive description: @@ -70,7 +70,7 @@ packages: name: connectivity_plus_macos url: "https://pub.dartlang.org" source: hosted - version: "1.2.0" + version: "1.2.1" connectivity_plus_platform_interface: dependency: transitive description: @@ -119,7 +119,7 @@ packages: name: dio url: "https://pub.dartlang.org" source: hosted - version: "4.0.0" + version: "4.0.1" fake_async: dependency: transitive description: @@ -218,7 +218,7 @@ packages: name: matcher url: "https://pub.dartlang.org" source: hosted - version: "0.12.11" + version: "0.12.10" meta: dependency: transitive description: @@ -267,7 +267,7 @@ packages: name: package_info_plus_macos url: "https://pub.dartlang.org" source: hosted - version: "1.2.0" + version: "1.3.0" package_info_plus_platform_interface: dependency: transitive description: @@ -358,7 +358,7 @@ packages: name: petitparser url: "https://pub.dartlang.org" source: hosted - version: "4.3.0" + version: "4.4.0" platform: dependency: transitive description: @@ -379,7 +379,7 @@ packages: name: process url: "https://pub.dartlang.org" source: hosted - version: "4.2.3" + version: "4.2.4" provider: dependency: "direct dev" description: @@ -510,7 +510,7 @@ packages: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.4.3" + version: "0.4.2" typed_data: dependency: transitive description: @@ -531,7 +531,7 @@ packages: name: vector_math url: "https://pub.dartlang.org" source: hosted - version: "2.1.1" + version: "2.1.0" web_socket_channel: dependency: transitive description: @@ -545,7 +545,7 @@ packages: name: win32 url: "https://pub.dartlang.org" source: hosted - version: "2.2.9" + version: "2.2.10" xdg_directories: dependency: transitive description: @@ -559,7 +559,7 @@ packages: name: xml url: "https://pub.dartlang.org" source: hosted - version: "5.3.0" + version: "5.3.1" xxtea: dependency: transitive description: