If there is a rest time, show a countdown timer in the gym mode

This commit is contained in:
Roland Geider
2025-03-11 21:42:41 +01:00
parent 140fe11e2a
commit 6039f51b2a
6 changed files with 100 additions and 21 deletions

View File

@@ -145,6 +145,7 @@ class DayHeader extends StatelessWidget {
style: Theme.of(context).textTheme.headlineSmall,
overflow: TextOverflow.ellipsis,
),
subtitle: Text(_dayData.day!.description),
leading: _viewMode ? null : const Icon(Icons.play_arrow),
trailing: _dayData.date.isSameDayAs(DateTime.now()) ? const Icon(Icons.today) : null,
minLeadingWidth: 8,

View File

@@ -167,7 +167,19 @@ class _GymModeState extends ConsumerState<GymMode> {
state.exercisePages,
widget._iteration,
));
out.add(TimerWidget(_controller, ratioCompleted, state.exercisePages));
// If there is a rest time, add a countdown timer
if (config.restTime != null) {
out.add(TimerCountdownWidget(
_controller,
config.restTime!.toInt(),
ratioCompleted,
state.exercisePages,
));
} else {
out.add(TimerWidget(_controller, ratioCompleted, state.exercisePages));
}
firstPage = false;
}
}
@@ -1026,22 +1038,16 @@ class _TimerWidgetState extends State<TimerWidget> {
DateTime today = DateTime(2000, 1, 1, 0, 0, 0);
void startTimer() {
setState(() {
_seconds = 0;
});
setState(() => _seconds = 0);
_timer?.cancel();
const oneSecond = Duration(seconds: 1);
_timer = Timer.periodic(oneSecond, (Timer timer) {
if (_seconds == _maxSeconds) {
setState(() {
timer.cancel();
});
setState(() => timer.cancel());
} else {
setState(() {
_seconds++;
});
setState(() => _seconds++);
}
});
}
@@ -1081,6 +1087,79 @@ class _TimerWidgetState extends State<TimerWidget> {
}
}
class TimerCountdownWidget extends StatefulWidget {
final PageController _controller;
final double _ratioCompleted;
final int _seconds;
final Map<Exercise, int> _exercisePages;
const TimerCountdownWidget(
this._controller,
this._seconds,
this._ratioCompleted,
this._exercisePages,
);
@override
_TimerCountdownWidgetState createState() => _TimerCountdownWidgetState();
}
class _TimerCountdownWidgetState extends State<TimerCountdownWidget> {
// See https://stackoverflow.com/questions/54610121/flutter-countdown-timer
Timer? _timer;
late int _seconds;
DateTime today = DateTime(2000, 1, 1, 0, 0, 0);
void startTimer() {
_timer?.cancel();
const oneSecond = Duration(seconds: 1);
_timer = Timer.periodic(oneSecond, (Timer timer) {
if (_seconds == 0) {
setState(() => timer.cancel());
} else {
setState(() => _seconds--);
}
});
}
@override
void dispose() {
_timer?.cancel();
super.dispose();
}
@override
void initState() {
super.initState();
_seconds = widget._seconds;
startTimer();
}
@override
Widget build(BuildContext context) {
return Column(
children: [
NavigationHeader(
AppLocalizations.of(context).pause,
widget._controller,
exercisePages: widget._exercisePages,
),
Expanded(
child: Center(
child: Text(
DateFormat('m:ss').format(today.add(Duration(seconds: _seconds))),
style: Theme.of(context).textTheme.displayLarge!.copyWith(color: wgerPrimaryColor),
),
),
),
NavigationFooter(widget._controller, widget._ratioCompleted),
],
);
}
}
class NavigationFooter extends StatelessWidget {
final PageController _controller;
final double _ratioCompleted;

View File

@@ -148,7 +148,7 @@ void main() {
// Bench press - pause
//
expect(find.text('Pause'), findsOneWidget);
expect(find.byType(TimerWidget), findsOneWidget);
expect(find.byType(TimerCountdownWidget), findsOneWidget);
expect(find.byIcon(Icons.close), findsOneWidget);
expect(find.byIcon(Icons.toc), findsOneWidget);
expect(find.byIcon(Icons.chevron_left), findsOneWidget);
@@ -169,7 +169,7 @@ void main() {
// Pause
//
expect(find.text('Pause'), findsOneWidget);
expect(find.byType(TimerWidget), findsOneWidget);
expect(find.byType(TimerCountdownWidget), findsOneWidget);
expect(find.byIcon(Icons.chevron_left), findsOneWidget);
expect(find.byIcon(Icons.close), findsOneWidget);
expect(find.byIcon(Icons.chevron_right), findsOneWidget);
@@ -189,7 +189,7 @@ void main() {
// Pause
//
expect(find.text('Pause'), findsOneWidget);
expect(find.byType(TimerWidget), findsOneWidget);
expect(find.byType(TimerCountdownWidget), findsOneWidget);
await tester.tap(find.byIcon(Icons.chevron_right));
await tester.pumpAndSettle();

View File

@@ -65,9 +65,7 @@ void main() {
),
child: const SizedBox(),
),
routes: {
RoutineScreen.routeName: (ctx) => const RoutineScreen(),
},
routes: {RoutineScreen.routeName: (ctx) => const RoutineScreen()},
),
);
}
@@ -83,6 +81,7 @@ void main() {
expect(find.text('3 day workout'), findsOneWidget);
expect(find.text('first day'), findsOneWidget);
debugDumpApp();
expect(find.text('chest, shoulders'), findsOneWidget);
expect(find.text('second day'), findsOneWidget);

View File

@@ -56,7 +56,7 @@ void main() {
localizationsDelegates: AppLocalizations.localizationsDelegates,
supportedLocales: AppLocalizations.supportedLocales,
navigatorKey: key,
home: Scaffold(body: SlotEntryForm(slotEntry, simpleMode: simpleMode)),
home: Scaffold(body: SlotEntryForm(slotEntry, 1, simpleMode: simpleMode)),
),
);
}
@@ -89,7 +89,7 @@ void main() {
await tester.tap(find.byKey(const Key(SUBMIT_BUTTON_KEY_NAME)));
await tester.pumpAndSettle();
verify(mockRoutinesProvider.editSlotEntry(any)).called(1);
verify(mockRoutinesProvider.editSlotEntry(any, any)).called(1);
verify(mockRoutinesProvider.handleConfig(any, any, any)).called(8);
});
}

View File

@@ -296,7 +296,7 @@ Routine getTestRoutine({List<Exercise>? exercises}) {
repetitionsUnit: testRepetitionUnit1,
weight: 10,
weightUnit: testWeightUnit1,
restTime: 60,
restTime: null,
rir: '',
rpe: '',
textRepr: '12x10kg',
@@ -310,7 +310,7 @@ Routine getTestRoutine({List<Exercise>? exercises}) {
repetitionsUnit: testRepetitionUnit1,
weight: 10,
weightUnit: testWeightUnit1,
restTime: 60,
restTime: null,
rir: '',
rpe: '',
textRepr: '12x10kg',
@@ -324,7 +324,7 @@ Routine getTestRoutine({List<Exercise>? exercises}) {
repetitionsUnit: testRepetitionUnit1,
weight: 10,
weightUnit: testWeightUnit1,
restTime: 60,
restTime: null,
rir: '',
rpe: '',
textRepr: '12x10kg',