Responsive Design Partie 3 : Rendre le contenu dynamique et adaptatif đ§©

- PrĂ©ambule : OĂč en sommes-nous ?
- Présentation des propriétés et paramÚtres que nous allons utiliser dans ce chapitre
- Tutoriel : Mise en pratique des techniques dâadaptation
- Ătape 4 : Solutions crĂ©atives
- Ătape 5 : La remodĂ©lisation (Master / DĂ©tails)
- Conclusion
PrĂ©ambule : OĂč en sommes-nous ?
Faisons un rapide récapitulatif :
-
Dans la Partie 1, nous avons explorĂ© les grandes stratĂ©gies du design rĂ©actif (Reflow, Repositionnement, Afficher/Masquer) et lâapproche âMobile-Firstâ.
-
Dans la Partie 2, nous avons mis en pratique la structure en utilisant les âGroup Containersâ auto-adaptatifs (en-tĂȘte, menu latĂ©ral, corps de page, pied de page), crĂ©ant ainsi une disposition fluide qui sâĂ©tire et se compresse sans utiliser de coordonnĂ©es X ou Y.
Lâobjectif de cette Partie 3 est de remplir cette structure. Nous allons intĂ©grer un menu de navigation, des indicateurs de performance (KPI) et une galerie de donnĂ©es. Nous allons configurer ces Ă©lĂ©ments internes pour quâils appliquent concrĂštement les thĂ©ories dâadaptation vues dans le premier volet, en exploitant les propriĂ©tĂ©s de Power Apps.
Présentation des propriétés et paramÚtres que nous allons utiliser dans ce chapitre
La propriĂ©tĂ© [Ăcran].Size (Le point de rupture)

Pour rendre le contenu dâun Ă©cran rĂ©actif, nous allons principalement nous appuyer sur la propriĂ©tĂ© de taille de lâĂ©cran, appelĂ©e directement par son nom (par exemple 'Mon Ă©cran'.Size).
Cette propriĂ©tĂ© renvoie un entier de 1 Ă 4 reprĂ©sentant la taille de lâappareil. Câest ce que lâon appelle le âBreakpointâ (point de rupture). Ces seuils de basculement peuvent dâailleurs ĂȘtre configurĂ©s sur mesure pour votre application via la propriĂ©tĂ© SizeBreakpoints de lâobjet App, dont la valeur par dĂ©faut est [600; 900; 1200].
Astuce : Quand une application a un format âRĂ©actifâ dans ses paramĂštres, des contrĂŽles de facteur de forme apparaissent dans lâĂ©diteur en bas de lâĂ©cran. Ces boutons vous permettent de changer instantanĂ©ment la taille de la zone de conception (le canevas) pour simuler les diffĂ©rents appareils, et ce, en se basant exactement sur les SizeBreakpoints paramĂ©trĂ©s dans lâobjet
App!
Voici la correspondance par défaut :
| Appareil | Valeur Size | Largeur de l'écran (par défaut) |
|---|---|---|
| Téléphone | 1 | <= 600 px |
| Tablette portrait | 2 | 601 px - 900 px |
| Tablette paysage | 3 | 901 px - 1200 px |
| Ordinateur de bureau | 4 | > 1200 px |
Avertissement : Il est frĂ©quent de voir lâutilisation de
App.ActiveScreen.Sizedans divers exemples pour cibler cette valeur. Cependant, cette pratique est dĂ©conseillĂ©e : la valeur renvoyĂ©e parApp.ActiveScreennâest pas actualisĂ©e si lâutilisateur modifie lâorientation de son appareil (passage du mode portrait au mode paysage) en cours dâutilisation. Pour garantir un rendu fiable, il est prĂ©fĂ©rable dâappeler systĂ©matiquement la propriĂ©tĂ©Sizepar le nom exact de lâĂ©cran concernĂ©. (Note : Jâai profitĂ© de la rĂ©daction de cet article pour faire remonter ce bug Ă lâĂ©quipe produit. Il se peut donc que ce comportement ait Ă©tĂ© corrigĂ© Ă lâheure oĂč vous lisez ces lignes).
Tutoriel : Mise en pratique des techniques dâadaptation
Ătape 1 : Le Menu de navigation (Technique : Afficher ou Masquer)

Dans notre conteneur latĂ©ral gauche (ctn_Index_1), insĂ©rez un contrĂŽle moderne de type âToolbarâ (Barre dâoutils) et nommez-le tlb_Menu_1 pour faire office de menu.

Sur un Ă©cran dâordinateur, lâespace permet dâafficher lâicĂŽne et le texte du menu. Sur un smartphone, ce texte repousserait le contenu principal hors de la zone visible de lâĂ©cran. Nous allons donc Afficher/Masquer le texte de maniĂšre conditionnelle.
1. Rappel : La largeur du conteneur parent : Souvenez-vous, dans la Partie 2, nous avions configurĂ© la propriĂ©tĂ© Largeur (Width) du conteneur de notre menu latĂ©ral (ctn_Index_1) pour quâelle sâadapte en fonction de lâĂ©cran :
If('Mon écran réactif Partie 3'.Size < 2, 60, 210)
Explication : Si la taille de lâĂ©cran est infĂ©rieure Ă 2 (donc un mobile), la largeur du menu se rĂ©duit Ă 60px. Sinon, elle sâĂ©tablit Ă 210px.
2. Adapter la disposition du menu : Nous allons utiliser la mĂȘme logique pour afficher ou masquer le texte du menu. Ajoutez un contrĂŽle Barre dâoutils(Toolbar) (tlb_Menu_1) et modifiez sa propriĂ©tĂ© Disposition (Layout) avec cette formule, qui sâinspire de celle que nous avons utilisĂ© pour le container :
If('Mon écran réactif Partie 3'.Size < 2,
'Toolbar.Layout'.IconOnly,
'Toolbar.Layout'.IconBefore
)
Explication : Sur mobile (Size 1), la barre dâoutils bascule en mode âIcĂŽne uniquementâ. Sur des Ă©crans plus larges, elle affiche âIcĂŽne + Texteâ. Lâergonomie est prĂ©servĂ©e en limitant lâencombrement horizontal.

Ătape 2 : Les cartes KPI (Technique : Repositionner)

