Merge pull request #459 from 12people/ux_fixes

Removing Dismissible for better discoverability
This commit is contained in:
Roland Geider
2024-01-23 14:31:16 +01:00
committed by GitHub
16 changed files with 432 additions and 466 deletions

View File

@@ -808,5 +808,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"
}

View File

@@ -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),

View File

@@ -82,6 +82,7 @@ class _WorkoutPlanScreenState extends State<WorkoutPlanScreen> {
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),

View File

@@ -146,14 +146,6 @@ class _DashboardNutritionWidgetState extends State<DashboardNutritionWidget> {
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<DashboardNutritionWidget> {
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<DashboardNutritionWidget> {
},
),
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<DashboardWorkoutWidget> {
_hasContent = _workoutPlan != null;
}
Widget getTrailing() {
if (!_hasContent) {
return const Text('');
}
return _showDetail ? const Icon(Icons.expand_less) : const Icon(Icons.expand_more);
}
List<Widget> getContent() {
final List<Widget> out = [];
@@ -540,7 +534,15 @@ class _DashboardWorkoutWidgetState extends State<DashboardWorkoutWidget> {
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<DashboardWorkoutWidget> {
},
),
if (_hasContent)
Container(
padding: const EdgeInsets.only(left: 10),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 10),
child: Column(
children: [
...getContent(),

View File

@@ -43,11 +43,18 @@ class MealWidget extends StatefulWidget {
}
class _MealWidgetState extends State<MealWidget> {
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<MealWidget> {
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<NutritionPlansProvider>(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<NutritionPlansProvider>(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<NutritionPlansProvider>(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<MealWidget> {
}
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<NutritionPlansProvider>(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<NutritionPlansProvider>(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();
},
),
],
),
],
),
);
},
),
),
],
);
}
}

View File

@@ -195,6 +195,7 @@ class NutritionalDiaryDetailWidget extends StatelessWidget {
),
),
IconButton(
tooltip: AppLocalizations.of(context).delete,
onPressed: () {
Provider.of<NutritionPlansProvider>(context, listen: false)
.deleteLog(log.id!, _nutritionalPlan.id!);

View File

@@ -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,
),
),
);
},
),
],
);
});
},
)
]),
),
);
},

View File

@@ -97,7 +97,7 @@ class WorkoutDayWidget extends StatefulWidget {
}
class _WorkoutDayWidgetState extends State<WorkoutDayWidget> {
bool _expanded = false;
bool _editing = false;
late List<Set> _sets;
@override
@@ -109,20 +109,21 @@ class _WorkoutDayWidgetState extends State<WorkoutDayWidget> {
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<WorkoutPlansProvider>(context, listen: false).deleteSet(set);
},
@@ -135,7 +136,7 @@ class _WorkoutDayWidgetState extends State<WorkoutDayWidget> {
(setting) => SettingWidget(
set: set,
setting: setting,
expanded: _expanded,
expanded: _editing,
toggle: _toggleExpanded,
),
),
@@ -143,7 +144,7 @@ class _WorkoutDayWidgetState extends State<WorkoutDayWidget> {
],
),
),
if (_expanded)
if (_editing)
ReorderableDragStartListener(
index: index,
child: const IconButton(
@@ -162,62 +163,65 @@ class _WorkoutDayWidgetState extends State<WorkoutDayWidget> {
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<WorkoutPlansProvider>(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<WorkoutPlansProvider>(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<WorkoutPlansProvider>(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<WorkoutPlansProvider>(context, listen: false).deleteDay(
widget._day,
);
},
),
],
)),
const Divider(),
ReorderableListView(
physics: const NeverScrollableScrollPhysics(),
shrinkWrap: true,
@@ -240,21 +244,6 @@ class _WorkoutDayWidgetState extends State<WorkoutDayWidget> {
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,
),
);
},
),
],
),
),
@@ -262,77 +251,48 @@ class _WorkoutDayWidgetState extends State<WorkoutDayWidget> {
}
}
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();
},
),
],
),
),
);
}
}

View File

@@ -1131,7 +1131,7 @@ class NavigationHeader extends StatelessWidget {
),
),
IconButton(
icon: const Icon(Icons.menu),
icon: const Icon(Icons.toc),
onPressed: () {
showDialog(
context: context,

View File

@@ -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<WorkoutPlansProvider>(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<WorkoutPlansProvider>(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,
),
),
);
},
),
],
);
});
},
)
]),
));
},
),
);

View File

@@ -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

View File

@@ -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);
});

View File

@@ -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

View File

@@ -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));

View File

@@ -72,7 +72,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 {

View File

@@ -83,14 +83,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