Flutter Layouts and Widgets
Flutter Layouts and Widgets
Section titled “Flutter Layouts and Widgets”Introduction
Section titled “Introduction”Flutter propose plusieurs widgets puissants pour créer des mises en page complexes et réactives. Ce guide couvre les concepts essentiels des layouts Flutter, des widgets de base aux principes avancés de contraintes et de positionnement.
Row, Column et Wrap
Section titled “Row, Column et Wrap”Row et Column
Section titled “Row et Column”Un grand nombre de mises en pages peuvent être construites à l’aide des Row et Column, un peu à la manière des display:flex en CSS.
Ces deux classes permettent d’aligner leur contenu à l’aide des propriétés :
mainAxisAlignment- et
crossAxisAlignment
Il est également possible de paramètrer la taille occupée par la ligne/column à l’aide de mainAxisSize.
Row( mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ Column( crossAxisAlignment: CrossAxisAlignment.center, children: const [ Icon(Icons.add), Text('Ajouter'), ], ), Column( crossAxisAlignment: CrossAxisAlignment.center, children: const [ Icon(Icons.edit), Text('Modifier'), ], ), Column( crossAxisAlignment: CrossAxisAlignment.center, children: const [ Icon(Icons.remove), Text('Supprimer'), ], ), ],)Le widget Wrap permet de disposer des éléments dans une direction comme les Row et les Columns, mais sur plusieurs “lignes”. Lorsqu’un élément ne peut être ajouté à la ligne courante, il passe “à la ligne”.
Scaffold( backgroundColor: Colors.blueGrey[200], appBar: AppBar(), body: SizedBox.expand( child: Container( color: Colors.yellow, child: Wrap( crossAxisAlignment: WrapCrossAlignment.center, /*runSpacing: 30,*/ spacing: 30, runAlignment: WrapAlignment.spaceEvenly, /*direction: Axis.vertical,*/ verticalDirection: VerticalDirection.up, children: List.generate(20, (index) => index) .map( (e) => Container( padding: const EdgeInsets.all(12), color: Colors.cyan, child: Text('Item $e')), ) .toList(), ), ), ),);Exemple avancé de Wrap
Section titled “Exemple avancé de Wrap”Wrap( alignment: WrapAlignment.end, runAlignment: WrapAlignment.center, crossAxisAlignment: WrapCrossAlignment.end, direction: Axis.horizontal, children: List.generate( 12, (index) => Container( height: 60 + (5.0 * index), margin: const EdgeInsets.all(8), padding: const EdgeInsets.all(12), color: Colors.grey.shade300, child: Text('item ${index + 1}'), ), ),)Flex (Expanded, Flexible, Spacer)
Section titled “Flex (Expanded, Flexible, Spacer)”Il est possible d’étendre l’espace accordé à un élément de Row ou de Column.
Expanded
Section titled “Expanded”Expanded : s’étend “autant que faire se peut”
Row( children: [ Expanded( child: Container( color: Colors.red, child: Text('Expanded 1'), ), ), Expanded( child: Container( color: Colors.blue, child: Text('Expanded 2'), ), ), ],)Flexible
Section titled “Flexible”Flexible : permet de définir un coefficient de flexibilité flex
Row( children: [ Flexible( flex: 1, child: Container( color: Colors.red, child: Text('Flexible 1'), ), ), Flexible( flex: 2, child: Container( color: Colors.blue, child: Text('Flexible 2'), ), ), ],)Spacer
Section titled “Spacer”Spacer : permet d’inclure un espacement, qui occupera la totalité de l’espace disponible, ou un coefficient de flexibilité flex
Row( children: [ Text('Début'), Spacer(), Text('Fin'), ],)Stack et positionnement
Section titled “Stack et positionnement”Stack crée une pile de widgets, positionnés via Positioned ou Align.
Exemple simple
Section titled “Exemple simple”SizedBox( width: 64, height: 64, child: Stack( children: const [ Positioned( top: 2, child: Icon( Icons.account_circle_rounded, size: 54, color: Colors.orange, ), ), Positioned( right: 4, child: Icon( Icons.add_circle_outlined, color: Colors.green, size: 24, ), ), ], ),)Exemple avancé avec overlay
Section titled “Exemple avancé avec overlay”Stack( children: [ Image.network( 'https://fr.web.img4.acsta.net/r_654_368/newsv7/21/01/11/16/11/2598562.jpg', ), Positioned( bottom: 0, left: 0, right: 0, child: Container( height: 74, padding: const EdgeInsets.all(8), color: Colors.black54, child: RichText( text: const TextSpan(children: [ TextSpan( text: 'Spiderman\n', style: TextStyle(color: Colors.white, fontSize: 24), ), TextSpan( text: '2007', style: TextStyle(color: Colors.grey, fontSize: 14), ), ]), ), ), ), const Positioned( right: 8, top: 8, child: Icon(Icons.favorite, color: Colors.red, size: 42)) ],)Positionnement animé
Section titled “Positionnement animé”Flutter propose des widgets animés pour le positionnement :
AnimatedPositioned( right: position, top: 8, duration: const Duration(seconds: 1), child: IconButton( icon: Icon(Icons.favorite, color: Colors.red, size: 42), onPressed: () => setState(() => position = -20), ),)Taille et contraintes
Section titled “Taille et contraintes”Principe fondamental
Section titled “Principe fondamental”“Constraints go down, Size go up and parents sets position”
Une contrainte permet au parent d’un widget, d’exprimer une taille minimale et une taille maximale disponible pour le widget. Le widget enfant détermine ensuite sa propre taille.
Types de contraintes
Section titled “Types de contraintes”tight: assigner une taille fixeloosen: définit uniquement une taille maximale
Documentation officielle sur les contraintes
Gestion des différentes tailles d’écrans
Section titled “Gestion des différentes tailles d’écrans”Flutter propose plusieurs manières de gérer les différentes tailles d’écrans :
MediaQuery
Section titled “MediaQuery”MediaQuery.of(context).size/MediaQuery.sizeOf(context)MediaQuery.of(context).orientation/MediaQuery.orientationOf(context)
final screenSize = MediaQuery.of(context).size;final orientation = MediaQuery.of(context).orientation;
Container( width: screenSize.width * 0.8, height: orientation == Orientation.portrait ? 200 : 100, child: Text('Responsive Container'),)LayoutBuilder
Section titled “LayoutBuilder”LayoutBuilder permet de construire des widgets en fonction des contraintes disponibles.
LayoutBuilder( builder: (context, constraints) { if (constraints.maxWidth > 600) { return Row(children: widgets); } else { return Column(children: widgets); } },)Layout principles et best practices
Section titled “Layout principles et best practices”1. Composition vs. Container
Section titled “1. Composition vs. Container”Flutter favorise la composition de widgets simples plutôt que l’utilisation de widgets complexes comme Container.
// ✅ Recommandé : CompositionPadding( padding: EdgeInsets.all(8.0), child: DecoratedBox( decoration: BoxDecoration( color: Colors.blue, borderRadius: BorderRadius.circular(8), ), child: Text('Hello'), ),)
// ⚠️ Acceptable mais moins flexibleContainer( padding: EdgeInsets.all(8.0), decoration: BoxDecoration( color: Colors.blue, borderRadius: BorderRadius.circular(8), ), child: Text('Hello'),)2. Widgets de composition utiles
Section titled “2. Widgets de composition utiles”Padding: exprimé à l’aide deEdgeInsets
Column( crossAxisAlignment: CrossAxisAlignment.center, children: const [ Padding( padding: EdgeInsets.all(8.0), child: Icon(Icons.add), ), Text('Ajouter'), ],)Center: centre son enfantSizedBox: définit une taille fixe ou crée un espacementAspectRatio: maintient un ratio largeur/hauteur
3. Container - Widget multi-usage
Section titled “3. Container - Widget multi-usage”Le widget Container a la particularité d’aller à l’encontre du principe de composition. En effet, ce widget intègre la logique de plusieurs autres widgets, et permet de déclarer :
- un padding
- une marge
- un alignment
- une taille : la largeur
widthet la hauteurheight - une couleur
color - une decoration de type
BoxDecoration, pouvant définir :- un borderRadius
- un contour
- une contrainte
constraints
Container( width: 300, height: 300, decoration: BoxDecoration( borderRadius: BorderRadius.circular(8), border: Border.all(color: Colors.red, width: 3), boxShadow: const [BoxShadow(blurRadius: 8, spreadRadius: 2)], gradient: LinearGradient(colors: [Colors.cyan, Colors.blueGrey]), ), alignment: Alignment.center, child: Text( 'Hello, World!', style: Theme.of(context).textTheme.headline4, ),);4. Card - Material Design
Section titled “4. Card - Material Design”Card est un container Material ombré et légèrement arrondi :
Card( elevation: 5, color: Colors.grey.shade300, child: Padding( padding: const EdgeInsets.all(8.0), child: Column( mainAxisSize: MainAxisSize.min, children: [ Padding( padding: const EdgeInsets.all(8.0), child: Text('Settings : $category'), ), ElevatedButton( onPressed: context.pop, child: const Text('Fermer'), ) ], ), ),)5. Bonnes pratiques
Section titled “5. Bonnes pratiques”- Utilisez la composition : Préférez composer plusieurs widgets simples plutôt qu’utiliser un widget complexe
- Optimisez les performances : Utilisez
constconstructors quand possible - Gérez les contraintes : Comprenez comment les contraintes se propagent dans l’arbre de widgets
- Testez sur différentes tailles : Utilisez des outils comme
MediaQueryetLayoutBuilderpour créer des layouts responsives - Évitez les overflows : Utilisez
Flexible,Expanded, ouWrappour gérer le contenu dynamique