En haut de notre contenu principal, nous avons ajouté un conteneur horizontal (ctn_kpi_1) nous allons y placer trois petites cartes affichant des métriques (Clients, Ventes, CA).
Vous pouvez copier-coller le code YAML ci-dessous directement Ă lâintĂ©rieur de votre conteneur ctn_kpi_1 pour gĂ©nĂ©rer automatiquement ces trois cartes KPI :
Code YAML cartes KPI (Déployer pour voir le code)
- kpi_Clients_1:
Control: GroupContainer@1.5.0
Variant: AutoLayout
Properties:
AlignInContainer: =AlignInContainer.SetByContainer
DropShadow: =DropShadow.Semilight
Fill: =RGBA(255, 244, 240, 1)
Height: =40
LayoutAlignItems: =LayoutAlignItems.Center
LayoutDirection: =LayoutDirection.Horizontal
LayoutJustifyContent: =LayoutJustifyContent.Center
LayoutMinHeight: =50
LayoutMinWidth: =210
LayoutWrap: =true
PaddingBottom: =5
PaddingLeft: =5
PaddingRight: =5
PaddingTop: =5
RadiusBottomLeft: =8
RadiusBottomRight: =8
RadiusTopLeft: =8
RadiusTopRight: =8
X: =40
Y: =40
Children:
- img_IconClients_1:
Control: Image@2.2.3
Properties:
AlignInContainer: =AlignInContainer.Center
FillPortions: =1
Height: =30
Image: ="data:image/svg+xml;utf8, " & EncodeUrl("<svg viewBox='0 0 24 24' xmlns='[http://www.w3.org/2000/svg](http://www.w3.org/2000/svg)'><path d='M16 11c1.66 0 2.99-1.34 2.99-3S17.66 5 16 5c-1.66 0-3 1.34-3 3s1.34 3 3 3zm-8 0c1.66 0 2.99-1.34 2.99-3S9.66 5 8 5C6.34 5 5 6.34 5 8s1.34 3 3 3zm0 2c-2.33 0-7 1.17-7 3.5V19h14v-2.5c0-2.33-4.67-3.5-7-3.5zm8 0c-.29 0-.62.02-.97.05 1.16.84 1.97 1.97 1.97 3.45V19h6v-2.5c0-2.33-4.67-3.5-7-3.5z' fill='#D83B01'/></svg>")
LayoutMinHeight: =28
LayoutMinWidth: =28
Width: =40
- lbl_ValueClients_1:
Control: Label@2.5.1
Properties:
Align: =Align.Center
AlignInContainer: =AlignInContainer.Center
Color: =RGBA(216, 59, 1, 1)
FillPortions: =1
FontWeight: =FontWeight.Bold
Height: =30
LayoutMinHeight: =28
LayoutMinWidth: =28
Size: =11
Text: ="34"
- kpi_Ventes_1:
Control: GroupContainer@1.5.0
Variant: AutoLayout
Properties:
AlignInContainer: =AlignInContainer.SetByContainer
DropShadow: =DropShadow.Semilight
Fill: =RGBA(240, 255, 244, 1)
Height: =40
LayoutAlignItems: =LayoutAlignItems.Center
LayoutDirection: =LayoutDirection.Horizontal
LayoutJustifyContent: =LayoutJustifyContent.Center
LayoutMinHeight: =50
LayoutMinWidth: =210
LayoutWrap: =true
PaddingBottom: =5
PaddingLeft: =5
PaddingRight: =5
PaddingTop: =5
RadiusBottomLeft: =8
RadiusBottomRight: =8
RadiusTopLeft: =8
RadiusTopRight: =8
X: =40
Y: =40
Children:
- img_IconVentes_1:
Control: Image@2.2.3
Properties:
AlignInContainer: =AlignInContainer.Start
FillPortions: =1
Height: =30
Image: ="data:image/svg+xml;utf8, " & EncodeUrl("<svg viewBox='0 0 24 24' xmlns='[http://www.w3.org/2000/svg](http://www.w3.org/2000/svg)'><path d='M7 18c-1.1 0-1.99.9-1.99 2S5.9 22 7 22s2-.9 2-2-.9-2-2-2zM1 2v2h2l3.6 7.59-1.35 2.45c-.16.28-.25.61-.25.96 0 1.1.9 2 2 2h12v-2H7.42c-.14 0-.25-.11-.25-.25l.03-.12.9-1.63h7.45c.75 0 1.41-.41 1.75-1.03l3.58-6.49c.08-.14.12-.31.12-.48 0-.55-.45-1-1-1H5.21l-.94-2H1zm16 16c-1.1 0-1.99.9-1.99 2s.89 2 1.99 2 2-.9 2-2-.9-2-2-2z' fill='#107C10'/></svg>")
LayoutMinHeight: =28
LayoutMinWidth: =28
Width: =40
- lbl_ValueVentes_1:
Control: Label@2.5.1
Properties:
Align: =Align.Center
AlignInContainer: =AlignInContainer.Start
Color: =RGBA(16, 124, 16, 1)
FillPortions: =1
FontWeight: =FontWeight.Bold
Height: =30
LayoutMinHeight: =28
LayoutMinWidth: =28
Size: =11
Text: ="128"
- kpi_CA_1:
Control: GroupContainer@1.5.0
Variant: AutoLayout
Properties:
AlignInContainer: =AlignInContainer.SetByContainer
DropShadow: =DropShadow.Semilight
Fill: =RGBA(240, 244, 255, 1)
Height: =40
LayoutAlignItems: =LayoutAlignItems.Center
LayoutDirection: =LayoutDirection.Horizontal
LayoutJustifyContent: =LayoutJustifyContent.Center
LayoutMinHeight: =50
LayoutMinWidth: =210
LayoutWrap: =true
PaddingBottom: =5
PaddingLeft: =5
PaddingRight: =5
PaddingTop: =5
RadiusBottomLeft: =8
RadiusBottomRight: =8
RadiusTopLeft: =8
RadiusTopRight: =8
X: =40
Y: =40
Children:
- img_IconCA_1:
Control: Image@2.2.3
Properties:
AlignInContainer: =AlignInContainer.Start
FillPortions: =1
Height: =30
Image: ="data:image/svg+xml;utf8, " & EncodeUrl("<svg viewBox='0 0 24 24' xmlns='[http://www.w3.org/2000/svg](http://www.w3.org/2000/svg)'><path d='M16 6l2.29 2.29-4.88 4.88-4-4L2 16.59 3.41 18l6-6 4 4 6.3-6.29L22 12V6z' fill='#0078D4'/></svg>")
LayoutMinHeight: =28
LayoutMinWidth: =28
Width: =40
- lbl_ValueCA_1:
Control: Label@2.5.1
Properties:
Align: =Align.Center
AlignInContainer: =AlignInContainer.Start
Color: =App.Theme.Colors.Primary
FillPortions: =1
FontWeight: =FontWeight.Bold
Height: =30
LayoutMinHeight: =28
LayoutMinWidth: =28
Size: =11
Text: ="45.2 KâŹ"

