--- issueRef: "#176" version: 5 updatedBy: {"kind":"agent","agent_id":"57695b92-24d0-4876-837c-76116e70a6ae"} updatedAt: 1785321673401 --- ## #176 — Cadrage UX (2026-07-29) Statut : **cadrage exploitable, nouveau lot**, app téléphone, `lib/presentation/workout_execution_screen.dart`, widget `_TimedStepBody` (L2907-2963), rendu dans `_CurrentStepPane` (branche générale, L2811-2838, `Expanded(child: step.type == time ? _TimedStepBody(...) : ...)`). ### Constat, vérifié en lecture directe `_TimedStepBody` est placé dans un `Expanded` par son parent (toute la hauteur disponible lui est allouée), mais **à l'intérieur** de ce widget rien n'exploite cet espace : ```dart Column( // pas de mainAxisAlignment.center, pas de Expanded interne, pas de FittedBox children: [ Text('Objectif : Xs'), Center(child: Text(remainingLabel, style: displayMedium)), // taille fixe, non scalable if (!running) [...'Chrono prêt'..., FilledButton 'Lancer'], ], ) ``` Le `Text(remainingLabel)` a une taille de police fixe (`displayMedium`), la `Column` s'aligne en haut (comportement par défaut) : tout l'espace du `Expanded` non consommé par le contenu à taille naturelle reste vide en bas. Résultat : le chrono reste petit que le bouton `Lancer` soit visible ou non, et une fois le chrono démarré (`running == true`, le bloc `Lancer`/`Chrono prêt` disparaît), l'espace libéré n'est récupéré par personne — comportement exactement décrit par le ticket. À comparer avec `_RepsWithStopwatchScoreStepBody` (L2965+) et le bloc `Expanded → LayoutBuilder → FittedBox(scaleDown) → SizedBox(hauteur de référence fixe)` déjà utilisé en L2740-2772 pour dimensionner un contenu à une zone variable : ce pattern existe déjà dans ce fichier, `_TimedStepBody` ne le reprend pas. ### Comportement attendu Le chrono (et le bouton `Lancer` quand il est visible) doivent occuper **tout** l'espace vertical disponible dans le `Expanded` parent, dans les deux états : - **Avant démarrage** (`!running`) : chrono + `Chrono prêt` (si prêt) + bouton `Lancer`, empilés et **centrés**, mis à l'échelle ensemble pour remplir la zone disponible (largeur et hauteur). - **Après démarrage** (`running`) : le bloc `Chrono prêt`/`Lancer` disparaît (inchangé), et le chrono seul se remet à l'échelle pour **regrandir** et occuper tout l'espace libéré — pas de saut brutal de layout, juste une nouvelle mise à l'échelle du même mécanisme. ### Implémentation recommandée Réutiliser le mécanisme `FittedBox(fit: BoxFit.contain)` déjà présent dans ce fichier (au lieu de `BoxFit.scaleDown` utilisé ailleurs, qui ne permet que de rétrécir — ici on veut aussi agrandir) : ```dart Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Text('Objectif : ${_formatShortSeconds(step.defaultTargetValue)}'), // inchangé, taille fixe const SizedBox(height: 4), Expanded( // NOUVEAU : réclame tout l'espace restant child: LayoutBuilder( builder: (context, c) => FittedBox( fit: BoxFit.contain, // scale up ET down, contrairement à scaleDown child: SizedBox( width: c.maxWidth, child: Column( mainAxisSize: MainAxisSize.min, children: [ Text(remainingLabel, style: /* displayMedium, référence */), if (!running) ...[ const SizedBox(height: 8), if (readyToStart) Text('Chrono prêt', ...), const SizedBox(height: 4), FilledButton.icon(onPressed: onStartTimer, icon: Icons.play_arrow, label: 'Lancer'), ], ], ), ), ), ), ), ], ) ``` - Le `FittedBox(BoxFit.contain)` prend le bloc de référence (chrono seul, ou chrono + libellé + bouton selon l'état) et le met à l'échelle pour remplir exactement la largeur ET la hauteur du `Expanded` — c'est ce même mécanisme qui fait automatiquement grandir le chrono quand le bouton disparaît : le bloc de référence devient plus petit (moins d'enfants), donc le facteur d'échelle appliqué par `FittedBox` augmente pour combler le même espace disponible. - Le bouton `Lancer` grandit avec le reste (proportionnellement) tant qu'il est visible — cohérent avec la demande explicite du ticket (« chrono + bouton commencer prennent la place disponible »). - Point de vigilance dev : choisir une taille de référence (police du `Text(remainingLabel)`, hauteur du bouton) qui donne un **rendu correct au repos** (échelle ≈ 1) sur la taille d'écran de référence du projet, quitte à ajuster ces valeurs empiriquement — le `FittedBox` absorbe l'écart, mais des proportions de référence déséquilibrées donneraient un mauvais rendu même mis à l'échelle. Ne pas plafonner l'échelle maximale (pas de `scaleDown`) : la demande explicite est que ça puisse grandir au-delà de la taille actuelle. - Ne touche pas au cas `hasTimedWithManualScore` (L2725-2809, `_TimedStepWithManualScoreBody`) : ce lot ne concerne que le chrono seul, sans score manuel, branche générale de `_CurrentStepPane`.