feat(ui): programme simplifié en cartes compactes (ticket #20)
Simplifie l'affichage du programme sur program_screen.dart avec des cartes compactes. flutter analyze propre, 30/30 tests verts, build APK debug validé. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@ -226,7 +226,7 @@ final class _ProgramFormScreenState extends State<ProgramFormScreen> {
|
|||||||
key: ValueKey(exercise.key),
|
key: ValueKey(exercise.key),
|
||||||
draft: exercise,
|
draft: exercise,
|
||||||
defaultRestSeconds: _defaultRestSeconds,
|
defaultRestSeconds: _defaultRestSeconds,
|
||||||
onChanged: () => setState(() {}),
|
onCustomize: () => _customizeExercise(exercise.key),
|
||||||
index: index,
|
index: index,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@ -246,8 +246,46 @@ final class _ProgramFormScreenState extends State<ProgramFormScreen> {
|
|||||||
);
|
);
|
||||||
if (exercise == null) return;
|
if (exercise == null) return;
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
|
final draft = _ProgramExerciseDraft.fromExercise(
|
||||||
|
exercise,
|
||||||
|
restSecondsOverride: _defaultRestSeconds,
|
||||||
|
);
|
||||||
setState(() {
|
setState(() {
|
||||||
_exercises.add(_ProgramExerciseDraft.fromExercise(exercise));
|
_exercises.add(draft);
|
||||||
|
});
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(
|
||||||
|
content: const Text('Exercice ajouté'),
|
||||||
|
action: SnackBarAction(
|
||||||
|
label: 'Personnaliser',
|
||||||
|
onPressed: () => _customizeExercise(draft.key),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _customizeExercise(String key) async {
|
||||||
|
final index = _exercises.indexWhere((exercise) => exercise.key == key);
|
||||||
|
if (index == -1) return;
|
||||||
|
final result = await Navigator.of(context).push<_ProgramExerciseEditResult>(
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => _ProgramExerciseCustomizationScreen(
|
||||||
|
draft: _exercises[index],
|
||||||
|
defaultRestSeconds: _defaultRestSeconds,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
if (result == null || !mounted) return;
|
||||||
|
setState(() {
|
||||||
|
final currentIndex = _exercises.indexWhere(
|
||||||
|
(exercise) => exercise.key == key,
|
||||||
|
);
|
||||||
|
if (currentIndex == -1) return;
|
||||||
|
if (result.deleted) {
|
||||||
|
_exercises.removeAt(currentIndex);
|
||||||
|
} else if (result.draft != null) {
|
||||||
|
_exercises[currentIndex] = result.draft!;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -418,116 +456,198 @@ final class _ProgramExerciseCard extends StatelessWidget {
|
|||||||
const _ProgramExerciseCard({
|
const _ProgramExerciseCard({
|
||||||
required this.draft,
|
required this.draft,
|
||||||
required this.defaultRestSeconds,
|
required this.defaultRestSeconds,
|
||||||
required this.onChanged,
|
required this.onCustomize,
|
||||||
required this.index,
|
required this.index,
|
||||||
super.key,
|
super.key,
|
||||||
});
|
});
|
||||||
|
|
||||||
final _ProgramExerciseDraft draft;
|
final _ProgramExerciseDraft draft;
|
||||||
final int defaultRestSeconds;
|
final int defaultRestSeconds;
|
||||||
final VoidCallback onChanged;
|
final VoidCallback onCustomize;
|
||||||
final int index;
|
final int index;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final scoreUnit = draft.scoreUnitSnapshot;
|
|
||||||
return Card(
|
return Card(
|
||||||
margin: const EdgeInsets.only(bottom: 12),
|
margin: const EdgeInsets.only(bottom: 8),
|
||||||
child: Padding(
|
child: ListTile(
|
||||||
padding: const EdgeInsets.all(12),
|
leading: ReorderableDragStartListener(
|
||||||
child: Column(
|
index: index,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
child: const Icon(Icons.drag_handle),
|
||||||
|
),
|
||||||
|
title: Text(draft.exerciseNameSnapshot),
|
||||||
|
subtitle: Wrap(
|
||||||
|
spacing: 8,
|
||||||
|
runSpacing: 4,
|
||||||
|
crossAxisAlignment: WrapCrossAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
Row(
|
Text(_restLabel(draft, defaultRestSeconds)),
|
||||||
children: [
|
if (draft.exerciseArchivedSnapshot)
|
||||||
ReorderableDragStartListener(
|
const Chip(label: Text('Exercice archivé')),
|
||||||
index: index,
|
],
|
||||||
child: const Padding(
|
),
|
||||||
padding: EdgeInsets.only(right: 8),
|
trailing: IconButton(
|
||||||
child: Icon(Icons.drag_handle),
|
tooltip: "Personnaliser l'exercice",
|
||||||
),
|
onPressed: onCustomize,
|
||||||
),
|
icon: const Icon(Icons.tune),
|
||||||
Expanded(
|
),
|
||||||
child: Text(
|
),
|
||||||
draft.exerciseNameSnapshot,
|
);
|
||||||
style: Theme.of(context).textTheme.titleMedium,
|
}
|
||||||
),
|
}
|
||||||
),
|
|
||||||
if (draft.exerciseArchivedSnapshot)
|
final class _ProgramExerciseCustomizationScreen extends StatefulWidget {
|
||||||
const Chip(label: Text('Exercice archivé')),
|
const _ProgramExerciseCustomizationScreen({
|
||||||
],
|
required _ProgramExerciseDraft draft,
|
||||||
|
required this.defaultRestSeconds,
|
||||||
|
super.key,
|
||||||
|
}) : _initialDraft = draft;
|
||||||
|
|
||||||
|
final _ProgramExerciseDraft _initialDraft;
|
||||||
|
final int defaultRestSeconds;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<_ProgramExerciseCustomizationScreen> createState() =>
|
||||||
|
_ProgramExerciseCustomizationScreenState();
|
||||||
|
}
|
||||||
|
|
||||||
|
final class _ProgramExerciseCustomizationScreenState
|
||||||
|
extends State<_ProgramExerciseCustomizationScreen> {
|
||||||
|
final _formKey = GlobalKey<FormState>();
|
||||||
|
late final _ProgramExerciseDraft _draft;
|
||||||
|
late final TextEditingController _setsController;
|
||||||
|
late final TextEditingController _restController;
|
||||||
|
String? _measureError;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_draft = widget._initialDraft.copy();
|
||||||
|
_setsController = TextEditingController(text: '${_draft.setsCount}');
|
||||||
|
_restController = TextEditingController(
|
||||||
|
text: '${_draft.restSecondsOverride ?? widget.defaultRestSeconds}',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_setsController.dispose();
|
||||||
|
_restController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final scoreUnit = _draft.scoreUnitSnapshot;
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(title: const Text("Personnaliser l'exercice")),
|
||||||
|
bottomNavigationBar: SafeArea(
|
||||||
|
minimum: const EdgeInsets.all(16),
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
|
children: [
|
||||||
|
FilledButton.icon(
|
||||||
|
onPressed: _save,
|
||||||
|
icon: const Icon(Icons.check),
|
||||||
|
label: const Text('Enregistrer'),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
TextButton.icon(
|
||||||
|
onPressed: _confirmDelete,
|
||||||
|
icon: const Icon(Icons.delete_outline),
|
||||||
|
label: const Text('Supprimer du programme'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
body: Form(
|
||||||
|
key: _formKey,
|
||||||
|
child: ListView(
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
_draft.exerciseNameSnapshot,
|
||||||
|
style: Theme.of(context).textTheme.headlineMedium,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 4),
|
const SizedBox(height: 4),
|
||||||
Text(_exerciseSummary(draft, defaultRestSeconds)),
|
Text(_exerciseSummary(_draft, widget.defaultRestSeconds)),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: 24),
|
||||||
|
Text('Séries', style: Theme.of(context).textTheme.titleMedium),
|
||||||
|
const SizedBox(height: 8),
|
||||||
TextFormField(
|
TextFormField(
|
||||||
initialValue: '${draft.setsCount}',
|
controller: _setsController,
|
||||||
decoration: const InputDecoration(labelText: 'Nombre de séries'),
|
decoration: const InputDecoration(labelText: 'Nombre de séries'),
|
||||||
keyboardType: TextInputType.number,
|
keyboardType: TextInputType.number,
|
||||||
onChanged: (value) {
|
validator: _positiveIntValidator,
|
||||||
draft.setsCount = int.tryParse(value) ?? draft.setsCount;
|
|
||||||
onChanged();
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: 24),
|
||||||
Text(
|
Text(
|
||||||
'Mesures à suivre',
|
'Mesures à suivre',
|
||||||
style: Theme.of(context).textTheme.titleSmall,
|
style: Theme.of(context).textTheme.titleMedium,
|
||||||
),
|
),
|
||||||
if (draft.availableTimeSnapshot)
|
if (_draft.availableTimeSnapshot)
|
||||||
CheckboxListTile(
|
SwitchListTile(
|
||||||
contentPadding: EdgeInsets.zero,
|
contentPadding: EdgeInsets.zero,
|
||||||
title: const Text('Temps'),
|
title: const Text('Temps'),
|
||||||
value: draft.enabledMeasures.contains(WorkoutMeasure.time),
|
value: _draft.enabledMeasures.contains(WorkoutMeasure.time),
|
||||||
onChanged: (value) => _toggle(WorkoutMeasure.time, value),
|
onChanged: (value) => _toggle(WorkoutMeasure.time, value),
|
||||||
),
|
),
|
||||||
if (draft.availableRepsSnapshot)
|
if (_draft.availableRepsSnapshot)
|
||||||
CheckboxListTile(
|
SwitchListTile(
|
||||||
contentPadding: EdgeInsets.zero,
|
contentPadding: EdgeInsets.zero,
|
||||||
title: const Text('Répétitions'),
|
title: const Text('Répétitions'),
|
||||||
value: draft.enabledMeasures.contains(WorkoutMeasure.reps),
|
value: _draft.enabledMeasures.contains(WorkoutMeasure.reps),
|
||||||
onChanged: (value) => _toggle(WorkoutMeasure.reps, value),
|
onChanged: (value) => _toggle(WorkoutMeasure.reps, value),
|
||||||
),
|
),
|
||||||
if (draft.availableScoreSnapshot)
|
if (_draft.availableScoreSnapshot)
|
||||||
CheckboxListTile(
|
SwitchListTile(
|
||||||
contentPadding: EdgeInsets.zero,
|
contentPadding: EdgeInsets.zero,
|
||||||
title: Text(scoreUnit == null ? 'Score' : 'Score ($scoreUnit)'),
|
title: Text(scoreUnit == null ? 'Score' : 'Score ($scoreUnit)'),
|
||||||
value: draft.enabledMeasures.contains(WorkoutMeasure.score),
|
value: _draft.enabledMeasures.contains(WorkoutMeasure.score),
|
||||||
onChanged: (value) => _toggle(WorkoutMeasure.score, value),
|
onChanged: (value) => _toggle(WorkoutMeasure.score, value),
|
||||||
),
|
),
|
||||||
if (draft.enabledMeasures.contains(WorkoutMeasure.time))
|
if (_measureError != null)
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(top: 8),
|
||||||
|
child: Text(
|
||||||
|
_measureError!,
|
||||||
|
style: TextStyle(color: Theme.of(context).colorScheme.error),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 24),
|
||||||
|
Text('Objectifs', style: Theme.of(context).textTheme.titleMedium),
|
||||||
|
if (_draft.enabledMeasures.contains(WorkoutMeasure.time))
|
||||||
_NumberField(
|
_NumberField(
|
||||||
label: 'Cible temps (s)',
|
label: 'Cible temps (s)',
|
||||||
initialValue: draft.targetTimeSeconds,
|
initialValue: _draft.targetTimeSeconds,
|
||||||
onChanged: (value) => draft.targetTimeSeconds = value?.round(),
|
onChanged: (value) => _draft.targetTimeSeconds = value?.round(),
|
||||||
),
|
),
|
||||||
if (draft.enabledMeasures.contains(WorkoutMeasure.reps))
|
if (_draft.enabledMeasures.contains(WorkoutMeasure.reps))
|
||||||
_NumberField(
|
_NumberField(
|
||||||
label: 'Cible répétitions',
|
label: 'Cible répétitions',
|
||||||
initialValue: draft.targetReps,
|
initialValue: _draft.targetReps,
|
||||||
onChanged: (value) => draft.targetReps = value?.round(),
|
onChanged: (value) => _draft.targetReps = value?.round(),
|
||||||
),
|
),
|
||||||
if (draft.enabledMeasures.contains(WorkoutMeasure.score))
|
if (_draft.enabledMeasures.contains(WorkoutMeasure.score))
|
||||||
_NumberField(
|
_NumberField(
|
||||||
label: scoreUnit == null
|
label: scoreUnit == null
|
||||||
? 'Cible score'
|
? 'Cible score'
|
||||||
: 'Cible score ($scoreUnit)',
|
: 'Cible score ($scoreUnit)',
|
||||||
initialValue: draft.targetScore,
|
initialValue: _draft.targetScore,
|
||||||
onChanged: (value) => draft.targetScore = value,
|
onChanged: (value) => _draft.targetScore = value,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 12),
|
const SizedBox(height: 24),
|
||||||
|
Text('Repos', style: Theme.of(context).textTheme.titleMedium),
|
||||||
|
const SizedBox(height: 8),
|
||||||
TextFormField(
|
TextFormField(
|
||||||
initialValue:
|
controller: _restController,
|
||||||
'${draft.restSecondsOverride ?? defaultRestSeconds}',
|
|
||||||
decoration: const InputDecoration(
|
decoration: const InputDecoration(
|
||||||
labelText: 'Repos après chaque série (s)',
|
labelText: 'Repos après chaque série (s)',
|
||||||
helperText: 'Pas de repos après la toute dernière série.',
|
helperText: 'Pas de repos après la toute dernière série.',
|
||||||
),
|
),
|
||||||
keyboardType: TextInputType.number,
|
keyboardType: TextInputType.number,
|
||||||
onChanged: (value) {
|
validator: _positiveIntValidator,
|
||||||
draft.restSecondsOverride = int.tryParse(value);
|
|
||||||
onChanged();
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -535,14 +655,75 @@ final class _ProgramExerciseCard extends StatelessWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _toggle(WorkoutMeasure measure, bool? value) {
|
void _toggle(WorkoutMeasure measure, bool value) {
|
||||||
if (value == true) {
|
setState(() {
|
||||||
draft.enabledMeasures = {...draft.enabledMeasures, measure};
|
_measureError = null;
|
||||||
} else if (draft.enabledMeasures.length > 1) {
|
if (value) {
|
||||||
draft.enabledMeasures = {...draft.enabledMeasures}..remove(measure);
|
_draft.enabledMeasures = {..._draft.enabledMeasures, measure};
|
||||||
}
|
} else {
|
||||||
onChanged();
|
_draft.enabledMeasures = {..._draft.enabledMeasures}..remove(measure);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String? _positiveIntValidator(String? value) {
|
||||||
|
final number = int.tryParse(value?.trim() ?? '');
|
||||||
|
if (number == null || number <= 0) {
|
||||||
|
return 'Saisis un nombre supérieur à 0.';
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _save() {
|
||||||
|
final hasMeasure = _draft.enabledMeasures.isNotEmpty;
|
||||||
|
setState(() {
|
||||||
|
_measureError = hasMeasure
|
||||||
|
? null
|
||||||
|
: 'Active au moins une mesure pour enregistrer.';
|
||||||
|
});
|
||||||
|
if (!_formKey.currentState!.validate() || !hasMeasure) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_draft.setsCount = int.parse(_setsController.text.trim());
|
||||||
|
_draft.restSecondsOverride = int.parse(_restController.text.trim());
|
||||||
|
Navigator.of(context).pop(_ProgramExerciseEditResult.saved(_draft));
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _confirmDelete() async {
|
||||||
|
final confirmed = await showDialog<bool>(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => AlertDialog(
|
||||||
|
title: const Text('Supprimer cet exercice ?'),
|
||||||
|
content: const Text('Il sera retiré de ce programme.'),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Navigator.of(context).pop(false),
|
||||||
|
child: const Text('Annuler'),
|
||||||
|
),
|
||||||
|
FilledButton(
|
||||||
|
onPressed: () => Navigator.of(context).pop(true),
|
||||||
|
child: const Text('Supprimer'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
if (confirmed == true && mounted) {
|
||||||
|
Navigator.of(context).pop(const _ProgramExerciseEditResult.deleted());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final class _ProgramExerciseEditResult {
|
||||||
|
const _ProgramExerciseEditResult._({this.draft, required this.deleted});
|
||||||
|
|
||||||
|
const _ProgramExerciseEditResult.deleted()
|
||||||
|
: this._(draft: null, deleted: true);
|
||||||
|
|
||||||
|
const _ProgramExerciseEditResult.saved(_ProgramExerciseDraft draft)
|
||||||
|
: this._(draft: draft, deleted: false);
|
||||||
|
|
||||||
|
final _ProgramExerciseDraft? draft;
|
||||||
|
final bool deleted;
|
||||||
}
|
}
|
||||||
|
|
||||||
final class _NumberField extends StatelessWidget {
|
final class _NumberField extends StatelessWidget {
|
||||||
@ -593,7 +774,10 @@ final class _ProgramExerciseDraft {
|
|||||||
this.restSecondsOverride,
|
this.restSecondsOverride,
|
||||||
});
|
});
|
||||||
|
|
||||||
factory _ProgramExerciseDraft.fromExercise(Exercise exercise) {
|
factory _ProgramExerciseDraft.fromExercise(
|
||||||
|
Exercise exercise, {
|
||||||
|
int? restSecondsOverride,
|
||||||
|
}) {
|
||||||
return _ProgramExerciseDraft(
|
return _ProgramExerciseDraft(
|
||||||
key: UniqueKey().toString(),
|
key: UniqueKey().toString(),
|
||||||
sourceExerciseId: exercise.metadata.id,
|
sourceExerciseId: exercise.metadata.id,
|
||||||
@ -609,6 +793,7 @@ final class _ProgramExerciseDraft {
|
|||||||
scoreUnitSnapshot: exercise.scoreUnit,
|
scoreUnitSnapshot: exercise.scoreUnit,
|
||||||
setsCount: 3,
|
setsCount: 3,
|
||||||
enabledMeasures: exercise.availableMeasures,
|
enabledMeasures: exercise.availableMeasures,
|
||||||
|
restSecondsOverride: restSecondsOverride,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -660,6 +845,30 @@ final class _ProgramExerciseDraft {
|
|||||||
double? targetScore;
|
double? targetScore;
|
||||||
int? restSecondsOverride;
|
int? restSecondsOverride;
|
||||||
|
|
||||||
|
_ProgramExerciseDraft copy() {
|
||||||
|
return _ProgramExerciseDraft(
|
||||||
|
key: key,
|
||||||
|
existingMetadata: existingMetadata,
|
||||||
|
sourceExerciseId: sourceExerciseId,
|
||||||
|
exerciseNameSnapshot: exerciseNameSnapshot,
|
||||||
|
exerciseDescriptionSnapshot: exerciseDescriptionSnapshot,
|
||||||
|
exerciseImageMediaIdSnapshot: exerciseImageMediaIdSnapshot,
|
||||||
|
exerciseVideoMediaIdSnapshot: exerciseVideoMediaIdSnapshot,
|
||||||
|
exerciseArchivedSnapshot: exerciseArchivedSnapshot,
|
||||||
|
availableTimeSnapshot: availableTimeSnapshot,
|
||||||
|
availableRepsSnapshot: availableRepsSnapshot,
|
||||||
|
availableScoreSnapshot: availableScoreSnapshot,
|
||||||
|
scoreLabelSnapshot: scoreLabelSnapshot,
|
||||||
|
scoreUnitSnapshot: scoreUnitSnapshot,
|
||||||
|
setsCount: setsCount,
|
||||||
|
enabledMeasures: {...enabledMeasures},
|
||||||
|
targetTimeSeconds: targetTimeSeconds,
|
||||||
|
targetReps: targetReps,
|
||||||
|
targetScore: targetScore,
|
||||||
|
restSecondsOverride: restSecondsOverride,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
ProgramExerciseConfig toConfig() {
|
ProgramExerciseConfig toConfig() {
|
||||||
return ProgramExerciseConfig(
|
return ProgramExerciseConfig(
|
||||||
existingMetadata: existingMetadata,
|
existingMetadata: existingMetadata,
|
||||||
@ -710,6 +919,11 @@ String _exerciseSummary(_ProgramExerciseDraft draft, int defaultRestSeconds) {
|
|||||||
return '${draft.setsCount} séries · $measures · Repos $rest s';
|
return '${draft.setsCount} séries · $measures · Repos $rest s';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String _restLabel(_ProgramExerciseDraft draft, int defaultRestSeconds) {
|
||||||
|
final rest = draft.restSecondsOverride ?? defaultRestSeconds;
|
||||||
|
return 'Repos $rest s';
|
||||||
|
}
|
||||||
|
|
||||||
String _measureLabel(WorkoutMeasure measure) {
|
String _measureLabel(WorkoutMeasure measure) {
|
||||||
return switch (measure) {
|
return switch (measure) {
|
||||||
WorkoutMeasure.time => 'Temps',
|
WorkoutMeasure.time => 'Temps',
|
||||||
|
|||||||
@ -8,6 +8,7 @@ void main() {
|
|||||||
testWidgets(
|
testWidgets(
|
||||||
'ajoute un exercice au programme avec sa configuration par défaut',
|
'ajoute un exercice au programme avec sa configuration par défaut',
|
||||||
(tester) async {
|
(tester) async {
|
||||||
|
final programRepository = _FakeProgramRepository();
|
||||||
final exerciseRepository = _FakeExerciseRepository()
|
final exerciseRepository = _FakeExerciseRepository()
|
||||||
..exercises.add(
|
..exercises.add(
|
||||||
Exercise(
|
Exercise(
|
||||||
@ -23,7 +24,7 @@ void main() {
|
|||||||
MaterialApp(
|
MaterialApp(
|
||||||
home: ProgramFormScreen(
|
home: ProgramFormScreen(
|
||||||
programUseCases: _programUseCases(
|
programUseCases: _programUseCases(
|
||||||
_FakeProgramRepository(),
|
programRepository,
|
||||||
exerciseRepository,
|
exerciseRepository,
|
||||||
),
|
),
|
||||||
exerciseUseCases: _exerciseUseCases(exerciseRepository),
|
exerciseUseCases: _exerciseUseCases(exerciseRepository),
|
||||||
@ -31,21 +32,30 @@ void main() {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
await tester.enterText(
|
||||||
|
find.widgetWithText(TextFormField, 'Nom'),
|
||||||
|
'Jambes',
|
||||||
|
);
|
||||||
await tester.tap(find.text('Ajouter un exercice'));
|
await tester.tap(find.text('Ajouter un exercice'));
|
||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
await tester.tap(find.text('Squat'));
|
await tester.tap(find.text('Squat'));
|
||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
expect(find.text('Squat'), findsOneWidget);
|
expect(find.text('Squat'), findsOneWidget);
|
||||||
expect(
|
expect(find.text('Repos 60 s'), findsOneWidget);
|
||||||
find.text('3 séries · Temps + Répétitions · Repos 60 s'),
|
expect(find.text('Exercice ajouté'), findsOneWidget);
|
||||||
findsOneWidget,
|
expect(find.byTooltip("Personnaliser l'exercice"), findsOneWidget);
|
||||||
);
|
expect(find.byType(CheckboxListTile), findsNothing);
|
||||||
expect(find.widgetWithText(CheckboxListTile, 'Temps'), findsOneWidget);
|
|
||||||
expect(
|
await tester.tap(find.text('Enregistrer'));
|
||||||
find.widgetWithText(CheckboxListTile, 'Répétitions'),
|
await tester.pumpAndSettle();
|
||||||
findsOneWidget,
|
|
||||||
);
|
final savedExercise = programRepository.programs.single.exercises.single;
|
||||||
|
expect(savedExercise.setsCount, 3);
|
||||||
|
expect(savedExercise.timeEnabled, isTrue);
|
||||||
|
expect(savedExercise.repsEnabled, isTrue);
|
||||||
|
expect(savedExercise.scoreEnabled, isFalse);
|
||||||
|
expect(savedExercise.restSecondsOverride, 60);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -80,12 +90,16 @@ void main() {
|
|||||||
await tester.tap(find.text('Pompes'));
|
await tester.tap(find.text('Pompes'));
|
||||||
await tester.pumpAndSettle();
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
|
await tester.tap(find.byTooltip("Personnaliser l'exercice"));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
|
expect(find.text("Personnaliser l'exercice"), findsOneWidget);
|
||||||
expect(
|
expect(
|
||||||
find.widgetWithText(CheckboxListTile, 'Répétitions'),
|
find.widgetWithText(SwitchListTile, 'Répétitions'),
|
||||||
findsOneWidget,
|
findsOneWidget,
|
||||||
);
|
);
|
||||||
expect(find.widgetWithText(CheckboxListTile, 'Temps'), findsNothing);
|
expect(find.widgetWithText(SwitchListTile, 'Temps'), findsNothing);
|
||||||
expect(find.widgetWithText(CheckboxListTile, 'Score'), findsNothing);
|
expect(find.widgetWithText(SwitchListTile, 'Score'), findsNothing);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user