Lâobjectif : Sur un grand Ă©cran, ces trois blocs sâalignent cĂŽte Ă cĂŽte. Sur un smartphone, lâespace horizontal est insuffisant. La technique du Repositionnement consiste Ă les empiler verticalement lorsque lâespace manque. Lâutilisation des Layout Containers simplifie cette Ă©tape grĂące Ă la propriĂ©tĂ© de renvoi Ă la ligne automatique.
Toutefois, une spĂ©cificitĂ© technique doit ĂȘtre gĂ©rĂ©e : dans Power Apps, un conteneur horizontal configurĂ© avec un renvoi Ă la ligne nâajuste pas automatiquement sa propre hauteur globale pour sâadapter aux Ă©lĂ©ments passĂ©s Ă la ligne infĂ©rieure. Nous devons inverser la logique : ce nâest pas le conteneur qui va dĂ©terminer la taille et la position des objets quâil contient, mais les objets quâil contient qui vont dĂ©terminer sa hauteur. Pour cela, nous nous appuierons sur la position en Y et la hauteur de la derniĂšre carte KPI pour dĂ©terminer la hauteur du conteneur.
đĄPour rĂ©aliser cette fonctionnalitĂ©, nous allons exploiter un comportement mĂ©connu : les objets dans un container ont des propriĂ©tĂ©s de sortie X et Y qui correspondent Ă leur position dans le container. ce qui sera bien utile pour dĂ©terminer la hauteur que notre container doit avoir pour afficher tous les Ă©lĂ©ments qui ont Ă©tĂ© renvoyĂ© Ă la ligne.
1. Configurer les cartes enfants : Pour que le paramĂštre de renvoi Ă la ligne sâactive, les Ă©lĂ©ments Ă lâintĂ©rieur doivent possĂ©der une largeur minimale fixe.
-
Sélectionnez vos trois blocs KPI.
-
Définissez leur propriété Largeur minimale (LayoutMinWidth) à une valeur pertinente, par exemple
210.
2. Configurer le conteneur parent (Les KPI) :
-
Sélectionnez
ctn_kpi_1. -
Assurez-vous que la Direction (LayoutDirection) est configurée sur
Horizontale. -
Activez la propriĂ©tĂ© Renvoyer Ă la ligne (LayoutWrap) en la passant Ă
true. -
Afin de forcer lâajustement manuel de sa hauteur, dĂ©sactivez son expansion verticale automatique (Portions de remplissage (FillPortions) sur
0). -
Enfin, modifiez la propriété Hauteur (Height) avec la formule ci-dessous :
Height: =kpi_CA_1.Y + kpi_CA_1.Height + Self.PaddingBottom
Explication : Cette formule indique au conteneur parent de calculer sa hauteur en fonction de la position du dernier Ă©lĂ©ment (ici kpi_CA_1). Elle prend sa position verticale sur lâaxe (Y), y ajoute sa hauteur (Height), puis y additionne la marge infĂ©rieure du conteneur parent (Self.PaddingBottom). Ainsi, lorsque lâespace se rĂ©duit et que les KPI passent Ă la ligne, le conteneur calcule sa nouvelle position et sâagrandit vers le bas de maniĂšre fluide.
đĄNotez que les container des cartes KPI conservent leur propriĂ©tĂ© âlargeur flexibleâ, quand les 3 cartes sont rĂ©parties sur deux lignes, la derniĂšre occupe toute la largeur :
â ïž Attention : Avec cette mĂ©thode, nous utilisons la position du dernier objet dans le container, sâil est dĂ©place ou si un objet est placĂ© aprĂšs lui, le comportement du container pourrait sâen trouver perturbĂ©
Ătape 3 : La galerie de donnĂ©es (Technique : Ajuster dynamiquement / Reflow)

En dessous de nos KPI, nous allons crĂ©er une galerie pour afficher une liste dâĂ©lĂ©ments, que nous structurerons avec un contrĂŽle de carte moderne.
1. Création de la galerie :
-
Insérez une Galerie verticale dans le conteneur
ctn_Content_1et nommez-lagal_Cards_1. -
DĂ©finissez ses ĂlĂ©ments (Items) avec des donnĂ©es de test :
=Table(CustomGallerySample, CustomGallerySample, CustomGallerySample). -
Changez la disposition par défaut pour un modÚle vide.
-
Passez son Remplissage du modĂšle (TemplatePadding) Ă
0(nous allons gĂ©rer les marges directement sur la carte Ă lâintĂ©rieur). -
Dans sa propriĂ©tĂ© Taille du modĂšle (TemplateSize), ajoutez Max( ⊠; 90) au tour de la formule. Cela empĂȘchera les lignes de la galerie dâĂȘtre rĂ©duites en deçà de 90 pixels. vous devriez obtenir la formule :
Max(If(Self.Layout = Layout.Horizontal; Min(280; Self.Width - 60); Min(280; Self.Height - 60));90)

