mirror of
https://github.com/wger-project/flutter.git
synced 2026-02-18 00:17:48 +01:00
Move the workout progression to the main navigation menu
This commit is contained in:
@@ -37,27 +37,8 @@ import 'package:wger/widgets/routines/forms/reps_unit.dart';
|
||||
import 'package:wger/widgets/routines/forms/rir.dart';
|
||||
import 'package:wger/widgets/routines/forms/weight_unit.dart';
|
||||
import 'package:wger/widgets/routines/gym_mode/navigation.dart';
|
||||
import 'package:wger/widgets/routines/gym_mode/workout_progresion.dart';
|
||||
import 'package:wger/widgets/routines/plate_calculator.dart';
|
||||
|
||||
void _openWorkoutProgressionDialog(BuildContext context) {
|
||||
showDialog<void>(
|
||||
context: context,
|
||||
builder: (ctx) {
|
||||
return AlertDialog(
|
||||
title: Text(AppLocalizations.of(context).todaysWorkout),
|
||||
content: const WorkoutProgression(),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(ctx).pop(),
|
||||
child: Text(MaterialLocalizations.of(context).closeButtonLabel),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
class LogPage extends ConsumerStatefulWidget {
|
||||
final _logger = Logger('LogPage');
|
||||
|
||||
@@ -108,15 +89,11 @@ class _LogPageState extends ConsumerState<LogPage> {
|
||||
..routineId = state.routine.id!
|
||||
..iteration = state.iteration;
|
||||
|
||||
// Mark done sets
|
||||
final decorationStyle = slotEntryPage.logDone
|
||||
? TextDecoration.lineThrough
|
||||
: TextDecoration.none;
|
||||
|
||||
final style = {
|
||||
'textDecoration': decorationStyle,
|
||||
'color': Theme.of(context).colorScheme.primary,
|
||||
};
|
||||
|
||||
return Column(
|
||||
children: [
|
||||
NavigationHeader(
|
||||
@@ -130,31 +107,26 @@ class _LogPageState extends ConsumerState<LogPage> {
|
||||
child: Center(
|
||||
child: Column(
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
_openWorkoutProgressionDialog(context);
|
||||
},
|
||||
child: Column(
|
||||
children: [
|
||||
Column(
|
||||
children: [
|
||||
Text(
|
||||
setConfigData.textRepr,
|
||||
textAlign: TextAlign.center,
|
||||
style: theme.textTheme.headlineMedium?.copyWith(
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
decoration: decorationStyle,
|
||||
),
|
||||
),
|
||||
if (setConfigData.type != SlotEntryType.normal)
|
||||
Text(
|
||||
setConfigData.textRepr,
|
||||
setConfigData.type.name.toUpperCase(),
|
||||
textAlign: TextAlign.center,
|
||||
style: theme.textTheme.headlineMedium?.copyWith(
|
||||
style: theme.textTheme.headlineSmall?.copyWith(
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
decoration: decorationStyle,
|
||||
),
|
||||
),
|
||||
if (setConfigData.type != SlotEntryType.normal)
|
||||
Text(
|
||||
setConfigData.type.name.toUpperCase(),
|
||||
textAlign: TextAlign.center,
|
||||
style: theme.textTheme.headlineSmall?.copyWith(
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
decoration: decorationStyle,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
Text(
|
||||
'${slotEntryPage.setIndex + 1} / ${widget._slotData.setConfigs.length}',
|
||||
|
||||
@@ -24,6 +24,7 @@ import 'package:wger/l10n/generated/app_localizations.dart';
|
||||
import 'package:wger/providers/exercises.dart';
|
||||
import 'package:wger/providers/gym_state.dart';
|
||||
import 'package:wger/theme/theme.dart';
|
||||
import 'package:wger/widgets/routines/gym_mode/workout_menu.dart';
|
||||
|
||||
class NavigationFooter extends ConsumerWidget {
|
||||
final PageController _controller;
|
||||
@@ -109,39 +110,10 @@ class NavigationHeader extends ConsumerWidget {
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
contentPadding: EdgeInsets.zero,
|
||||
content: SingleChildScrollView(
|
||||
child: Column(
|
||||
children: [
|
||||
...pages.where((page) => page.type == PageType.set).map((page) {
|
||||
return ListTile(
|
||||
leading: page.allLogsDone ? const Icon(Icons.check) : null,
|
||||
title: Text(
|
||||
page.exerciseIds
|
||||
.map(
|
||||
(id) => exercisesProvider
|
||||
.findExerciseById(id)
|
||||
.getTranslation(Localizations.localeOf(context).languageCode)
|
||||
.name,
|
||||
)
|
||||
.toList()
|
||||
.join('\n'),
|
||||
style: TextStyle(
|
||||
decoration: page.allLogsDone ? TextDecoration.lineThrough : TextDecoration.none,
|
||||
),
|
||||
),
|
||||
trailing: const Icon(Icons.chevron_right),
|
||||
onTap: () {
|
||||
_controller.animateToPage(
|
||||
page.pageIndex,
|
||||
duration: DEFAULT_ANIMATION_DURATION,
|
||||
curve: DEFAULT_ANIMATION_CURVE,
|
||||
);
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
);
|
||||
}),
|
||||
],
|
||||
),
|
||||
content: SizedBox(
|
||||
height: double.maxFinite,
|
||||
width: double.maxFinite,
|
||||
child: WorkoutMenu(_controller),
|
||||
),
|
||||
actions: [
|
||||
?endWorkoutButton,
|
||||
|
||||
197
lib/widgets/routines/gym_mode/workout_menu.dart
Normal file
197
lib/widgets/routines/gym_mode/workout_menu.dart
Normal file
@@ -0,0 +1,197 @@
|
||||
// /*
|
||||
// * This file is part of wger Workout Manager <https://github.com/wger-project>.
|
||||
// * Copyright (C) 2020, 2025 wger Team
|
||||
// *
|
||||
// * wger Workout Manager is free software: you can redistribute it and/or modify
|
||||
// * it under the terms of the GNU Affero General Public License as published by
|
||||
// * the Free Software Foundation, either version 3 of the License, or
|
||||
// * (at your option) any later version.
|
||||
// *
|
||||
// * wger Workout Manager is distributed in the hope that it will be useful,
|
||||
// * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// * GNU Affero General Public License for more details.
|
||||
// *
|
||||
// * You should have received a copy of the GNU Affero General Public License
|
||||
// * along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
// */
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:wger/helpers/consts.dart';
|
||||
import 'package:wger/providers/gym_state.dart';
|
||||
|
||||
class WorkoutMenu extends StatelessWidget {
|
||||
final PageController _controller;
|
||||
|
||||
const WorkoutMenu(this._controller);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return DefaultTabController(
|
||||
length: 2,
|
||||
child: Column(
|
||||
children: [
|
||||
const TabBar(
|
||||
tabs: [
|
||||
Tab(icon: Icon(Icons.menu_open)),
|
||||
Tab(icon: Icon(Icons.stacked_bar_chart)),
|
||||
],
|
||||
),
|
||||
Flexible(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 10),
|
||||
child: TabBarView(
|
||||
children: [
|
||||
NavigationTab(_controller),
|
||||
ProgressionTab(_controller),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class NavigationTab extends ConsumerWidget {
|
||||
final PageController _controller;
|
||||
|
||||
const NavigationTab(this._controller);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final state = ref.watch(gymStateProvider);
|
||||
|
||||
return Column(
|
||||
children: [
|
||||
...state.pages.where((pageEntry) => pageEntry.type == PageType.set).map((page) {
|
||||
return ListTile(
|
||||
leading: page.allLogsDone ? const Icon(Icons.check) : null,
|
||||
title: Text(
|
||||
page.exercises
|
||||
.map(
|
||||
(exercise) =>
|
||||
exercise.getTranslation(Localizations.localeOf(context).languageCode).name,
|
||||
)
|
||||
.toList()
|
||||
.join('\n'),
|
||||
style: TextStyle(
|
||||
decoration: page.allLogsDone ? TextDecoration.lineThrough : TextDecoration.none,
|
||||
),
|
||||
),
|
||||
trailing: const Icon(Icons.chevron_right),
|
||||
onTap: () {
|
||||
_controller.animateToPage(
|
||||
page.pageIndex,
|
||||
duration: DEFAULT_ANIMATION_DURATION,
|
||||
curve: DEFAULT_ANIMATION_CURVE,
|
||||
);
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
);
|
||||
}),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ProgressionTab extends ConsumerWidget {
|
||||
final PageController _controller;
|
||||
|
||||
const ProgressionTab(this._controller);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final state = ref.watch(gymStateProvider);
|
||||
final theme = Theme.of(context);
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||
child: Column(
|
||||
children: [
|
||||
...state.pages.where((page) => page.type == PageType.set).map((page) {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
...page.slotPages.where((slotPage) => slotPage.type == SlotPageType.log).map(
|
||||
(
|
||||
slotPage,
|
||||
) {
|
||||
final exercise = slotPage.setConfigData!.exercise
|
||||
.getTranslation(
|
||||
Localizations.localeOf(context).languageCode,
|
||||
)
|
||||
.name;
|
||||
|
||||
// Sets that are done are marked with a strikethrough
|
||||
final decoration = slotPage.logDone
|
||||
? TextDecoration.lineThrough
|
||||
: TextDecoration.none;
|
||||
|
||||
// Sets that are done have a lighter color
|
||||
final color = slotPage.logDone
|
||||
? theme.colorScheme.onSurface.withValues(alpha: 0.6)
|
||||
: null;
|
||||
|
||||
// he row for the current page is highlighted in bold
|
||||
final fontWeight = state.currentPage == slotPage.pageIndex
|
||||
? FontWeight.bold
|
||||
: null;
|
||||
|
||||
return Text.rich(
|
||||
TextSpan(
|
||||
children: [
|
||||
if (slotPage.logDone) const TextSpan(text: '✅ '),
|
||||
TextSpan(
|
||||
text: '$exercise - ${slotPage.setConfigData!.textReprWithType}',
|
||||
style: theme.textTheme.bodyMedium!.copyWith(
|
||||
decoration: decoration,
|
||||
fontWeight: fontWeight,
|
||||
color: color,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 1,
|
||||
);
|
||||
},
|
||||
),
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
//mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
IconButton(
|
||||
onPressed: () {},
|
||||
icon: const Icon(Icons.swap_horiz, size: 18),
|
||||
),
|
||||
IconButton(
|
||||
onPressed: () {},
|
||||
icon: const Icon(Icons.add, size: 18),
|
||||
),
|
||||
Expanded(child: Container()),
|
||||
IconButton(
|
||||
onPressed: () {
|
||||
_controller.animateToPage(
|
||||
page.pageIndex,
|
||||
duration: DEFAULT_ANIMATION_DURATION,
|
||||
curve: DEFAULT_ANIMATION_CURVE,
|
||||
);
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
icon: const Icon(Icons.chevron_right),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
],
|
||||
);
|
||||
}),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,108 +0,0 @@
|
||||
/*
|
||||
* This file is part of wger Workout Manager <https://github.com/wger-project>.
|
||||
* Copyright (C) 2020, 2025 wger Team
|
||||
*
|
||||
* wger Workout Manager is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* wger Workout Manager is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:wger/providers/gym_state.dart';
|
||||
|
||||
class WorkoutProgression extends ConsumerWidget {
|
||||
const WorkoutProgression();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final state = ref.watch(gymStateProvider);
|
||||
final theme = Theme.of(context);
|
||||
|
||||
return SingleChildScrollView(
|
||||
child: Column(
|
||||
children: [
|
||||
...state.pages.where((page) => page.type == PageType.set).map((page) {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
...page.slotPages.where((slotPage) => slotPage.type == SlotPageType.log).map((
|
||||
slotPage,
|
||||
) {
|
||||
final exercise = slotPage.setConfigData!.exercise
|
||||
.getTranslation(Localizations.localeOf(context).languageCode)
|
||||
.name;
|
||||
|
||||
// Sets that are done are marked with a strikethrough
|
||||
final decoration = slotPage.logDone
|
||||
? TextDecoration.lineThrough
|
||||
: TextDecoration.none;
|
||||
|
||||
// Sets that are done have a lighter color
|
||||
final color = slotPage.logDone
|
||||
? theme.colorScheme.onSurface.withValues(alpha: 0.6)
|
||||
: null;
|
||||
|
||||
// he row for the current page is highlighted in bold
|
||||
final fontWeight = state.currentPage == slotPage.pageIndex
|
||||
? FontWeight.bold
|
||||
: null;
|
||||
|
||||
return Text.rich(
|
||||
TextSpan(
|
||||
children: [
|
||||
if (slotPage.logDone) const TextSpan(text: '✅ '),
|
||||
TextSpan(
|
||||
text: '$exercise - ${slotPage.setConfigData!.textReprWithType}',
|
||||
style: theme.textTheme.bodyMedium!.copyWith(
|
||||
decoration: decoration,
|
||||
fontWeight: fontWeight,
|
||||
color: color,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 1,
|
||||
);
|
||||
}),
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
//mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
IconButton(
|
||||
onPressed: () {},
|
||||
icon: const Icon(Icons.swap_horiz, size: 18),
|
||||
),
|
||||
/*
|
||||
IconButton(
|
||||
onPressed: () {},
|
||||
icon: const Icon(Icons.add, size: 18),
|
||||
),
|
||||
*/
|
||||
Expanded(child: Container()),
|
||||
IconButton(
|
||||
onPressed: () {},
|
||||
icon: const Icon(Icons.chevron_right),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
],
|
||||
);
|
||||
}),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user