Regroupe l'état de travail en cours réalisé dans un même worktree sur plusieurs tickets/sprints (#85, #136, #145, #155-160, #162-164), mélangeant des tickets QA et inProgress. Ne constitue pas une feature terminée : commit de sauvegarde avant triage/split par ticket en branches feature/* dédiées. Exclut les dossiers d'environnement de build locaux et le heap dump parasite (.gitignore mis à jour). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
4.6 KiB
issueRef, version, updatedBy, updatedAt
| issueRef | version | updatedBy | updatedAt | ||
|---|---|---|---|---|---|
| #131 | 5 |
|
1785216979125 |
#131 — Cadrage technique Architect (2026-07-27)
Root cause identifiée dans le code réel — combinaison de contenu dense jamais rendue scrollable, dans une branche de layout spécifique.
L'écran d'exécution (lib/presentation/workout_execution_screen.dart) a deux branches de layout selon que l'exercice a des étapes (hasStepSequence, ligne 192-323) :
- Branche sans étapes (ligne 283-318) : le contenu (
SetMeasureInput) est déjà enveloppé dans unListViewscrollable (ligne 300-317) — pas de risque d'overflow, quelle que soit la densité. - Branche avec étapes (ligne 227-282, celle du scénario rapporté : exercice à 2 étapes) : empile un
_StepSequencePanel(Expanded, non scrollable) puis, si activés, un bloc score de série_StepSetResultSummary(SizedBox(height: 66)) et/ou_ScoreStopwatchInput(SizedBox(height: 96)) — aucun scroll nulle part dans cette branche.
À l'intérieur, _CurrentStepPane (ligne 2321-2449) empile pour l'étape courante, dans un Column non scrollable : titre d'étape, nom, corps de l'étape (Flexible(fit: FlexFit.tight) — chrono ou reps), puis si step.hasScore (score au niveau de l'étape, ligne 2395-2409) un _StepScoreInput supplémentaire (Flexible(fit: FlexFit.loose)), puis la rangée de boutons "Passer l'étape". Flexible.loose ne force pas le contenu à rétrécir sous sa taille intrinsèque minimale (champ de score + boutons +/- + libellés) — il autorise seulement le widget à être plus petit que l'espace alloué, pas à comprimer son propre contenu.
Le scénario rapporté combine trois blocs verticaux simultanément : chrono d'étape (10s) + score d'étape ("panier", step.hasScore) + score de série ("panier" aussi, _exercise.manualScoreEnabled → déclenche le SizedBox(height: 66) supplémentaire en dehors de _CurrentStepPane). C'est une combinaison de configuration peu courante (score à la fois au niveau étape ET au niveau série, avec un chrono d'étape court) qui n'avait probablement jamais été testée sur petit écran — la somme des hauteurs minimales requises dépasse la hauteur réellement disponible, et comme rien n'est scrollable dans cette branche, Flutter rapporte l'overflow exact ("Bottom Overflowed by 82 pixels") au lieu de dégrader proprement.
Correctif (contrat clair pour DevFrontend)
Rendre la branche hasStepSequence dégradable par scroll, symétriquement à ce que fait déjà la branche sans étapes :
- Option minimale et cohérente avec l'existant : envelopper le contenu de
_CurrentStepPane(ligne 2356, leColumnqui empile texte/corps d'étape/score d'étape/boutons) dans unSingleChildScrollViewlorsque le contenu dépasse l'espace disponible — remplacer lesFlexibleinternes qui n'ont plus de sens dans un contexte scrollable par leur contenu intrinsèque directement (unScrollViewdonne une hauteur non bornée à son enfant, doncFlexible/Expandedy sont invalides et doivent être retirés à cet endroit précis). - Alternative équivalente au niveau du parent : envelopper le
Columnde la branchehasStepSequence(ligne 228-281, qui empile_StepSequencePanel+ blocs score de série) dans unSingleChildScrollView, en remplaçant l'Expandeddu_StepSequencePanel(ligne 231) par une hauteur qui s'adapte à son contenu. - Ne pas retirer le mode "tient sur un écran sans scroll" pour le cas courant (chrono seul, ou score seul) : le
SingleChildScrollViewne change rien visuellement quand tout le contenu tient déjà — c'est un filet de sécurité pour les combinaisons denses, pas un changement de comportement pour le cas nominal. - Ne pas retirer d'information ni de bouton pour "faire tenir" le contenu (pas de compromis produit ici) — c'est un problème de layout à corriger, pas un périmètre fonctionnel à réduire.
Tests
- Widget test avec la configuration exacte du ticket (3 séries, 20 passages, 2 étapes, étape courante = chrono 10s + score d'étape "panier", score de série "panier") sur une hauteur d'écran réduite (ex. contrainte de test type Wear/petit téléphone) : aucune exception
RenderFlex overflowed, le contenu doit rester scrollable jusqu'au bouton "Passer l'étape". - Test de non-régression sur le cas nominal (chrono seul, pas de score d'étape ni de série) : layout identique à avant, pas de scroll visible si le contenu tient.
Prêt pour implémentation directe, aucun arbitrage supplémentaire nécessaire. Priorité critique confirmée (bloque l'affichage complet de l'écran d'exécution pour cette combinaison de configuration).