2. Création de la carte interne :
-
Sélectionnez la premiÚre ligne (le modÚle) de votre galerie et insérez-y un contrÎle moderne Carte (ModernCard) et nommez-le
Card1_1. - Liez les propriétés de la carte aux données de la galerie. Par exemple :
-
Titre (Title) :
=ThisItem.SampleHeading -
Sous-titre (Subtitle) :
=ThisItem.SampleText -
Image (HeaderImage) :
=ThisItem.SampleImage
-
-
DĂ©finissez les coordonnĂ©es de la carte : X et Y Ă
8. Nous nous servirons de cet espacement comme marges. - Ajustez dynamiquement sa taille pour quâelle remplisse lâespace du modĂšle tout en respectant ces marges :
-
Largeur (Width) :
=Parent.TemplateWidth - Self.X * 2 - 5(le-5compense la barre de dĂ©filement potentielle, cela Ă©vitera que âlâascenseurâ ne tronque le contenu de la galerie) -
Hauteur (Height) :
=Parent.TemplateHeight - Self.Y * 2
-

- Nous allons limiter la hauteur du modĂšle de la galerie, afin que celui-ci ne soit pas trop important sur les grands Ă©crans, repĂ©rez le paramĂštre Taille du modĂšle (TemplateSize) : Il contiendra peut ĂȘtre la formule suivante qui est liĂ©e au modĂšle galerie verticale vide :
If(Self.Layout = Layout.Horizontal; Min(280; Self.Width - 60); Min(280; Self.Height - 60))
- Ajoutez un Max( ⊠; 90) autour de cette formule pour obtenir :
Max(If(Self.Layout = Layout.Horizontal; Min(280; Self.Width - 60); Min(280; Self.Height - 60));90)
Vous devriez obtenir ce résultat, une galerie contenant une carte sur une colonne :

Nous allons maintenant appliquer la technique du Reflow sâappliquera. PlutĂŽt que dâavoir une liste dĂ©roulante dâune seule colonne, lâobjectif est dâafficher 1 colonne sur smartphone, 2 ou 3 sur tablette, et jusquâĂ 6 sur un grand Ă©cran PC.
Pour cela, nous allons manipuler la propriĂ©tĂ© Nombre dâinclusions (WrapCount) de la galerie (qui nâaccepte quâune valeur comprise entre 1 et 10, toute valeur supĂ©rieure ou infĂ©rieure Ă©tant ignorĂ©e) avec une formule mathĂ©matique basĂ©e sur la largeur :
WrapCount: =Min(Max(1, Trunc(Self.Width / 250, 0)), 6)
Explication de cette formule :
-
Self.Width / 250: La formule divise la largeur totale disponible de la galerie par la largeur souhaitĂ©e pour une carte (ici 250px). Cela indique combien de cartes peuvent thĂ©oriquement ĂȘtre alignĂ©es. -
Trunc(..., 0): La fonction arrondit ce rĂ©sultat Ă lâentier infĂ©rieur (ex: si lâespace permet 2.8 cartes, on limite Ă 2 colonnes pour Ă©viter quâelles ne soient tronquĂ©es). -
Max(1, ...): Cette fonction agit comme une sĂ©curitĂ© pour forcer un minimum de 1 colonne, mĂȘme sur un Ă©cran trĂšs Ă©troit. -
Min(..., 6): Cette fonction plafonne lâaffichage Ă 6 colonnes maximum sur les Ă©crans ultra-larges afin de maintenir la lisibilitĂ© de lâinformation (et nous assure de ne jamais dĂ©passer la limite technique de 10 inhĂ©rente Ă cette propriĂ©tĂ©).
Câest le principe de base du design fluide : le composant calcule sa disposition optimale en temps rĂ©el selon lâespace allouĂ©.

Alternative plus simple : Si les mathĂ©matiques vous rebutent, il existe une mĂ©thode alternative beaucoup plus simple consistant Ă lier directement ce nombre dâinclusions Ă la propriĂ©tĂ© de taille de lâĂ©cran :
WrapCount: ='Mon écran'.Size
RĂ©sultat attendu : Puisque la propriĂ©tĂ© Size renvoie un chiffre allant de 1 Ă 4, votre galerie affichera automatiquement 1 colonne sur un tĂ©lĂ©phone, 2 sur une tablette en mode portrait, 3 sur une tablette en mode paysage, et 4 sur un Ă©cran dâordinateur de bureau. Câest une solution extrĂȘmement rapide Ă mettre en place et trĂšs souvent suffisante pour la majoritĂ© des besoins !
đĄ Information : Lâexemple est montrĂ© avec une galerie verticale, mais la logique sera la mĂȘme avec une galerie horizontale, sauf que le nombre dâinclusions fera apparaĂźtre des lignes Ă la place des colonnes.
â ïž Avertissement : Le nombre dâinclusions nâest pas disponible sur les galeries Ă hauteur flexible.
Ătape 4 : Solutions crĂ©atives

Parfois, la solution responsive idéale ne réside pas dans les propriétés natives des conteneurs, mais dans des astuces plus originales. Voici un exemple concret : gérer un texte long dans un espace restreint (comme un pied de page) sans faire déborder le design.
Au lieu de tronquer un texte long ou de forcer un retour Ă la ligne qui augmenterait inĂ©vitablement la hauteur de lâĂ©cran (ce qui est gĂȘnant pour un pied de page fixe comme notre ctn_Footer_1), nous pouvons utiliser un texte dĂ©filant de type âmarqueeâ grĂące au composant HTML de Power Apps.
Création du texte défilant :
-
Dans votre conteneur de pied de page (
ctn_Footer_1), insérez un contrÎle Texte HTML (HtmlViewer) et nommez-leHtmlText1_1. -
Définissez son Alignement dans le conteneur (AlignInContainer) sur
Stretchet ses Portions de remplissage (FillPortions) Ă1pour quâil occupe tout lâespace disponible horizontalement. -
Passez toutes ses marges intĂ©rieures (Padding) Ă
0. -
Dans la propriété HtmlText, insérez le code suivant qui tire parti de la balise HTML
<marquee>:
="<marquee direction='left' scrollamount='3' behavior='scroll' style='font-size:16px; background:#0000; padding:0px;'>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper congue, euismod non, mi.
</marquee>"
Cette technique offre une excellente rĂ©ponse au manque dâespace sur les mobiles tout en conservant lâintĂ©gralitĂ© de lâinformation accessible pour lâutilisateur.

