Files
GameTime/.ideai/tickets/131/carnet.md
Blomios 917777e18b chore(wip): consolidation intermédiaire multi-tickets (sprints Statistiques, UI, Bug resolution, Serveur-client)
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>
2026-07-28 16:48:54 +02:00

4.6 KiB

issueRef, version, updatedBy, updatedAt
issueRef version updatedBy updatedAt
#131 5
kind
user
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 un ListView scrollable (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, le Column qui empile texte/corps d'étape/score d'étape/boutons) dans un SingleChildScrollView lorsque le contenu dépasse l'espace disponible — remplacer les Flexible internes qui n'ont plus de sens dans un contexte scrollable par leur contenu intrinsèque directement (un ScrollView donne une hauteur non bornée à son enfant, donc Flexible/Expanded y sont invalides et doivent être retirés à cet endroit précis).
  • Alternative équivalente au niveau du parent : envelopper le Column de la branche hasStepSequence (ligne 228-281, qui empile _StepSequencePanel + blocs score de série) dans un SingleChildScrollView, en remplaçant l'Expanded du _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 SingleChildScrollView ne 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).