Le code se recopie depuis mymuseum-visitapp — ce n'est pas là qu'est le travail.
Ces écrans sont dessinés pour un téléphone tenu à la verticale, à trente centimètres des yeux.
Une borne fait 1280 px de large, elle est fixe, et on la lit debout à un mètre.
Cette page arrête la mise en page des deux écrans ensemble, parce que les trancher
séparément donnerait deux grammaires différentes sur la même dalle.
Validée le 12 août — les quatre décisions de fin de page sont prises.
Six décisions valables sur toute la borne. Elles ne sortent pas d'une préférence : chacune corrige un idiome de téléphone qui devient faux sur une dalle large et fixe.
La largeur est la ressource qu'on gagne, et une colonne unique la gaspille intégralement. Chaque écran se découpe en un ancrage qui ne bouge pas et un flux qui défile. Sur l'article, le média ancre et le texte défile ; sur l'événement, la carte ancre et le programme défile. mobile — une colonne, tout défile ensemble
Élargir la colonne de texte à 1280 px la rend illisible : l'œil perd la ligne au retour. Le corps de l'article est plafonné autour de 65 caractères et le reste de la largeur va au média, pas au texte. C'est la règle qui justifie à elle seule les deux colonnes.
Sur mobile, l'événement ouvre sur une image qui occupe 52 % de la hauteur, et le parallaxe récompense le défilement. Debout devant une borne, personne ne défile pour découvrir qu'il y a un programme dessous. Le héros tombe à 26 % : il situe, il n'accueille pas. mobile — SliverAppBar, expandedHeight = 0.52 × hauteur
showModalBottomSheet est une réponse à l'étroitesse : sur un téléphone il n'y a
pas de place à côté, alors on empile. Sur une borne il y a la place à côté. Le détail d'un
bloc de programme s'ouvre dans la colonne de droite, sous la carte, sans
masquer ce qu'on regardait.
mobile — _showBlockDetail() en bottom sheet
La carte de l'événement est un aperçu de 220 px qu'il faut toucher pour ouvrir une page
pleine. Sur une borne d'accueil, la carte est la raison pour laquelle on s'arrête :
elle est affichée à sa taille utile d'emblée, et EventMapFullPage ne se porte pas.
mobile — aperçu 220 px + bouton « Agrandir »
mymuseum-visitapp code ses couleurs en dur par flavor client — rouge MDLF, bleu
Fort. tablet-app n'a pas de flavor : un seul APK sert tous les clients et la
charte arrive avec la configuration choisie au pincode. Tout accent dérive de
configuration.primaryColor, comme l'écran Game livré en K4.
Le bleu ardoise de cette page est le repli kTestSecondColor, pas une proposition
de charte.
Un article porte un corps HTML, souvent un audio, et parfois une série d'images. Sur mobile les trois s'empilent et l'audio finit en lecteur flottant par-dessus le texte. En paysage, chacun a sa place et le lecteur audio n'a plus besoin de flotter.
Rail média, 38 %. L'image principale prend la hauteur disponible, les
autres ressources deviennent une bande de vignettes — c'est SliderImages à plat
plutôt qu'en carrousel, parce qu'une borne n'a pas de raison de cacher quatre images
derrière un geste. L'audio est docké en bas du rail : sur mobile,
audio_player_floating flotte au-dessus du texte faute de place. Ici il a une
place, donc il ne flotte plus.
Colonne de lecture, 62 %, texte plafonné. Le rendu HTML
(flutter_widget_from_html) se recopie tel quel ; ce qui change est le
maxWidth qui l'entoure. Le titre passe en display, avec l'étiquette de section
au-dessus — sur une borne, un visiteur arrive au milieu d'un parcours et doit savoir
où il est avant quoi il lit.
Barre de pied, hauteur de doigt. Un seul bouton, cible à
56 px minimum : on tape debout, parfois avec un enfant au bout du bras.
Pas de sélecteur de langue — elle est choisie en amont, la réafficher ici
rouvrirait une décision déjà prise. Et rien d'autre : SectionPageDetail ne
reçoit qu'une section et un booléen isFromMenu, jamais la liste de ses
voisines. Un repère du type « 3 / 8 » n'est donc adossé à aucune donnée — il faudrait
descendre la liste jusqu'ici pour le calculer.
Les trois cas sont possibles en configuration, et le rail média de 38 % ne survit pas tel quel à la disparition de son contenu. La règle qui les couvre tous : le rail n'existe que s'il a quelque chose à ancrer. Sinon la colonne de lecture se centre — ce qui ne contredit pas la règle des deux colonnes, dont l'objet est d'empêcher une ligne de texte de s'étirer sur 1280 px, pas d'imposer une colonne vide.
Le dock disparaît, l'image reprend la hauteur libérée. Pas de lecteur grisé, pas de repli sur une autre langue — le lecteur n'existe pas pour ce visiteur. pas d'entrée audioIds pour la langue choisie
Le rail tombe — une colonne de 38 % contenant un seul lecteur serait un trou, pas une mise en page. L'audio passe en tête de la colonne de lecture, à la largeur du texte, juste sous le titre : c'est là qu'on le cherche quand il est la pièce principale. imageSource nul et contents vide
Une seule colonne, centrée, toujours plafonnée à 65 caractères. Le vide de part et d'autre est voulu : c'est ce qui garde le texte lisible. La tentation d'étirer la ligne pour « remplir » est précisément ce que la règle interdit. ni image, ni contents, ni audio
ArticleDTO, pas supposées
audioIds est une List<TranslationDTO>, pas un id.
L'audio est donc par langue : un article peut avoir sa version française et pas sa
néerlandaise. La règle est donc stricte — pas d'entrée pour la langue choisie, pas de
lecteur du tout. Ni lecteur grisé, ni repli sur une autre langue : un visiteur
néerlandophone à qui la borne servirait l'audio français serait plus mal servi que par le
silence. « Sans audio » est un état du visiteur, pas de l'article.
isContentTop existe déjà et dit si le texte passe avant le média.
En portrait, « avant » veut dire au-dessus ; en paysage il n'y a plus de dessus, donc
le drapeau devient le côté :
isContentTop == true → texte à gauche, rail média à droite ;
sinon la disposition par défaut des maquettes, média à gauche. Le rail garde sa composition
dans les deux cas — image, vignettes, audio docké — il change de bord, pas de contenu. À
câbler, sinon un réglage déjà offert dans manager-app reste sans effet sur la borne.
Rien à câbler. section_page_detail.dart émet sectionView à
l'ouverture et sectionLeave avec la durée au dispose, et il
enveloppe les treize types : l'article sera compté dès que son case
entre dans le switch. statisticsService est câblé à sept endroits de
l'app. L'événement articleRead de mymuseum, plus spécifique, n'existe pas ici —
c'est un ajout facultatif, pas un manque.
En revanche, le marquage « déjà lu » par visiteur
(articleRead en base locale, DatabaseHelper) ne se recopie pas : sur
un appareil partagé, le lecteur suivant n'est pas le précédent.
C'est l'écran du cas E du test-plan : la borne d'accueil d'un carnaval ou d'un festival, qui répond à deux questions et deux seulement — qu'est-ce qui se passe maintenant et où est-ce que ça se passe. La mise en page les met côte à côte, sans geste entre elles.
Bande héros, 26 %. Assez pour porter l'image, le titre et les dates ;
pas assez pour repousser le programme sous la ligne de flottaison. Le dégradé de repli
dérive de primaryColor — c'est exactement le calcul déjà écrit dans l'écran
Game de K4, à réutiliser plutôt qu'à refaire.
Programme, 44 %. La frise verticale à pastilles et trait de liaison de mymuseum tombe : elle coûte 56 px de gouttière pour exprimer une chronologie que l'heure en tête de ligne dit déjà. Le bloc actif garde son fond plein et son pastillage « EN COURS » — c'est l'information la plus demandée de l'écran, elle doit se voir à trois mètres.
Carte, 56 %, toujours vive. Les annotations globales sont dans l'accent,
celles du bloc en cours dans l'orange sémantique — la même paire de couleurs que le
programme, pour que l'œil relie une ligne à un point sans légende. Défilement et zoom
restent actifs ; InteractiveFlag.none était une contrainte d'aperçu.
Les coordonnées de MapAnnotationDTO.geometry.coordinates sont en
[lng, lat] — GeoJSON. Celles de GuidedStep sont en
[lat, lng]. Les deux ordres sont en production, et une confusion
ne lève aucune erreur : elle déplace les points sur une carte qui reste
plausible. La lecture de mymuseum (coords[1] = lat, coords[0] = lng)
se recopie telle quelle, sans passer par un helper venu d'un autre contexte. Le volet parcours
étant exclu de la borne, cet écran ne voit qu'une seule des deux conventions — c'est une raison
de plus de ne pas y introduire l'autre.
La nouvelle section ne doit pas dénoter dans l'app. tablet-app est clair —
kBackgroundColor vaut #FFFFFF et kBackgroundLight
#F3F3F3 — et les maquettes ci-dessus emploient ces valeurs-là,
pas une palette inventée. Le #111111 en dur de l'écran Événement de mymuseum ne
se recopie donc pas : il vient d'un téléphone consulté en extérieur.
mymuseum — Scaffold backgroundColor: Color(0xFF111111)
Le carnaval dure trois jours, la borne tourne les 362 autres. Le programme reste affiché,
sans pastille « EN COURS » et avec les blocs en gris — le surlignage est
calculé sur DateTime.now(), il s'éteint tout seul. La bande héros continue de
porter les dates, qui sont numériques donc sans traduction, et suffisent à
dire qu'on est avant ou après.
état passé = les mêmes blocs, désaturés — pas un écran différent
Ton réflexe sur les traductions était le bon, et il est plus contraignant que prévu :
tablet-app n'a aucun système d'i18n — pas de l10n/,
pas d'AppLocalizations, les libellés sont en français en dur
("Ce type n'est pas supporté"). Un bandeau « Édition passée » serait donc
français pour un visiteur néerlandophone. Le vrai libellé viendrait soit
d'un champ backend traduit — qui n'existe pas — soit d'un i18n à créer, ce qui n'est pas un
chantier de K3. D'où la règle ci-dessus : on affiche des dates, pas des phrases.
Adopté, et écrit une seule fois pour les treize types :
section_page_detail enveloppe déjà toutes les sections et porte déjà les crochets
initState / dispose des statistiques. Le minuteur d'inactivité va
là, pas dans les deux écrans de cette page. Bénéfice de bord : un retour automatique passe
par le même dispose, donc il émet un sectionLeave avec sa durée
réelle — la statistique reste juste au lieu de compter une consultation ouverte jusqu'au
prochain visiteur.
hors périmètre K3/K7 — à ouvrir comme chantier propre