â ïž Avertissement : Attention cependant Ă veiller Ă ce que cette solution ne perturbe pas lâaccessibilitĂ© de votre application. Les textes en mouvement continu peuvent ĂȘtre difficiles Ă lire pour certains utilisateurs ou perturber lâutilisation de lecteurs dâĂ©cran. Ă utiliser avec parcimonie !
Ătape 5 : La remodĂ©lisation (Master / DĂ©tails)
LâĂ©cran âtableau de bordâ que nous avons dĂ©veloppĂ© jusquâĂ prĂ©sent (avec ses indicateurs et sa galerie) ne se prĂȘte pas vraiment Ă une dĂ©monstration de la technique de RemodĂ©lisation (Rearchitect). Par chance, Power Apps propose nativement un modĂšle dâĂ©cran parfait pour illustrer cette stratĂ©gie : lâĂ©cran âTable et formulaireâ (composĂ© comme son nom lâindique : dâune table et dâun formulaire, gĂ©nĂ©rable en un clic depuis lâĂ©diteur). Ajoutez cet Ă©cran depuis le volet âArborescenceâ:

Cette technique consiste Ă modifier lâarchitecture mĂȘme de la page en fonction de lâespace. Sur un Ă©cran dâordinateur (ou une tablette), lâutilisateur voit Ă la fois la liste des Ă©lĂ©ments Ă gauche (Master) et le dĂ©tail du formulaire Ă droite (Details). Sur smartphone en revanche, lâĂ©cran est trop Ă©troit pour afficher les deux cĂŽte Ă cĂŽte : lâinterface se mĂ©tamorphose alors en une navigation âĂ deux vuesâ oĂč lâon affiche soit la liste, soit le formulaire.
Lâastuce repose sur lâinteraction entre deux conteneurs frĂšres (SidebarContainer1 pour la table et RightContainer1 pour le formulaire) et des variables de contexte pilotĂ©es par lâutilisateur (isItemSelected et newMode).

1. Le comportement sur grand écran (Tablette / PC) :
Lorsque Screen2.Size est supĂ©rieur Ă ScreenSize.Small (soit une valeur > 1), la taille de lâĂ©cran nâest pas considĂ©rĂ©e comme contraignante :
-
Les deux conteneurs ont leur propriĂ©tĂ© Visible (Visible) Ă©valuĂ©e Ă
true. Ils sont donc affichés simultanément. -
La répartition opÚre grùce à la propriété Portions de remplissage (FillPortions). Le panneau de gauche (Table) se voit attribuer
3portions, et celui de droite (Formulaire) reçoit7portions. Le moteur de rendu divise donc naturellement lâĂ©cran en un ratio 30% / 70%.
2. Le comportement sur petit écran (Smartphone) :
Lorsque lâĂ©cran bascule au format tĂ©lĂ©phone (Screen2.Size = ScreenSize.Small), la logique change du tout au tout. Les propriĂ©tĂ©s dâaffichage deviennent strictement conditionnelles et âsâĂ©coutentâ mutuellement :
Pour le panneau de gauche contenant la Table (SidebarContainer1) :
Visible: = If(And(Screen2.Size = ScreenSize.Small; Or(newMode; isItemSelected)); false; true) FillPortions: =If(And(Screen2.Size = ScreenSize.Small; Or(newMode; isItemSelected)); 0; 3)
Explication : Si nous sommes sur un petit Ă©cran ET que lâutilisateur a sĂ©lectionnĂ© un Ă©lĂ©ment (isItemSelected) OU a cliquĂ© sur le bouton âNouveauâ (newMode), la liste comprend quâelle doit laisser sa place : sa visibilitĂ© passe Ă false et ses portions tombent Ă 0. Dans le cas contraire, elle reste visible et occupe 100% de lâĂ©cran (car lâautre conteneur sera masquĂ©).
Pour le panneau de droite contenant le Formulaire (RightContainer1) :
Visible: = If(Or(deleteMode; And(Screen2.Size = ScreenSize.Small; !newMode; !isItemSelected)); false; true)
Explication : Par dĂ©faut sur un petit Ă©cran, si lâutilisateur nâa encore rien sĂ©lectionnĂ© (!isItemSelected) ET nâest pas en train de crĂ©er un Ă©lĂ©ment (!newMode), le formulaire reste cachĂ© (false). Le panneau libĂšre ainsi tout lâespace pour laisser la table sâafficher. DĂšs que lâutilisateur interagit avec la table, ce panneau devient visible et sâapproprie ses 7 portions habituelles (soit la totalitĂ© de lâĂ©cran puisque le panneau de la table est passĂ© Ă 0).
Conseil :
ScreenSize.Smallest une variable dâĂ©numĂ©ration interne de Power Apps qui Ă©quivaut simplement au chiffre1. Lâutiliser rend vos formules sĂ©mantiquement plus claires Ă lire quâun simple>= 1.
GrĂące Ă cette jonglerie intelligente sur les propriĂ©tĂ©s Visible et FillPortions, lâexpĂ©rience utilisateur se transforme de maniĂšre spectaculaire, passant dâun seul Ă©cran riche sur PC Ă un processus de navigation par âĂ©crans virtuelsâ fluide sur mobile !


