tablet-app · lot K K3 — SectionEvent K7 — SectionArticle 12 août 2026

Deux écrans à faire passer du téléphone debout à la borne couchée

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.


Ce qui change, pour les deux

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.

Deux colonnes, jamais une

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

Le texte reste étroit

É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.

Le héros est une bande, pas un écran

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

Pas de feuille modale

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

Rien à agrandir

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 »

Les couleurs viennent de la configuration

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.

Article — le média ancre, le texte défile

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.

Article — 1280 × 800 MDLF · « Ruche 7 »
A
image principale
2
3
4
5
3:12
B
Rucher pédagogique
Ruche 7
A

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.

B

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 il est avant quoi il lit.

C

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.

Quand il manque l'audio, les photos, ou les deux

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.

image principale
2
3
4
5
Rucher pédagogique
Ruche 7

Sans audio

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

Rucher pédagogique
Ruche 7
3:12

Sans photo, avec audio

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

Rucher pédagogique
Ruche 7

Texte seul

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

Deux choses vérifiées dans 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 == truetexte à 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.

Les statistiques sont déjà là — vérifié dans le code

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.

Événement — la carte ancre, le programme défile

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.

Événement — 1280 × 800 Cas E · carnaval
D
Carnaval des Légendes
14/03/2026 → 16/03/2026
E
Programme du jour
10:00 Ouverture des portes
14:00 Grand cortège — départ place du Marché EN COURS
16:30 Remise des prix des chars
18:00 Brûlage du Bonhomme Hiver
20:00 Feu d'artifice
F
5 points · le point orange suit le bloc en cours
D

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.

E

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.

F

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.

Le piège à ne pas déplacer

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.

Tranché le 12 août

Fond clair

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)

Hors dates, aucune prose

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

Et c'est l'i18n qui tranche

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.

Retour à l'accueil après 5 min

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