From 32c7ca080324dd58953db4424d836f59f696761e Mon Sep 17 00:00:00 2001 From: Miroslav Mazel Date: Thu, 23 Nov 2023 13:15:03 +0100 Subject: [PATCH 1/5] Centered titles on macOS / iOS --- lib/screens/nutritional_plan_screen.dart | 1 + lib/screens/workout_plan_screen.dart | 1 + 2 files changed, 2 insertions(+) diff --git a/lib/screens/nutritional_plan_screen.dart b/lib/screens/nutritional_plan_screen.dart index c590464e..f79dddc4 100644 --- a/lib/screens/nutritional_plan_screen.dart +++ b/lib/screens/nutritional_plan_screen.dart @@ -107,6 +107,7 @@ class NutritionalPlanScreen extends StatelessWidget { ), ], flexibleSpace: FlexibleSpaceBar( + titlePadding: const EdgeInsets.fromLTRB(56, 0, 56, 16), title: Text( nutritionalPlan.getLabel(context), style: Theme.of(context).textTheme.titleLarge?.copyWith(color: appBarForeground), diff --git a/lib/screens/workout_plan_screen.dart b/lib/screens/workout_plan_screen.dart index 0fb01343..b0e175f1 100644 --- a/lib/screens/workout_plan_screen.dart +++ b/lib/screens/workout_plan_screen.dart @@ -82,6 +82,7 @@ class _WorkoutPlanScreenState extends State { iconTheme: const IconThemeData(color: appBarForeground), backgroundColor: wgerPrimaryColor, flexibleSpace: FlexibleSpaceBar( + titlePadding: const EdgeInsets.fromLTRB(56, 0, 56, 16), title: Text( workoutPlan.name, style: Theme.of(context).textTheme.titleLarge?.copyWith(color: appBarForeground), From bc1cfc5ca11712cc43b50ea00aa65303a792e9d9 Mon Sep 17 00:00:00 2001 From: Miroslav Mazel Date: Thu, 23 Nov 2023 13:15:12 +0100 Subject: [PATCH 2/5] macOS Podfile update --- macos/Podfile.lock | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/macos/Podfile.lock b/macos/Podfile.lock index 72fa8204..2c359a41 100644 --- a/macos/Podfile.lock +++ b/macos/Podfile.lock @@ -13,6 +13,9 @@ PODS: - FlutterMacOS - url_launcher_macos (0.0.1): - FlutterMacOS + - video_player_avfoundation (0.0.1): + - Flutter + - FlutterMacOS DEPENDENCIES: - file_selector_macos (from `Flutter/ephemeral/.symlinks/plugins/file_selector_macos/macos`) @@ -22,6 +25,7 @@ DEPENDENCIES: - rive_common (from `Flutter/ephemeral/.symlinks/plugins/rive_common/macos`) - shared_preferences_foundation (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin`) - url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`) + - video_player_avfoundation (from `Flutter/ephemeral/.symlinks/plugins/video_player_avfoundation/darwin`) EXTERNAL SOURCES: file_selector_macos: @@ -38,15 +42,18 @@ EXTERNAL SOURCES: :path: Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin url_launcher_macos: :path: Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos + video_player_avfoundation: + :path: Flutter/ephemeral/.symlinks/plugins/video_player_avfoundation/darwin SPEC CHECKSUMS: - file_selector_macos: 0f85c1108e2fd597b58246bc0b0c1cb483d7593b + file_selector_macos: 468fb6b81fac7c0e88d71317f3eec34c3b008ff9 flutter_zxing: ea030278aa2a051cd81a5b0053be9d0569d11c9a FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24 package_info_plus: 02d7a575e80f194102bef286361c6c326e4c29ce - rive_common: acedcab7802c0ece4b0d838b71d7deb637e1309a - shared_preferences_foundation: 986fc17f3d3251412d18b0265f9c64113a8c2472 - url_launcher_macos: 5335912b679c073563f29d89d33d10d459f95451 + rive_common: 0f0aadf670f0c6a7872dfe3e6186f112a5319108 + shared_preferences_foundation: 5b919d13b803cadd15ed2dc053125c68730e5126 + url_launcher_macos: d2691c7dd33ed713bf3544850a623080ec693d95 + video_player_avfoundation: 8563f13d8fc8b2c29dc2d09e60b660e4e8128837 PODFILE CHECKSUM: 0d3963a09fc94f580682bd88480486da345dc3f0 From 1983d3890af10155d03e0102e9aa65bf2bae714c Mon Sep 17 00:00:00 2001 From: Miroslav Mazel Date: Thu, 23 Nov 2023 13:53:06 +0100 Subject: [PATCH 3/5] GymMode: Use Table of Contents icon, not Menu --- lib/widgets/workouts/gym_mode.dart | 2 +- test/workout/gym_mode_screen_test.dart | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/widgets/workouts/gym_mode.dart b/lib/widgets/workouts/gym_mode.dart index b188d52d..c1f996c2 100644 --- a/lib/widgets/workouts/gym_mode.dart +++ b/lib/widgets/workouts/gym_mode.dart @@ -1130,7 +1130,7 @@ class NavigationHeader extends StatelessWidget { ), ), IconButton( - icon: const Icon(Icons.menu), + icon: const Icon(Icons.toc), onPressed: () { showDialog( context: context, diff --git a/test/workout/gym_mode_screen_test.dart b/test/workout/gym_mode_screen_test.dart index 54efa024..7ce88d37 100644 --- a/test/workout/gym_mode_screen_test.dart +++ b/test/workout/gym_mode_screen_test.dart @@ -90,7 +90,7 @@ void main() { expect(find.text('Bench press'), findsOneWidget); expect(find.text('Side raises'), findsOneWidget); expect(find.byIcon(Icons.close), findsOneWidget); - expect(find.byIcon(Icons.menu), findsOneWidget); + expect(find.byIcon(Icons.toc), findsOneWidget); expect(find.byIcon(Icons.chevron_left), findsNothing); expect(find.byIcon(Icons.chevron_right), findsOneWidget); await tester.tap(find.byIcon(Icons.chevron_right)); @@ -102,7 +102,7 @@ void main() { expect(find.text('Bench press'), findsOneWidget); expect(find.byType(ExerciseOverview), findsOneWidget); expect(find.byIcon(Icons.close), findsOneWidget); - expect(find.byIcon(Icons.menu), findsOneWidget); + expect(find.byIcon(Icons.toc), findsOneWidget); expect(find.byIcon(Icons.chevron_left), findsOneWidget); expect(find.byIcon(Icons.chevron_right), findsOneWidget); await tester.drag(find.byType(ExerciseOverview), const Offset(-500.0, 0.0)); @@ -119,7 +119,7 @@ void main() { expect(find.text('12 × 10 kg (2 RiR)'), findsOneWidget); expect(find.text('Make sure to warm up'), findsOneWidget, reason: 'Set comment'); expect(find.byIcon(Icons.close), findsOneWidget); - expect(find.byIcon(Icons.menu), findsOneWidget); + expect(find.byIcon(Icons.toc), findsOneWidget); expect(find.byIcon(Icons.chevron_left), findsOneWidget); expect(find.byIcon(Icons.chevron_right), findsOneWidget); @@ -145,7 +145,7 @@ void main() { expect(find.text('Pause'), findsOneWidget); expect(find.byType(TimerWidget), findsOneWidget); expect(find.byIcon(Icons.close), findsOneWidget); - expect(find.byIcon(Icons.menu), findsOneWidget); + expect(find.byIcon(Icons.toc), findsOneWidget); expect(find.byIcon(Icons.chevron_left), findsOneWidget); expect(find.byIcon(Icons.chevron_right), findsOneWidget); await tester.tap(find.byIcon(Icons.chevron_right)); From 814acd14c7eda99c16e281d284aeb0dc77d3c6a1 Mon Sep 17 00:00:00 2001 From: Miroslav Mazel Date: Fri, 24 Nov 2023 13:01:30 +0100 Subject: [PATCH 4/5] Meals + workouts: Removing Dismissible --- lib/l10n/app_en.arb | 8 +- lib/widgets/nutrition/meal.dart | 302 +++++++++--------- .../nutrition/nutritional_diary_detail.dart | 1 + .../nutrition/nutritional_plans_list.dart | 134 ++++---- lib/widgets/workouts/day.dart | 230 ++++++------- lib/widgets/workouts/workout_plans_list.dart | 132 ++++---- .../nutritional_plan_screen_test.dart | 2 +- .../nutritional_plans_screen_test.dart | 7 +- test/workout/workout_plan_screen_test.dart | 2 +- test/workout/workout_plans_screen_test.dart | 7 +- 10 files changed, 389 insertions(+), 436 deletions(-) diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index f4a2de7b..65007b5d 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -798,5 +798,11 @@ "none__bodyweight_exercise_": "none (bodyweight exercise)", "@none__bodyweight_exercise_": { "description": "Generated entry for translation for server strings" - } + }, + "editSchedule": "Edit schedule", + "log": "Log", + "@log": { + "description": "Log a specific meal" + }, + "done": "Done" } diff --git a/lib/widgets/nutrition/meal.dart b/lib/widgets/nutrition/meal.dart index 381ef006..2002c0ae 100644 --- a/lib/widgets/nutrition/meal.dart +++ b/lib/widgets/nutrition/meal.dart @@ -43,11 +43,18 @@ class MealWidget extends StatefulWidget { } class _MealWidgetState extends State { - bool _expanded = false; + bool _showingDetails = false; + bool _editing = false; - void _toggleExpanded() { + void _toggleEditing() { setState(() { - _expanded = !_expanded; + _editing = !_editing; + }); + } + + void _toggleDetails() { + setState(() { + _showingDetails = !_showingDetails; }); } @@ -57,85 +64,72 @@ class _MealWidgetState extends State { padding: const EdgeInsets.all(3), child: Card( child: Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - DismissibleMealHeader(_expanded, _toggleExpanded, meal: widget._meal), - if (_expanded) - Row( - mainAxisSize: MainAxisSize.max, - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - IconButton( - onPressed: () { - // Delete the meal - Provider.of(context, listen: false) - .deleteMeal(widget._meal); - - // and inform the user - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text( - AppLocalizations.of(context).successfullyDeleted, - textAlign: TextAlign.center, - ), - ), - ); - }, - icon: const Icon(Icons.delete), - ), - if (widget._meal.mealItems.isNotEmpty) - Ink( - decoration: ShapeDecoration( - color: Theme.of(context).primaryColor, //wgerPrimaryButtonColor, - shape: const CircleBorder(), - ), - child: IconButton( - icon: const Icon(Icons.history_edu), - color: Colors.white, + MealHeader( + editing: _editing, + toggleEditing: _toggleEditing, + showingDetails: _showingDetails, + toggleDetails: _toggleDetails, + meal: widget._meal), + if (_editing) + Padding( + padding: const EdgeInsets.symmetric(vertical: 8), + child: Wrap( + spacing: 8, + children: [ + TextButton.icon( + icon: const Icon(Icons.add), + label: Text(AppLocalizations.of(context).addIngredient), onPressed: () { - Provider.of(context, listen: false) - .logMealToDiary(widget._meal); - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text( - AppLocalizations.of(context).mealLogged, - textAlign: TextAlign.center, - ), + Navigator.pushNamed( + context, + FormScreen.routeName, + arguments: FormScreenArguments( + AppLocalizations.of(context).addIngredient, + MealItemForm(widget._meal, widget._listMealItems), + hasListView: true, ), ); }, ), - ), - IconButton( - onPressed: () { - Navigator.pushNamed( - context, - FormScreen.routeName, - arguments: FormScreenArguments( - AppLocalizations.of(context).edit, - MealForm(widget._meal.planId, widget._meal), - ), - ); - }, - icon: const Icon(Icons.edit), - ), - ], - ), - if (_expanded) const Divider(), - ...widget._meal.mealItems.map((item) => MealItemWidget(item, _expanded)), - OutlinedButton( - child: Text(AppLocalizations.of(context).addIngredient), - onPressed: () { - Navigator.pushNamed( - context, - FormScreen.routeName, - arguments: FormScreenArguments( - AppLocalizations.of(context).addIngredient, - MealItemForm(widget._meal, widget._listMealItems), - hasListView: true, - ), - ); - }, - ), + TextButton.icon( + label: Text(AppLocalizations.of(context).editSchedule), + onPressed: () { + Navigator.pushNamed( + context, + FormScreen.routeName, + arguments: FormScreenArguments( + AppLocalizations.of(context).edit, + MealForm(widget._meal.planId, widget._meal), + ), + ); + }, + icon: const Icon(Icons.timer), + ), + TextButton.icon( + onPressed: () { + // Delete the meal + Provider.of(context, listen: false) + .deleteMeal(widget._meal); + + // and inform the user + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text( + AppLocalizations.of(context).successfullyDeleted, + textAlign: TextAlign.center, + ), + ), + ); + }, + label: Text(AppLocalizations.of(context).delete), + icon: const Icon(Icons.delete)), + ], + )), + const Divider(), + ...widget._meal.mealItems + .map((item) => MealItemWidget(item, _showingDetails, _editing)), ], ), ), @@ -144,10 +138,11 @@ class _MealWidgetState extends State { } class MealItemWidget extends StatelessWidget { - final bool _expanded; + final bool _editing; + final bool _showingDetails; final MealItem _item; - const MealItemWidget(this._item, this._expanded); + const MealItemWidget(this._item, this._showingDetails, this._editing); @override Widget build(BuildContext context) { @@ -180,11 +175,12 @@ class MealItemWidget extends StatelessWidget { ), subtitle: Column( crossAxisAlignment: CrossAxisAlignment.start, - children: [if (_expanded) ...getMutedNutritionalValues(values, context)], + children: [if (_showingDetails) ...getMutedNutritionalValues(values, context)], ), - trailing: _expanded + trailing: _editing ? IconButton( icon: const Icon(Icons.delete), + tooltip: AppLocalizations.of(context).delete, iconSize: ICON_SIZE_SMALL, onPressed: () { // Delete the meal item @@ -205,87 +201,95 @@ class MealItemWidget extends StatelessWidget { } } -class DismissibleMealHeader extends StatelessWidget { - final bool _expanded; - final Function _toggle; +class MealHeader extends StatelessWidget { + final bool _editing; + final bool _showingDetails; + final Function _toggleEditing; + final Function _toggleDetails; - const DismissibleMealHeader( - this._expanded, - this._toggle, { + const MealHeader({ required Meal meal, - }) : _meal = meal; + required bool editing, + required Function toggleEditing, + required bool showingDetails, + required Function toggleDetails, + }) : _toggleDetails = toggleDetails, + _toggleEditing = toggleEditing, + _showingDetails = showingDetails, + _editing = editing, + _meal = meal; final Meal _meal; @override Widget build(BuildContext context) { - return Dismissible( - key: Key(_meal.id.toString()), - direction: DismissDirection.startToEnd, - background: Container( - color: Theme.of(context).primaryColor, //wgerPrimaryButtonColor, - alignment: Alignment.centerLeft, - padding: const EdgeInsets.only(left: 10), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ListTile( + contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), + title: Row(children: [ + Expanded( + child: (_meal.name != '') + ? Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + _meal.name, + style: Theme.of(context).textTheme.titleMedium, + ), + Text( + _meal.time!.format(context), + style: Theme.of(context).textTheme.headlineSmall, + ) + ], + ) + : Text( + _meal.time!.format(context), + style: Theme.of(context).textTheme.headlineSmall, + ), + ), Text( - AppLocalizations.of(context).logMeal, - style: const TextStyle(color: Colors.white), + AppLocalizations.of(context).log, + style: Theme.of(context) + .textTheme + .labelLarge + ?.copyWith(color: Theme.of(context).colorScheme.primary), ), - const Icon( - Icons.history_edu, - color: Colors.white, + const SizedBox(width: 26), + const SizedBox(height: 40, width: 1, child: VerticalDivider()), + ]), + trailing: Row(mainAxisSize: MainAxisSize.min, children: [ + IconButton( + icon: _showingDetails ? const Icon(Icons.info) : const Icon(Icons.info_outline), + onPressed: () { + _toggleDetails(); + }, + tooltip: AppLocalizations.of(context).toggleDetails, ), - ], - ), - ), - confirmDismiss: (direction) async { - // Delete - if (direction == DismissDirection.startToEnd) { - Provider.of(context, listen: false).logMealToDiary(_meal); - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text( - AppLocalizations.of(context).mealLogged, - textAlign: TextAlign.center, - ), - ), - ); - } - return false; - }, - child: Container( - padding: const EdgeInsets.all(10), - decoration: BoxDecoration(color: Theme.of(context).colorScheme.inversePrimary), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - if (_meal.name != '') - Text( - _meal.name, - style: Theme.of(context).textTheme.headlineSmall, - ), - Row( - children: [ - Expanded( - child: Text( - _meal.time!.format(context), - style: Theme.of(context).textTheme.headlineSmall, - ), + const SizedBox(width: 5), + IconButton( + icon: _editing ? const Icon(Icons.done) : const Icon(Icons.edit), + tooltip: + _editing ? AppLocalizations.of(context).done : AppLocalizations.of(context).edit, + onPressed: () { + _toggleEditing(); + }, + ) + ]), + onTap: () { + Provider.of(context, listen: false).logMealToDiary(_meal); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text( + AppLocalizations.of(context).mealLogged, + textAlign: TextAlign.center, ), - IconButton( - visualDensity: VisualDensity.compact, - icon: _expanded ? const Icon(Icons.unfold_less) : const Icon(Icons.unfold_more), - onPressed: () { - _toggle(); - }, - ), - ], - ), - ], + ), + ); + }, ), - ), + ], ); } } diff --git a/lib/widgets/nutrition/nutritional_diary_detail.dart b/lib/widgets/nutrition/nutritional_diary_detail.dart index af07c291..d1080e4f 100644 --- a/lib/widgets/nutrition/nutritional_diary_detail.dart +++ b/lib/widgets/nutrition/nutritional_diary_detail.dart @@ -195,6 +195,7 @@ class NutritionalDiaryDetailWidget extends StatelessWidget { ), ), IconButton( + tooltip: AppLocalizations.of(context).delete, onPressed: () { Provider.of(context, listen: false) .deleteLog(log.id!, _nutritionalPlan.id!); diff --git a/lib/widgets/nutrition/nutritional_plans_list.dart b/lib/widgets/nutrition/nutritional_plans_list.dart index 58b11210..3377a6c9 100644 --- a/lib/widgets/nutrition/nutritional_plans_list.dart +++ b/lib/widgets/nutrition/nutritional_plans_list.dart @@ -39,78 +39,70 @@ class NutritionalPlansList extends StatelessWidget { itemCount: _nutritionProvider.items.length, itemBuilder: (context, index) { final currentPlan = _nutritionProvider.items[index]; - return Dismissible( - key: Key(currentPlan.id.toString()), - confirmDismiss: (direction) async { - // Delete workout from DB - final bool? res = await showDialog( - context: context, - builder: (BuildContext contextDialog) { - return AlertDialog( - content: Text( - AppLocalizations.of(context).confirmDelete(currentPlan.description), - ), - actions: [ - TextButton( - child: Text(MaterialLocalizations.of(context).cancelButtonLabel), - onPressed: () => Navigator.of(contextDialog).pop(), - ), - TextButton( - child: Text( - AppLocalizations.of(context).delete, - style: TextStyle(color: Theme.of(context).colorScheme.error), - ), - onPressed: () { - // Confirmed, delete the workout - _nutritionProvider.deletePlan(currentPlan.id!); - - // Close the popup - Navigator.of(contextDialog).pop(); - - // and inform the user - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text( - AppLocalizations.of(context).successfullyDeleted, - textAlign: TextAlign.center, - ), + return Card( + child: ListTile( + onTap: () { + Navigator.of(context).pushNamed( + NutritionalPlanScreen.routeName, + arguments: currentPlan, + ); + }, + title: Text(currentPlan.getLabel(context)), + subtitle: Text( + DateFormat.yMd(Localizations.localeOf(context).languageCode) + .format(currentPlan.creationDate), + ), + trailing: Row(mainAxisSize: MainAxisSize.min, children: [ + const VerticalDivider(), + IconButton( + icon: const Icon(Icons.delete), + tooltip: AppLocalizations.of(context).delete, + onPressed: () async { + // Delete workout from DB + await showDialog( + context: context, + builder: (BuildContext contextDialog) { + return AlertDialog( + content: Text( + AppLocalizations.of(context) + .confirmDelete(currentPlan.description), + ), + actions: [ + TextButton( + child: + Text(MaterialLocalizations.of(context).cancelButtonLabel), + onPressed: () => Navigator.of(contextDialog).pop(), ), - ); - }, - ), - ], - ); - }); - return res; - }, - background: Container( - color: Theme.of(context).colorScheme.error, - alignment: Alignment.centerRight, - padding: const EdgeInsets.only(right: 20), - margin: const EdgeInsets.symmetric( - horizontal: 4, - vertical: 4, - ), - child: const Icon( - Icons.delete, - color: Colors.white, - ), - ), - direction: DismissDirection.endToStart, - child: Card( - child: ListTile( - onTap: () { - Navigator.of(context).pushNamed( - NutritionalPlanScreen.routeName, - arguments: currentPlan, - ); - }, - title: Text(currentPlan.getLabel(context)), - subtitle: Text( - DateFormat.yMd(Localizations.localeOf(context).languageCode) - .format(currentPlan.creationDate), - ), - ), + TextButton( + child: Text( + AppLocalizations.of(context).delete, + style: + TextStyle(color: Theme.of(context).colorScheme.error), + ), + onPressed: () { + // Confirmed, delete the workout + _nutritionProvider.deletePlan(currentPlan.id!); + + // Close the popup + Navigator.of(contextDialog).pop(); + + // and inform the user + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text( + AppLocalizations.of(context).successfullyDeleted, + textAlign: TextAlign.center, + ), + ), + ); + }, + ), + ], + ); + }); + }, + ) + ]), ), ); }, diff --git a/lib/widgets/workouts/day.dart b/lib/widgets/workouts/day.dart index dbd923dc..0229df1c 100644 --- a/lib/widgets/workouts/day.dart +++ b/lib/widgets/workouts/day.dart @@ -97,7 +97,7 @@ class WorkoutDayWidget extends StatefulWidget { } class _WorkoutDayWidgetState extends State { - bool _expanded = false; + bool _editing = false; late List _sets; @override @@ -109,20 +109,21 @@ class _WorkoutDayWidgetState extends State { void _toggleExpanded() { setState(() { - _expanded = !_expanded; + _editing = !_editing; }); } Widget getSetRow(Set set, int index) { return Row( key: ValueKey(set.id), - crossAxisAlignment: CrossAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.center, children: [ - if (_expanded) + if (_editing) IconButton( visualDensity: VisualDensity.compact, icon: const Icon(Icons.delete), iconSize: ICON_SIZE_SMALL, + tooltip: AppLocalizations.of(context).delete, onPressed: () { Provider.of(context, listen: false).deleteSet(set); }, @@ -131,21 +132,19 @@ class _WorkoutDayWidgetState extends State { child: Column( children: [ if (set.comment != '') MutedText(set.comment), - ...set.settingsFiltered - .map( - (setting) => SettingWidget( - set: set, - setting: setting, - expanded: _expanded, - toggle: _toggleExpanded, - ), - ) - , + ...set.settingsFiltered.map( + (setting) => SettingWidget( + set: set, + setting: setting, + expanded: _editing, + toggle: _toggleExpanded, + ), + ), const Divider(), ], ), ), - if (_expanded) + if (_editing) ReorderableDragStartListener( index: index, child: const IconButton( @@ -164,62 +163,65 @@ class _WorkoutDayWidgetState extends State { child: Card( margin: EdgeInsets.zero, child: Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - DayHeaderDismissible( + DayHeader( day: widget._day, - expanded: _expanded, + expanded: _editing, toggle: _toggleExpanded, ), - if (_expanded) - Row( - mainAxisSize: MainAxisSize.max, - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - IconButton( - onPressed: () { - Provider.of(context, listen: false).deleteDay( - widget._day, - ); - }, - icon: const Icon(Icons.delete), - ), - if (widget._day.sets.isNotEmpty) - Ink( - decoration: ShapeDecoration( - color: Theme.of(context).primaryColor, - shape: const CircleBorder(), - ), - child: IconButton( - icon: const Icon(Icons.play_arrow), - color: Colors.white, + if (_editing) + Padding( + padding: const EdgeInsets.symmetric(vertical: 8), + child: Wrap( + spacing: 8, + children: [ + TextButton.icon( + label: Text(AppLocalizations.of(context).addSet), + icon: const Icon(Icons.add), onPressed: () { - Navigator.of(context).pushNamed( - GymModeScreen.routeName, - arguments: widget._day, + Navigator.pushNamed( + context, + FormScreen.routeName, + arguments: FormScreenArguments( + AppLocalizations.of(context).newSet, + SetFormWidget(widget._day), + hasListView: true, + padding: EdgeInsets.zero, + ), ); }, ), - ), - IconButton( - onPressed: () { - Navigator.pushNamed( - context, - FormScreen.routeName, - arguments: FormScreenArguments( - AppLocalizations.of(context).edit, - DayFormWidget( - Provider.of(context, listen: false) - .findById(widget._day.workoutId), - widget._day), - hasListView: true, - ), - ); - }, - icon: const Icon(Icons.edit), - ), - ], - ), - if (_expanded) const Divider(), + TextButton.icon( + icon: const Icon(Icons.calendar_month), + label: Text(AppLocalizations.of(context).editSchedule), + onPressed: () { + Navigator.pushNamed( + context, + FormScreen.routeName, + arguments: FormScreenArguments( + AppLocalizations.of(context).edit, + DayFormWidget( + Provider.of(context, listen: false) + .findById(widget._day.workoutId), + widget._day), + hasListView: true, + ), + ); + }, + ), + TextButton.icon( + icon: const Icon(Icons.delete), + label: Text(AppLocalizations.of(context).delete), + onPressed: () { + Provider.of(context, listen: false).deleteDay( + widget._day, + ); + }, + ), + ], + )), + const Divider(), ReorderableListView( physics: const NeverScrollableScrollPhysics(), shrinkWrap: true, @@ -242,21 +244,6 @@ class _WorkoutDayWidgetState extends State { for (var i = 0; i < widget._day.sets.length; i++) getSetRow(widget._day.sets[i], i), ], ), - OutlinedButton( - child: Text(AppLocalizations.of(context).addSet), - onPressed: () { - Navigator.pushNamed( - context, - FormScreen.routeName, - arguments: FormScreenArguments( - AppLocalizations.of(context).newSet, - SetFormWidget(widget._day), - hasListView: true, - padding: EdgeInsets.zero, - ), - ); - }, - ), ], ), ), @@ -264,77 +251,48 @@ class _WorkoutDayWidgetState extends State { } } -class DayHeaderDismissible extends StatelessWidget { +class DayHeader extends StatelessWidget { final Day _day; - final bool _expanded; + final bool _editing; final Function _toggle; - const DayHeaderDismissible({ + const DayHeader({ required Day day, required bool expanded, required Function toggle, }) : _day = day, - _expanded = expanded, + _editing = expanded, _toggle = toggle; @override Widget build(BuildContext context) { - return Dismissible( - key: Key(_day.id.toString()), - direction: DismissDirection.startToEnd, - background: Container( - color: Theme.of(context).primaryColor, - alignment: Alignment.centerLeft, - padding: const EdgeInsets.only(left: 10), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - AppLocalizations.of(context).gymMode, - style: const TextStyle(color: Colors.white), - ), - const Icon( - Icons.play_arrow, - color: Colors.white, - ), - ], - ), + return ListTile( + contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), + title: Text( + _day.description, + style: Theme.of(context).textTheme.headlineSmall, + overflow: TextOverflow.ellipsis, ), - confirmDismiss: (direction) async { - // Delete day - if (direction == DismissDirection.startToEnd) { - Navigator.of(context).pushNamed(GymModeScreen.routeName, arguments: _day); - } - return false; + subtitle: Text(_day.getDaysTextTranslated(Localizations.localeOf(context).languageCode)), + leading: const Icon(Icons.play_arrow), + minLeadingWidth: 8, + trailing: Row(mainAxisSize: MainAxisSize.min, children: [ + const SizedBox(height: 40, width: 1, child: VerticalDivider()), + const SizedBox(width: 10), + IconButton( + icon: _editing ? const Icon(Icons.done) : const Icon(Icons.edit), + tooltip: _editing ? AppLocalizations.of(context).done : AppLocalizations.of(context).edit, + onPressed: () { + _toggle(); + }, + ) + ]), + onTap: () { + Navigator.of(context).pushNamed( + GymModeScreen.routeName, + arguments: _day, + ); }, - child: Container( - padding: const EdgeInsets.all(10), - decoration: BoxDecoration(color: Theme.of(context).colorScheme.inversePrimary), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - _day.description, - style: Theme.of(context).textTheme.headlineSmall, - overflow: TextOverflow.ellipsis, - ), - Text(_day.getDaysTextTranslated(Localizations.localeOf(context).languageCode)), - ], - ), - ), - IconButton( - icon: _expanded ? const Icon(Icons.unfold_less) : const Icon(Icons.unfold_more), - onPressed: () { - _toggle(); - }, - ), - ], - ), - ), ); } } diff --git a/lib/widgets/workouts/workout_plans_list.dart b/lib/widgets/workouts/workout_plans_list.dart index d2bc3d7e..d7443bda 100644 --- a/lib/widgets/workouts/workout_plans_list.dart +++ b/lib/widgets/workouts/workout_plans_list.dart @@ -40,81 +40,71 @@ class WorkoutPlansList extends StatelessWidget { itemCount: _workoutProvider.items.length, itemBuilder: (context, index) { final currentWorkout = _workoutProvider.items[index]; - return Dismissible( - key: Key(currentWorkout.id.toString()), - confirmDismiss: (direction) async { - // Delete workout from DB - final res = await showDialog( - context: context, - builder: (BuildContext contextDialog) { - return AlertDialog( - content: Text( - AppLocalizations.of(context).confirmDelete(currentWorkout.name), - ), - actions: [ - TextButton( - child: Text(MaterialLocalizations.of(context).cancelButtonLabel), - onPressed: () => Navigator.of(contextDialog).pop(), - ), - TextButton( - child: Text( - AppLocalizations.of(context).delete, - style: TextStyle(color: Theme.of(context).colorScheme.error), - ), - onPressed: () { - // Confirmed, delete the workout - Provider.of(context, listen: false) - .deleteWorkout(currentWorkout.id!); - // Close the popup - Navigator.of(contextDialog).pop(); - - // and inform the user - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text( - AppLocalizations.of(context).successfullyDeleted, - textAlign: TextAlign.center, - ), - ), - ); - }, - ), - ], - ); - }); - return res; - }, - background: Container( - color: Theme.of(context).colorScheme.error, - alignment: Alignment.centerRight, - padding: const EdgeInsets.only(right: 20), - margin: const EdgeInsets.symmetric( - horizontal: 4, - vertical: 4, - ), - child: const Icon( - Icons.delete, - color: Colors.white, - ), - ), - direction: DismissDirection.endToStart, - child: Card( + return Card( child: ListTile( - onTap: () { - _workoutProvider.setCurrentPlan(currentWorkout.id!); + onTap: () { + _workoutProvider.setCurrentPlan(currentWorkout.id!); - Navigator.of(context) - .pushNamed(WorkoutPlanScreen.routeName, arguments: currentWorkout); - }, - title: Text(currentWorkout.name), - subtitle: Text( - DateFormat.yMd(Localizations.localeOf(context).languageCode) - .format(currentWorkout.creationDate), - ), - ), + Navigator.of(context) + .pushNamed(WorkoutPlanScreen.routeName, arguments: currentWorkout); + }, + title: Text(currentWorkout.name), + subtitle: Text( + DateFormat.yMd(Localizations.localeOf(context).languageCode) + .format(currentWorkout.creationDate), ), - ); + trailing: Row(mainAxisSize: MainAxisSize.min, children: [ + const VerticalDivider(), + IconButton( + icon: const Icon(Icons.delete), + tooltip: AppLocalizations.of(context).delete, + onPressed: () async { + // Delete workout from DB + await showDialog( + context: context, + builder: (BuildContext contextDialog) { + return AlertDialog( + content: Text( + AppLocalizations.of(context).confirmDelete(currentWorkout.name), + ), + actions: [ + TextButton( + child: + Text(MaterialLocalizations.of(context).cancelButtonLabel), + onPressed: () => Navigator.of(contextDialog).pop(), + ), + TextButton( + child: Text( + AppLocalizations.of(context).delete, + style: TextStyle(color: Theme.of(context).colorScheme.error), + ), + onPressed: () { + // Confirmed, delete the workout + Provider.of(context, listen: false) + .deleteWorkout(currentWorkout.id!); + + // Close the popup + Navigator.of(contextDialog).pop(); + + // and inform the user + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text( + AppLocalizations.of(context).successfullyDeleted, + textAlign: TextAlign.center, + ), + ), + ); + }, + ), + ], + ); + }); + }, + ) + ]), + )); }, ), ); diff --git a/test/nutrition/nutritional_plan_screen_test.dart b/test/nutrition/nutritional_plan_screen_test.dart index af66d005..cea4afb8 100644 --- a/test/nutrition/nutritional_plan_screen_test.dart +++ b/test/nutrition/nutritional_plan_screen_test.dart @@ -80,7 +80,7 @@ void main() { expect(find.text('75g Burger soup'), findsOneWidget); expect(find.text('300g Broccoli cake'), findsOneWidget); - expect(find.byType(Dismissible), findsNWidgets(2)); + expect(find.byType(Card), findsNWidgets(2)); expect(find.byType(FlNutritionalDiaryChartWidget), findsNothing); }); diff --git a/test/nutrition/nutritional_plans_screen_test.dart b/test/nutrition/nutritional_plans_screen_test.dart index b7fd34dc..df582833 100644 --- a/test/nutrition/nutritional_plans_screen_test.dart +++ b/test/nutrition/nutritional_plans_screen_test.dart @@ -86,14 +86,15 @@ void main() { //debugDumpApp(); expect(find.text('Nutritional plans'), findsOneWidget); - expect(find.byType(Dismissible), findsNWidgets(2)); + expect(find.byType(Card), findsNWidgets(2)); expect(find.byType(ListTile), findsNWidgets(2)); }); - testWidgets('Test deleting an item by dragging the dismissible', (WidgetTester tester) async { + testWidgets('Test deleting an item using the Delete button', (WidgetTester tester) async { await tester.pumpWidget(createHomeScreen()); - await tester.drag(find.byKey(const Key('1')), const Offset(-500.0, 0.0)); + await tester.tap(find.byIcon(Icons.delete).first); + await tester.pumpAndSettle(); // Confirmation dialog diff --git a/test/workout/workout_plan_screen_test.dart b/test/workout/workout_plan_screen_test.dart index 838e7d41..cd9febcc 100644 --- a/test/workout/workout_plan_screen_test.dart +++ b/test/workout/workout_plan_screen_test.dart @@ -67,7 +67,7 @@ void main() { expect(find.text('3 day workout'), findsOneWidget); expect(find.text('chest, shoulders'), findsOneWidget); expect(find.text('legs'), findsOneWidget); - expect(find.byType(Dismissible), findsNWidgets(2)); + expect(find.byType(Card), findsNWidgets(2)); }); testWidgets('Tests the localization of times - EN', (WidgetTester tester) async { diff --git a/test/workout/workout_plans_screen_test.dart b/test/workout/workout_plans_screen_test.dart index 45ee4879..5fe146c7 100644 --- a/test/workout/workout_plans_screen_test.dart +++ b/test/workout/workout_plans_screen_test.dart @@ -78,14 +78,15 @@ void main() { //debugDumpApp(); expect(find.text('Workout plans'), findsOneWidget); - expect(find.byType(Dismissible), findsNWidgets(2)); + expect(find.byType(Card), findsNWidgets(2)); expect(find.byType(ListTile), findsNWidgets(2)); }); - testWidgets('Test deleting an item by dragging the dismissible', (WidgetTester tester) async { + testWidgets('Test deleting an item using the Delete button', (WidgetTester tester) async { await tester.pumpWidget(createHomeScreen()); - await tester.drag(find.byKey(const Key('1')), const Offset(-500.0, 0.0)); + await tester.tap(find.byIcon(Icons.delete).first); + await tester.pumpAndSettle(); // Confirmation dialog From 87c8a599f0ed7b863d6614da3820fd9944f8152c Mon Sep 17 00:00:00 2001 From: Miroslav Mazel Date: Fri, 24 Nov 2023 13:02:25 +0100 Subject: [PATCH 5/5] Homescreen: new details icon + tweaked padding --- lib/widgets/dashboard/widgets.dart | 62 +++++++++++++++--------------- 1 file changed, 32 insertions(+), 30 deletions(-) diff --git a/lib/widgets/dashboard/widgets.dart b/lib/widgets/dashboard/widgets.dart index b5565c84..349eea65 100644 --- a/lib/widgets/dashboard/widgets.dart +++ b/lib/widgets/dashboard/widgets.dart @@ -146,14 +146,6 @@ class _DashboardNutritionWidgetState extends State { return out; } - Widget getTrailing() { - if (!_hasContent) { - return const Text(''); - } - - return _showDetail ? const Icon(Icons.expand_less) : const Icon(Icons.expand_more); - } - @override Widget build(BuildContext context) { return Card( @@ -174,7 +166,15 @@ class _DashboardNutritionWidgetState extends State { Icons.restaurant, color: Theme.of(context).textTheme.headlineSmall!.color, ), - trailing: getTrailing(), + trailing: _hasContent + ? Tooltip( + message: AppLocalizations.of(context).toggleDetails, + child: _showDetail + ? const Icon( + Icons.info, + ) + : const Icon(Icons.info_outline)) + : const SizedBox(), onTap: () { setState(() { _showDetail = !_showDetail; @@ -182,16 +182,18 @@ class _DashboardNutritionWidgetState extends State { }, ), if (_hasContent) - Column( - children: [ - ...getContent(), - Container( - padding: const EdgeInsets.all(15), - height: 180, - child: FlNutritionalPlanPieChartWidget(_plan!.nutritionalValues), - ) - ], - ) + Padding( + padding: const EdgeInsets.symmetric(horizontal: 10), + child: Column( + children: [ + ...getContent(), + Container( + padding: const EdgeInsets.symmetric(horizontal: 5, vertical: 15), + height: 180, + child: FlNutritionalPlanPieChartWidget(_plan!.nutritionalValues), + ) + ], + )) else NothingFound( AppLocalizations.of(context).noNutritionalPlans, @@ -441,14 +443,6 @@ class _DashboardWorkoutWidgetState extends State { _hasContent = _workoutPlan != null; } - Widget getTrailing() { - if (!_hasContent) { - return const Text(''); - } - - return _showDetail ? const Icon(Icons.expand_less) : const Icon(Icons.expand_more); - } - List getContent() { final List out = []; @@ -540,7 +534,15 @@ class _DashboardWorkoutWidgetState extends State { Icons.fitness_center, color: Theme.of(context).textTheme.headlineSmall!.color, ), - trailing: getTrailing(), + trailing: _hasContent + ? Tooltip( + message: AppLocalizations.of(context).toggleDetails, + child: _showDetail + ? const Icon( + Icons.info, + ) + : const Icon(Icons.info_outline)) + : const SizedBox(), onTap: () { setState(() { _showDetail = !_showDetail; @@ -548,8 +550,8 @@ class _DashboardWorkoutWidgetState extends State { }, ), if (_hasContent) - Container( - padding: const EdgeInsets.only(left: 10), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 10), child: Column( children: [ ...getContent(),