đĄ Aviez-vous remarquĂ© ? : Les modĂšles dâĂ©crans rĂ©actifs proposĂ©s nativement intĂšgrent chacun une des 6 techniques dĂ©crites dans la partie 1 de cet article, les voici :
(*) Pour la disposition adaptative de lâĂ©cran âentĂȘte et tableâ, câest surtout liĂ© au que le design adaptatif est directement intĂ©grĂ© Ă lâobjet Table
Microsoft a rĂ©cemment mis Ă jour sa documentation sur ces Ă©crans, nâhĂ©sitez pas Ă la consulter.
Conclusion
Si vous souhaitez vĂ©rifier votre travail et comparer votre Ă©cran avec le modĂšle final, voici le code YAML complet de lâĂ©cran rĂ©alisĂ© tout au long de cette partie. Vous pouvez le copier-coller tel quel dans votre interface Power Apps :
Déployer pour voir le code
Screens:
Mon écran réactif Partie 3:
Children:
- ctn_Main_1:
Control: GroupContainer@1.5.0
Variant: AutoLayout
Properties:
Fill: =RGBA(200,200,200,0.2)
Height: =Parent.Height
LayoutDirection: =LayoutDirection.Vertical
LayoutGap: =8
PaddingBottom: =8
PaddingLeft: =8
PaddingRight: =8
PaddingTop: =8
Width: =Parent.Width
Children:
- ctn_Header_1:
Control: GroupContainer@1.5.0
Variant: AutoLayout
Properties:
DropShadow: =DropShadow.Semilight
Fill: =App.Theme.Colors.Primary
FillPortions: =0
Height: =54
LayoutAlignItems: =LayoutAlignItems.Center
LayoutDirection: =LayoutDirection.Horizontal
LayoutGap: =15
LayoutJustifyContent: =LayoutJustifyContent.Center
LayoutMinHeight: =80
PaddingBottom: =8
PaddingLeft: =15
PaddingRight: =15
PaddingTop: =8
RadiusBottomLeft: =8
RadiusBottomRight: =8
RadiusTopLeft: =8
RadiusTopRight: =8
Children:
- lbl_Title_1:
Control: Label@2.5.1
Properties:
AlignInContainer: =AlignInContainer.Stretch
Color: =RGBA(255, 255, 255, 1)
FillPortions: =1
FontWeight: =FontWeight.Semibold
OnSelect: =Navigate(Screen1)
PaddingLeft: =20
Size: =15
Text: ="Mon Application ""Réactive"""
- img_userPhoto_1:
Control: Image@2.2.3
Properties:
AlignInContainer: =AlignInContainer.Stretch
Height: =80
Image: =User().Image
ImagePosition: =ImagePosition.Fill
LayoutMinHeight: =16
LayoutMinWidth: =16
RadiusBottomLeft: =Self.Height*0.5
RadiusBottomRight: =Self.Height*0.5
RadiusTopLeft: =Self.Height*0.5
RadiusTopRight: =Self.Height*0.5
Width: =Self.Height
- ctn_Body_1:
Control: GroupContainer@1.5.0
Variant: AutoLayout
Properties:
DropShadow: =DropShadow.None
Fill: =Color.Transparent
LayoutDirection: =LayoutDirection.Horizontal
LayoutGap: =8
RadiusBottomLeft: =8
RadiusBottomRight: =8
RadiusTopLeft: =8
RadiusTopRight: =8
Children:
- ctn_Index_1:
Control: GroupContainer@1.5.0
Variant: AutoLayout
Properties:
DropShadow: =DropShadow.Semilight
Fill: =Color.White
FillPortions: =0
LayoutAlignItems: =LayoutAlignItems.Center
LayoutDirection: =LayoutDirection.Vertical
LayoutMaxWidth: =300
LayoutMinHeight: =16
LayoutMinWidth: =60
PaddingBottom: =8
PaddingLeft: =8
PaddingRight: =8
PaddingTop: =8
RadiusBottomLeft: =8
RadiusBottomRight: =8
RadiusTopLeft: =8
RadiusTopRight: =8
Width: =If('Mon écran réactif Partie 3'.Size<2,60,210)
Children:
- tlb_Menu_1:
Control: Toolbar@2.0.5
Properties:
AlignInContainer: =AlignInContainer.Stretch
Alignment: ='Toolbar.Alignment'.Vertical
FillPortions: =1
Items: |-
=Table(
{ItemKey: "new", ItemDisplayName: "Nouveau", ItemIconName: "Add", ItemAppearance: "Primary", ItemIconStyle: "Regular", ItemTooltip: "Add new item"},
{ItemKey: "edit", ItemDisplayName: "Modifier", ItemIconName: "Edit", ItemAppearance: "Subtle", ItemIconStyle: "Regular"},
{ItemKey: "delete", ItemDisplayName: "Supprimer", ItemIconName: "Delete", ItemDisabled: true, ItemAppearance: "Subtle", ItemIconStyle: "Filled"},
{ItemKey: "info", ItemDisplayName: "Informations", ItemIconName: "Info", ItemAppearance: "Subtle", ItemIconStyle: "Regular"})
Layout: |-
=If('Mon écran réactif Partie 3'.Size <2,'Toolbar.Layout'.IconOnly,'Toolbar.Layout'.IconBefore)
//If(App.ActiveScreen.Size <2;'Toolbar.Layout'.IconOnly;'Toolbar.Layout'.IconBefore)
//Switch(App.ActiveScreen.Size ;1;'Toolbar.Layout'.IconOnly;'Toolbar.Layout'.IconBefore)
LayoutMinHeight: =16
LayoutMinWidth: =44
Padding: ='Toolbar.Padding'.Medium
Children:
- ItemAppearance1_1:
Control: ToolbarDataField@1.5.0
Variant: textualColumn
IsLocked: true
Properties:
FieldDisplayName: ="ItemAppearance"
FieldName: ="ItemAppearance"
FieldType: ="s"
Order: =1
- ItemDisabled1_1:
Control: ToolbarDataField@1.5.0
Variant: textualColumn
IsLocked: true
Properties:
FieldDisplayName: ="ItemDisabled"
FieldName: ="ItemDisabled"
FieldType: ="b"
Order: =2
- ItemDisplayName1_1:
Control: ToolbarDataField@1.5.0
Variant: textualColumn
IsLocked: true
Properties:
FieldDisplayName: ="ItemDisplayName"
FieldName: ="ItemDisplayName"
FieldType: ="s"
Order: =3
- ItemIconName1_1:
Control: ToolbarDataField@1.5.0
Variant: textualColumn
IsLocked: true
Properties:
FieldDisplayName: ="ItemIconName"
FieldName: ="ItemIconName"
FieldType: ="s"
Order: =4
- ItemIconStyle1_1:
Control: ToolbarDataField@1.5.0
Variant: textualColumn
IsLocked: true
Properties:
FieldDisplayName: ="ItemIconStyle"
FieldName: ="ItemIconStyle"
FieldType: ="s"
Order: =5
- ItemTooltip1_1:
Control: ToolbarDataField@1.5.0
Variant: textualColumn
IsLocked: true
Properties:
FieldDisplayName: ="ItemTooltip"
FieldName: ="ItemTooltip"
FieldType: ="s"
Order: =6
- ItemKey1_1:
Control: ToolbarDataField@1.5.0
Variant: textualColumn
IsLocked: true
Properties:
FieldDisplayName: ="ItemKey"
FieldName: ="ItemKey"
FieldType: ="s"
Order: =7
- ctn_Content_1:
Control: GroupContainer@1.5.0
Variant: AutoLayout
Properties:
DropShadow: =DropShadow.Semilight
Fill: =Color.White
LayoutDirection: =LayoutDirection.Vertical
LayoutGap: =8
RadiusBottomLeft: =8
RadiusBottomRight: =8
RadiusTopLeft: =8
RadiusTopRight: =8
Children:
- lbl_SousTitre_4:
Control: Label@2.5.1
Properties:
AlignInContainer: =AlignInContainer.Stretch
Color: =App.Theme.Colors.Lighter80
Fill: =App.Theme.Colors.Lighter20
FontWeight: =FontWeight.Semibold
Height: =32
LayoutMinHeight: =16
LayoutMinWidth: =16
Text: ="KPI"
- ctn_kpi_1:
Control: GroupContainer@1.5.0
Variant: AutoLayout
Properties:
DropShadow: =DropShadow.None
FillPortions: =0
Height: =kpi_CA_1.Y+kpi_CA_1.Height+Self.PaddingBottom
LayoutDirection: =LayoutDirection.Horizontal
LayoutGap: =16
LayoutJustifyContent: =LayoutJustifyContent.Center
LayoutMaxHeight: =140
LayoutMinHeight: =60
LayoutMinWidth: =16
LayoutOverflowY: =LayoutOverflow.Scroll
LayoutWrap: =true
PaddingBottom: =8
PaddingLeft: =8
PaddingRight: =8
PaddingTop: =8
RadiusBottomLeft: =0
RadiusBottomRight: =0
RadiusTopLeft: =0
RadiusTopRight: =0
Children:
- kpi_Clients_1:
Control: GroupContainer@1.5.0
Variant: AutoLayout
Properties:
AlignInContainer: =AlignInContainer.SetByContainer
DropShadow: =DropShadow.Semilight
Fill: =RGBA(255, 244, 240, 1)
Height: =40
LayoutAlignItems: =LayoutAlignItems.Center
LayoutDirection: =LayoutDirection.Horizontal
LayoutJustifyContent: =LayoutJustifyContent.Center
LayoutMinHeight: =50
LayoutMinWidth: =210
LayoutWrap: =true
PaddingBottom: =5
PaddingLeft: =5
PaddingRight: =5
PaddingTop: =5
RadiusBottomLeft: =8
RadiusBottomRight: =8
RadiusTopLeft: =8
RadiusTopRight: =8
X: =40
Y: =40
Children:
- img_IconClients_1:
Control: Image@2.2.3
Properties:
AlignInContainer: =AlignInContainer.Center
FillPortions: =1
Height: =30
Image: ="data:image/svg+xml;utf8, " & EncodeUrl("<svg viewBox='0 0 24 24' xmlns='[http://www.w3.org/2000/svg](http://www.w3.org/2000/svg)'><path d='M16 11c1.66 0 2.99-1.34 2.99-3S17.66 5 16 5c-1.66 0-3 1.34-3 3s1.34 3 3 3zm-8 0c1.66 0 2.99-1.34 2.99-3S9.66 5 8 5C6.34 5 5 6.34 5 8s1.34 3 3 3zm0 2c-2.33 0-7 1.17-7 3.5V19h14v-2.5c0-2.33-4.67-3.5-7-3.5zm8 0c-.29 0-.62.02-.97.05 1.16.84 1.97 1.97 1.97 3.45V19h6v-2.5c0-2.33-4.67-3.5-7-3.5z' fill='#D83B01'/></svg>")
LayoutMinHeight: =28
LayoutMinWidth: =28
Width: =40
- lbl_ValueClients_1:
Control: Label@2.5.1
Properties:
Align: =Align.Center
AlignInContainer: =AlignInContainer.Center
Color: =RGBA(216, 59, 1, 1)
FillPortions: =1
FontWeight: =FontWeight.Bold
Height: =30
LayoutMinHeight: =28
LayoutMinWidth: =28
Size: =11
Text: ="34"
- kpi_Ventes_1:
Control: GroupContainer@1.5.0
Variant: AutoLayout
Properties:
AlignInContainer: =AlignInContainer.SetByContainer
DropShadow: =DropShadow.Semilight
Fill: =RGBA(240, 255, 244, 1)
Height: =40
LayoutAlignItems: =LayoutAlignItems.Center
LayoutDirection: =LayoutDirection.Horizontal
LayoutJustifyContent: =LayoutJustifyContent.Center
LayoutMinHeight: =50
LayoutMinWidth: =210
LayoutWrap: =true
PaddingBottom: =5
PaddingLeft: =5
PaddingRight: =5
PaddingTop: =5
RadiusBottomLeft: =8
RadiusBottomRight: =8
RadiusTopLeft: =8
RadiusTopRight: =8
X: =40
Y: =40
Children:
- img_IconVentes_1:
Control: Image@2.2.3
Properties:
AlignInContainer: =AlignInContainer.Start
FillPortions: =1
Height: =30
Image: ="data:image/svg+xml;utf8, " & EncodeUrl("<svg viewBox='0 0 24 24' xmlns='[http://www.w3.org/2000/svg](http://www.w3.org/2000/svg)'><path d='M7 18c-1.1 0-1.99.9-1.99 2S5.9 22 7 22s2-.9 2-2-.9-2-2-2zM1 2v2h2l3.6 7.59-1.35 2.45c-.16.28-.25.61-.25.96 0 1.1.9 2 2 2h12v-2H7.42c-.14 0-.25-.11-.25-.25l.03-.12.9-1.63h7.45c.75 0 1.41-.41 1.75-1.03l3.58-6.49c.08-.14.12-.31.12-.48 0-.55-.45-1-1-1H5.21l-.94-2H1zm16 16c-1.1 0-1.99.9-1.99 2s.89 2 1.99 2 2-.9 2-2-.9-2-2-2z' fill='#107C10'/></svg>")
LayoutMinHeight: =28
LayoutMinWidth: =28
Width: =40
- lbl_ValueVentes_1:
Control: Label@2.5.1
Properties:
Align: =Align.Center
AlignInContainer: =AlignInContainer.Start
Color: =RGBA(16, 124, 16, 1)
FillPortions: =1
FontWeight: =FontWeight.Bold
Height: =30
LayoutMinHeight: =28
LayoutMinWidth: =28
Size: =11
Text: ="128"
- kpi_CA_1:
Control: GroupContainer@1.5.0
Variant: AutoLayout
Properties:
AlignInContainer: =AlignInContainer.SetByContainer
DropShadow: =DropShadow.Semilight
Fill: =RGBA(240, 244, 255, 1)
Height: =40
LayoutAlignItems: =LayoutAlignItems.Center
LayoutDirection: =LayoutDirection.Horizontal
LayoutJustifyContent: =LayoutJustifyContent.Center
LayoutMinHeight: =50
LayoutMinWidth: =210
LayoutWrap: =true
PaddingBottom: =5
PaddingLeft: =5
PaddingRight: =5
PaddingTop: =5
RadiusBottomLeft: =8
RadiusBottomRight: =8
RadiusTopLeft: =8
RadiusTopRight: =8
X: =40
Y: =40
Children:
- img_IconCA_1:
Control: Image@2.2.3
Properties:
AlignInContainer: =AlignInContainer.Start
FillPortions: =1
Height: =30
Image: ="data:image/svg+xml;utf8, " & EncodeUrl("<svg viewBox='0 0 24 24' xmlns='[http://www.w3.org/2000/svg](http://www.w3.org/2000/svg)'><path d='M16 6l2.29 2.29-4.88 4.88-4-4L2 16.59 3.41 18l6-6 4 4 6.3-6.29L22 12V6z' fill='#0078D4'/></svg>")
LayoutMinHeight: =28
LayoutMinWidth: =28
Width: =40
- lbl_ValueCA_1:
Control: Label@2.5.1
Properties:
Align: =Align.Center
AlignInContainer: =AlignInContainer.Start
Color: =App.Theme.Colors.Primary
FillPortions: =1
FontWeight: =FontWeight.Bold
Height: =30
LayoutMinHeight: =28
LayoutMinWidth: =28
Size: =11
Text: ="45.2 KâŹ"
- lbl_SousTitre_3:
Control: Label@2.5.1
Properties:
AlignInContainer: =AlignInContainer.Stretch
Color: =App.Theme.Colors.Lighter80
Fill: =App.Theme.Colors.Lighter20
FontWeight: =FontWeight.Semibold
Height: =32
LayoutMinHeight: =16
LayoutMinWidth: =16
Text: ="Sous-titre 1"
- gal_Cards_1:
Control: Gallery@2.15.0
Variant: Vertical
Properties:
FillPortions: =3
Items: =Table(CustomGallerySample,CustomGallerySample,CustomGallerySample)
LayoutMinHeight: =16
LayoutMinWidth: =16
TemplatePadding: =0
TemplateSize: =Max(If(Self.Layout = Layout.Horizontal, Min(280, Self.Width - 60), Min(280, Self.Height - 60)),90)
WrapCount: |-
=Min(Max(1,Trunc(Self.Width/250,0)),6) // Personnalisable
//'Mon écran réactif Partie 3'.Size // Variante plus simple
Children:
- Card1_1:
Control: ModernCard@1.0.0
Properties:
DropShadow: =DropShadow.Semilight
HeaderImage: =ThisItem.SampleImage
Height: =Parent.TemplateHeight-Self.Y*2
Image: =ThisItem.SampleImage
OnSelect: =Select(Parent)
Subtitle: =ThisItem.SampleText
Title: =ThisItem.SampleHeading
Width: =Parent.TemplateWidth-Self.X*2-5
X: =10
Y: =10
- ctn_Footer_1:
Control: GroupContainer@1.5.0
Variant: AutoLayout
Properties:
DropShadow: =DropShadow.Semilight
Fill: =Color.White
FillPortions: =0
Height: =44
LayoutAlignItems: =LayoutAlignItems.Center
LayoutDirection: =LayoutDirection.Horizontal
LayoutGap: =15
LayoutJustifyContent: =LayoutJustifyContent.SpaceBetween
LayoutMinHeight: =80
PaddingBottom: =8
PaddingLeft: =15
PaddingRight: =15
PaddingTop: =8
RadiusBottomLeft: =8
RadiusBottomRight: =8
RadiusTopLeft: =8
RadiusTopRight: =8
Children:
- HtmlText1_1:
Control: HtmlViewer@2.1.0
Properties:
AlignInContainer: =AlignInContainer.Stretch
FillPortions: =1
HtmlText: |-
="<marquee direction='left' scrollamount='3' behavior='scroll' style='font-size:16px; background:#0000; padding:0px;'>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor. Cras elementum ultrices diam. Maecenas ligula massa, varius a, semper congue, euismod non, mi. Proin porttitor orci nec nonummy molestie. Enim est eleifend mi, non fermentum diam nisl sit amet erat. Duis semper, dui et placerat cursus, nibh nisl feugiat velit, at dignissim sapien orci vel sem. Pellentesque congue efficitur nunc, in porta nisl.
</marquee>"
LayoutMinHeight: =16
LayoutMinWidth: =16
PaddingBottom: =0
PaddingLeft: =0
PaddingRight: =0
PaddingTop: =0

Votre application possÚde désormais une structure robuste et un contenu adaptatif.
En combinant les propriĂ©tĂ©s Screen.Size, la fonction dâenveloppement des conteneurs horizontaux et les calculs sur la propriĂ©tĂ© WrapCount des galeries, les thĂ©ories du Responsive Design sont appliquĂ©es sans recourir Ă des coordonnĂ©es fixes. Lâinterface sâajuste et se repositionne selon le support matĂ©riel utilisĂ© par lâutilisateur.
Et aprĂšs ?
Pour la suite, nous aborderons le cas particulier des formulaires, nous verrons que les formulaires natifs disposent de prĂ©cieux paramĂštres pour sâadapter dynamiquement Ă la rĂ©solution de lâĂ©cran. NâhĂ©sitez pas Ă vous abonner Ă la Newsletter pour recevoir directement les prochains articles !



Ăchanger autour de cet article
Les commentaires utilisent Giscus et GitHub Discussions. Le service externe nâest contactĂ© quâaprĂšs activation.