feat(ui): configuration du score chronométré (ticket #26)
Ajoute la configuration du score chronométré sur exercise_library_screen.dart et program_screen.dart, s'appuyant sur les fondations domain du ticket #25. flutter analyze propre, 41/41 tests verts, build APK debug validé. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@ -539,6 +539,13 @@ final class _ProgramExerciseCustomizationScreenState
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final scoreUnit = _draft.scoreUnitSnapshot;
|
||||
final hasStopwatchScore =
|
||||
_draft.scoreInputModeSnapshot == ScoreInputMode.stopwatch;
|
||||
final scoreTitle = hasStopwatchScore
|
||||
? 'Score chrono'
|
||||
: scoreUnit == null
|
||||
? 'Score'
|
||||
: 'Score ($scoreUnit)';
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: const Text("Personnaliser l'exercice")),
|
||||
bottomNavigationBar: SafeArea(
|
||||
@ -603,10 +610,19 @@ final class _ProgramExerciseCustomizationScreenState
|
||||
if (_draft.availableScoreSnapshot)
|
||||
SwitchListTile(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
title: Text(scoreUnit == null ? 'Score' : 'Score ($scoreUnit)'),
|
||||
title: Text(scoreTitle),
|
||||
value: _draft.enabledMeasures.contains(WorkoutMeasure.score),
|
||||
onChanged: (value) => _toggle(WorkoutMeasure.score, value),
|
||||
),
|
||||
if (_draft.enabledMeasures.contains(WorkoutMeasure.time) &&
|
||||
_draft.enabledMeasures.contains(WorkoutMeasure.score) &&
|
||||
hasStopwatchScore) ...[
|
||||
const SizedBox(height: 8),
|
||||
const Text(
|
||||
"Temps sert d'objectif de durée ; Score chrono enregistre "
|
||||
'le temps réalisé.',
|
||||
),
|
||||
],
|
||||
if (_measureError != null)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 8),
|
||||
@ -630,13 +646,31 @@ final class _ProgramExerciseCustomizationScreenState
|
||||
onChanged: (value) => _draft.targetReps = value?.round(),
|
||||
),
|
||||
if (_draft.enabledMeasures.contains(WorkoutMeasure.score))
|
||||
_NumberField(
|
||||
label: scoreUnit == null
|
||||
? 'Cible score'
|
||||
: 'Cible score ($scoreUnit)',
|
||||
initialValue: _draft.targetScore,
|
||||
onChanged: (value) => _draft.targetScore = value,
|
||||
),
|
||||
if (hasStopwatchScore)
|
||||
_NumberField(
|
||||
label: 'Objectif de chrono',
|
||||
helperText: 'Le résultat réel sera mesuré pendant la série.',
|
||||
initialValue: _millisecondsToSeconds(
|
||||
_draft.targetScoreTimeMs,
|
||||
),
|
||||
onChanged: (value) {
|
||||
_draft.targetScore = null;
|
||||
_draft.targetScoreTimeMs = value == null
|
||||
? null
|
||||
: (value * 1000).round();
|
||||
},
|
||||
)
|
||||
else
|
||||
_NumberField(
|
||||
label: scoreUnit == null
|
||||
? 'Cible score'
|
||||
: 'Cible score ($scoreUnit)',
|
||||
initialValue: _draft.targetScore,
|
||||
onChanged: (value) {
|
||||
_draft.targetScore = value;
|
||||
_draft.targetScoreTimeMs = null;
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
Text('Repos', style: Theme.of(context).textTheme.titleMedium),
|
||||
const SizedBox(height: 8),
|
||||
@ -662,6 +696,10 @@ final class _ProgramExerciseCustomizationScreenState
|
||||
_draft.enabledMeasures = {..._draft.enabledMeasures, measure};
|
||||
} else {
|
||||
_draft.enabledMeasures = {..._draft.enabledMeasures}..remove(measure);
|
||||
if (measure == WorkoutMeasure.score) {
|
||||
_draft.targetScore = null;
|
||||
_draft.targetScoreTimeMs = null;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -731,11 +769,13 @@ final class _NumberField extends StatelessWidget {
|
||||
required this.label,
|
||||
required this.initialValue,
|
||||
required this.onChanged,
|
||||
this.helperText,
|
||||
});
|
||||
|
||||
final String label;
|
||||
final num? initialValue;
|
||||
final ValueChanged<double?> onChanged;
|
||||
final String? helperText;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@ -743,7 +783,7 @@ final class _NumberField extends StatelessWidget {
|
||||
padding: const EdgeInsets.only(top: 8),
|
||||
child: TextFormField(
|
||||
initialValue: initialValue?.toString(),
|
||||
decoration: InputDecoration(labelText: label),
|
||||
decoration: InputDecoration(labelText: label, helperText: helperText),
|
||||
keyboardType: TextInputType.number,
|
||||
onChanged: (value) => onChanged(double.tryParse(value)),
|
||||
),
|
||||
@ -764,6 +804,7 @@ final class _ProgramExerciseDraft {
|
||||
required this.availableTimeSnapshot,
|
||||
required this.availableRepsSnapshot,
|
||||
required this.availableScoreSnapshot,
|
||||
this.scoreInputModeSnapshot = ScoreInputMode.manual,
|
||||
this.scoreLabelSnapshot,
|
||||
this.scoreUnitSnapshot,
|
||||
required this.setsCount,
|
||||
@ -771,6 +812,7 @@ final class _ProgramExerciseDraft {
|
||||
this.targetTimeSeconds,
|
||||
this.targetReps,
|
||||
this.targetScore,
|
||||
this.targetScoreTimeMs,
|
||||
this.restSecondsOverride,
|
||||
});
|
||||
|
||||
@ -789,6 +831,7 @@ final class _ProgramExerciseDraft {
|
||||
availableTimeSnapshot: exercise.hasTimeMeasure,
|
||||
availableRepsSnapshot: exercise.hasRepsMeasure,
|
||||
availableScoreSnapshot: exercise.hasScoreMeasure,
|
||||
scoreInputModeSnapshot: exercise.scoreInputMode,
|
||||
scoreLabelSnapshot: exercise.scoreLabel,
|
||||
scoreUnitSnapshot: exercise.scoreUnit,
|
||||
setsCount: 3,
|
||||
@ -810,6 +853,7 @@ final class _ProgramExerciseDraft {
|
||||
availableTimeSnapshot: exercise.availableTimeSnapshot,
|
||||
availableRepsSnapshot: exercise.availableRepsSnapshot,
|
||||
availableScoreSnapshot: exercise.availableScoreSnapshot,
|
||||
scoreInputModeSnapshot: exercise.scoreInputModeSnapshot,
|
||||
scoreLabelSnapshot: exercise.scoreLabelSnapshot,
|
||||
scoreUnitSnapshot: exercise.scoreUnitSnapshot,
|
||||
setsCount: exercise.setsCount,
|
||||
@ -821,6 +865,7 @@ final class _ProgramExerciseDraft {
|
||||
targetTimeSeconds: exercise.targetTimeSeconds,
|
||||
targetReps: exercise.targetReps,
|
||||
targetScore: exercise.targetScore,
|
||||
targetScoreTimeMs: exercise.targetScoreTimeMs,
|
||||
restSecondsOverride: exercise.restSecondsOverride,
|
||||
);
|
||||
}
|
||||
@ -836,6 +881,7 @@ final class _ProgramExerciseDraft {
|
||||
final bool availableTimeSnapshot;
|
||||
final bool availableRepsSnapshot;
|
||||
final bool availableScoreSnapshot;
|
||||
final ScoreInputMode scoreInputModeSnapshot;
|
||||
final String? scoreLabelSnapshot;
|
||||
final String? scoreUnitSnapshot;
|
||||
int setsCount;
|
||||
@ -843,6 +889,7 @@ final class _ProgramExerciseDraft {
|
||||
int? targetTimeSeconds;
|
||||
int? targetReps;
|
||||
double? targetScore;
|
||||
int? targetScoreTimeMs;
|
||||
int? restSecondsOverride;
|
||||
|
||||
_ProgramExerciseDraft copy() {
|
||||
@ -858,6 +905,7 @@ final class _ProgramExerciseDraft {
|
||||
availableTimeSnapshot: availableTimeSnapshot,
|
||||
availableRepsSnapshot: availableRepsSnapshot,
|
||||
availableScoreSnapshot: availableScoreSnapshot,
|
||||
scoreInputModeSnapshot: scoreInputModeSnapshot,
|
||||
scoreLabelSnapshot: scoreLabelSnapshot,
|
||||
scoreUnitSnapshot: scoreUnitSnapshot,
|
||||
setsCount: setsCount,
|
||||
@ -865,11 +913,14 @@ final class _ProgramExerciseDraft {
|
||||
targetTimeSeconds: targetTimeSeconds,
|
||||
targetReps: targetReps,
|
||||
targetScore: targetScore,
|
||||
targetScoreTimeMs: targetScoreTimeMs,
|
||||
restSecondsOverride: restSecondsOverride,
|
||||
);
|
||||
}
|
||||
|
||||
ProgramExerciseConfig toConfig() {
|
||||
final scoreEnabled = enabledMeasures.contains(WorkoutMeasure.score);
|
||||
final stopwatchScore = scoreInputModeSnapshot == ScoreInputMode.stopwatch;
|
||||
return ProgramExerciseConfig(
|
||||
existingMetadata: existingMetadata,
|
||||
sourceExerciseId: sourceExerciseId,
|
||||
@ -881,18 +932,26 @@ final class _ProgramExerciseDraft {
|
||||
availableTimeSnapshot: availableTimeSnapshot,
|
||||
availableRepsSnapshot: availableRepsSnapshot,
|
||||
availableScoreSnapshot: availableScoreSnapshot,
|
||||
scoreInputModeSnapshot: scoreInputModeSnapshot,
|
||||
scoreLabelSnapshot: scoreLabelSnapshot,
|
||||
scoreUnitSnapshot: scoreUnitSnapshot,
|
||||
setsCount: setsCount,
|
||||
enabledMeasures: enabledMeasures,
|
||||
targetTimeSeconds: targetTimeSeconds,
|
||||
targetReps: targetReps,
|
||||
targetScore: targetScore,
|
||||
targetScore: scoreEnabled && !stopwatchScore ? targetScore : null,
|
||||
targetScoreTimeMs: scoreEnabled && stopwatchScore
|
||||
? targetScoreTimeMs
|
||||
: null,
|
||||
restSecondsOverride: restSecondsOverride,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
double? _millisecondsToSeconds(int? milliseconds) {
|
||||
return milliseconds == null ? null : milliseconds / 1000;
|
||||
}
|
||||
|
||||
String _programSummary(Program program) {
|
||||
final exerciseCount = program.exercises.length;
|
||||
final setsCount = program.exercises.fold<int>(
|
||||
@ -907,6 +966,10 @@ String _exerciseSummary(_ProgramExerciseDraft draft, int defaultRestSeconds) {
|
||||
final measures = draft.enabledMeasures
|
||||
.map((measure) {
|
||||
final unit = draft.scoreUnitSnapshot?.trim();
|
||||
if (measure == WorkoutMeasure.score &&
|
||||
draft.scoreInputModeSnapshot == ScoreInputMode.stopwatch) {
|
||||
return 'Score chrono';
|
||||
}
|
||||
if (measure == WorkoutMeasure.score &&
|
||||
unit != null &&
|
||||
unit.isNotEmpty) {
|
||||
|
||||
Reference in New Issue
Block a user