CHAPITRE 2.4

Organisation de l'interface

Organisez et structurez vos widgets pour créer des interfaces riches
Maintenant que vous savez créer une page structurée avec Scaffold et AppBar, il est temps d'apprendre à organiser plusieurs widgets ensemble. Dans ce chapitre, nous découvrirons des widgets essentiels pour créer des interfaces plus riches : Container pour styliser, Column et Row pour organiser, et Card pour présenter du contenu. Chaque widget sera introduit progressivement, avec du code minimal et clair.

2.4Organisation de l'interface

2.4.1 – Découvrir Container

Jusqu'à présent, nous avons affiché du texte simple. Pour créer des interfaces plus attrayantes, nous avons besoin de widgets qui permettent de styliser et d'organiser le contenu. Le widget Container est l'un des plus utiles pour cela.

🎨 Pourquoi utiliser un Container ?

Container est un widget polyvalent qui permet de :

  • Définir une taille (largeur, hauteur)
  • Ajouter des couleurs de fond
  • Ajouter des marges et du padding (espacement)
  • Centrer ou aligner son contenu
Analogie :
Container dans Flutter ressemble beaucoup à une balise <div> en HTML : c'est une boîte polyvalente dans laquelle on peut placer du contenu, choisir sa taille, sa couleur, ajouter des marges (autour), du padding (à l'intérieur), etc. Cela permet de structurer et styliser visuellement tout ce que l’on veut, comme on le ferait avec un <div> sur une page web.

📏 Taille, couleur et marges simples

Commençons par les propriétés les plus simples de Container :

Container(
  width: 200,
  height: 100,
  color: Colors.blue,
  child: Text('Bonjour'),
)

Analysons ce code :

  • width: 200 : Définit la largeur du container (200 pixels)
  • height: 100 : Définit la hauteur du container (100 pixels)
  • color: Colors.blue : Définit la couleur de fond (bleu)
  • child: Text('Bonjour') : Le contenu du container (un texte)
Colors.blue :
Colors est une classe fournie par Flutter qui contient des couleurs prédéfinies. Colors.blue est le bleu standard. Il existe beaucoup d'autres couleurs : Colors.red, Colors.green, Colors.yellow, etc.
À savoir sur les couleurs :
Vous pouvez utiliser une couleur prédéfinie simplement comme Colors.blue.
Mais vous pouvez aussi utiliser une nuance précise avec la syntaxe Colors.nom[index], par exemple :
  • Colors.blue[100] : bleu très clair
  • Colors.blue[700] : bleu très foncé
  • Plus l’index (par exemple 100, 200, ..., 900) est élevé, plus la couleur est foncée.
Cela vous permet d’affiner la teinte exacte utilisée dans votre interface.
Exemple : Container(color: Colors.red[200]) affichera un rouge pâle.

📦 Container + child

Comme beaucoup de widgets Flutter, Container utilise le paramètre child pour définir son contenu. Voici un exemple complet dans une application :

Schéma du widget Container de Flutter
Illustration du widget Container en Flutter : largeur, hauteur, couleur et enfant.
import 'package:flutter/material.dart';

void main() {
  runApp(const MaPage());
}

class MaPage extends StatelessWidget {
  const MaPage({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Mon Application'),
        ),
        body: SafeArea(
          child: Center(
            child: Container(
              width: 200,
              height: 100,
              color: Colors.blue,
              child: Center(
                child: Text('Bonjour Flutter !'),
              ),
            ),
          ),
        ),
      ),
    );
  }
}

Dans cet exemple :

  • Nous avons un Container bleu de 200 pixels de large et 100 pixels de haut
  • À l'intérieur, il y a un Center qui centre le texte
  • Le texte "Bonjour Flutter !" est affiché au centre du container bleu
Important :
Pour centrer le texte dans un Container, nous devons utiliser un widget Center à l'intérieur. Le Container lui-même ne centre pas automatiquement son contenu.

🎨 Ajouter des marges

Les marges créent de l'espace autour du container. Utilisez le paramètre margin :

Container(
  width: 200,
  height: 100,
  color: Colors.blue,
  margin: EdgeInsets.all(20),
  padding: EdgeInsets.only(left: 10, top: 20),
  child: Center(
    child: Text('Bonjour'),
  ),
)

EdgeInsets.all(20) ajoute 20 pixels de marge de tous les côtés (haut, bas, gauche, droite).

EdgeInsets :
EdgeInsets est une classe Flutter pour définir les espacements. EdgeInsets.all(20) ajoute 20 pixels partout. Vous pouvez aussi utiliser EdgeInsets.only(left: 10, top: 20) pour définir des marges ou des paddings spécifiques.

💡 Points clés à retenir

  • Container permet de styliser et organiser le contenu
  • width et height définissent la taille
  • color définit la couleur de fond
  • margin ajoute de l'espace autour du container
  • child définit le contenu du container
Prochaine étape :
Dans la section suivante, nous allons découvrir Column, un widget qui permet d'organiser plusieurs widgets verticalement (les uns en dessous des autres).

2.4.2 – Organiser en colonne avec Column

Jusqu'à présent, nous avons affiché un seul widget à la fois. Pour créer des interfaces plus riches, nous devons pouvoir afficher plusieurs widgets ensemble. Le widget Column permet d'organiser plusieurs widgets verticalement (les uns en dessous des autres).

📐 Disposition verticale

Column organise ses enfants de haut en bas, en colonne. C'est parfait pour créer des listes verticales d'éléments.

Analogie :
Imaginez une pile de livres. Les livres sont empilés verticalement, un au-dessus de l'autre. Column fonctionne de la même manière : il empile les widgets verticalement.

📋 children : []

Contrairement aux widgets que nous avons vus jusqu'à présent (qui utilisent child pour un seul enfant), Column utilise children (au pluriel) pour accepter plusieurs widgets. C'est une liste de widgets.

Schéma : fonctionnement du widget Column en Flutter
Schéma : Le widget Column empile ses enfants verticalement, comme une pile de blocs.

Voici la structure de base :

Column(
  children: [
    Text('Premier élément'),
    Text('Deuxième élément'),
    Text('Troisième élément'),
  ],
)

Dans cet exemple, children: [...] contient une liste de trois widgets Text(). Ils seront affichés les uns en dessous des autres.

La syntaxe des listes :
En Dart, [ ] crée une liste. Les éléments sont séparés par des virgules. Ici, nous créons une liste de widgets. C'est la première fois que nous utilisons une liste, mais c'est très simple : c'est juste une collection d'éléments entre crochets.

🧪 Exemple complet avec Column

Voici un exemple complet dans une application :

Illustration du widget Column en Flutter (Chapitre 2.4.2)
Figure : Exemple visuel d'une Column qui aligne trois widgets Text verticalement.
import 'package:flutter/material.dart';

void main() {
  runApp(const MaPage());
}

class MaPage extends StatelessWidget {
  const MaPage({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Mon Application'),
        ),
        body: SafeArea(
          child: Center(
            child: Column(
              children: [
                Text('Premier élément'),
                Text('Deuxième élément'),
                Text('Troisième élément'),
              ],
            ),
          ),
        ),
      ),
    );
  }
}

Dans cet exemple, les trois textes seront affichés verticalement, centrés à l'écran.

🎯 Alignement main et cross

Column permet de contrôler l'alignement de ses enfants. Il y a deux axes :

  • Axe principal (main axis) : L'axe vertical (haut-bas). C'est l'axe le long duquel les éléments sont empilés.
  • Axe secondaire (cross axis) : L'axe horizontal (gauche-droite). C'est l'axe perpendiculaire à l'empilement.

Pour aligner les enfants, utilisez les paramètres mainAxisAlignment et crossAxisAlignment :

Column(
  spacing: 10,
  mainAxisAlignment: MainAxisAlignment.center,
  crossAxisAlignment: CrossAxisAlignment.center,
  children: [
    Text('Premier'),
    Text('Deuxième'),
    Text('Troisième'),
  ],
)

Analysons ces paramètres :

  • mainAxisAlignment: MainAxisAlignment.center : Centre les éléments verticalement (le long de l'axe principal)
  • crossAxisAlignment: CrossAxisAlignment.center : Centre les éléments horizontalement (le long de l'axe secondaire)
  • spacing: 10 : Ajoute un espace de 10 pixels entre les éléments
Schéma des axes main et cross de Column
Illustration des axes principal (main axis) et secondaire (cross axis) dans un Column.
Valeurs possibles pour mainAxisAlignment :
  • MainAxisAlignment.start : Aligne en haut (par défaut)
  • MainAxisAlignment.center : Centre verticalement
  • MainAxisAlignment.end : Aligne en bas
  • MainAxisAlignment.spaceBetween : Répartit l'espace entre les éléments
  • MainAxisAlignment.spaceAround : Répartit l'espace autour des éléments
Conseil :
Pour l'instant, utilisez simplement mainAxisAlignment: MainAxisAlignment.center et crossAxisAlignment: CrossAxisAlignment.center pour centrer vos éléments. Nous explorerons les autres options plus tard.

💡 Points clés à retenir

  • Column organise les widgets verticalement (de haut en bas)
  • Il utilise children: [] pour accepter plusieurs widgets (une liste)
  • mainAxisAlignment contrôle l'alignement vertical
  • crossAxisAlignment contrôle l'alignement horizontal
  • Les éléments sont séparés par des virgules dans la liste
Prochaine étape :
Dans la section suivante, nous allons découvrir Row, qui fonctionne comme Column mais organise les widgets horizontalement (de gauche à droite).

2.4.3 – Organiser en ligne avec Row

Dans la section précédente, nous avons découvert Column qui organise les widgets verticalement. Maintenant, découvrons Row, qui fonctionne de la même manière mais organise les widgets horizontalement (de gauche à droite).

➡️ Disposition horizontale

Row organise ses enfants de gauche à droite, en ligne. C'est parfait pour créer des barres horizontales d'éléments.

Diagramme illustrant la disposition d'une Row en Flutter (widgets côte à côte)
Schéma : Les widgets enfants d'une Row sont placés horizontalement, de gauche à droite.
Analogie :
Imaginez des mots sur une ligne de texte. Les mots sont placés horizontalement, un à côté de l'autre. Row fonctionne de la même manière : il place les widgets côte à côte horizontalement.

📋 children : []

Comme Column, Row utilise children (au pluriel) pour accepter plusieurs widgets :

Row(
  children: [
    Text('Premier'),
    Text('Deuxième'),
    Text('Troisième'),
  ],
)

Dans cet exemple, les trois textes seront affichés horizontalement, côte à côte.

🧪 Exemple complet avec Row

Voici un exemple complet dans une application :

Exemple visuel de Row en Flutter (widgets disposés horizontalement)
Illustration : Utilisation de Row pour placer plusieurs widgets horizontalement.
import 'package:flutter/material.dart';

void main() {
  runApp(const MaPage());
}

class MaPage extends StatelessWidget {
  const MaPage({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Mon Application'),
        ),
        body: SafeArea(
          child: Center(
            child: Row(
              children: [
                Text('Premier'),
                Text('Deuxième'),
                Text('Troisième'),
              ],
            ),
          ),
        ),
      ),
    );
  }
}

Dans cet exemple, les trois textes seront affichés horizontalement, centrés à l'écran.

🎯 Alignements de base

Comme Column, Row permet de contrôler l'alignement. Mais attention : pour Row, les axes sont inversés !

  • Axe principal (main axis) : L'axe horizontal (gauche-droite). C'est l'axe le long duquel les éléments sont alignés.
  • Axe secondaire (cross axis) : L'axe vertical (haut-bas). C'est l'axe perpendiculaire à l'alignement.
Row(
  spacing: 10,
  mainAxisAlignment: MainAxisAlignment.center,
  crossAxisAlignment: CrossAxisAlignment.center,
  children: [
    Text('Premier'),
    Text('Deuxième'),
    Text('Troisième'),
  ],
)

Pour Row :

  • mainAxisAlignment: MainAxisAlignment.center : Centre les éléments horizontalement (le long de l'axe principal)
  • crossAxisAlignment: CrossAxisAlignment.center : Centre les éléments verticalement (le long de l'axe secondaire)
  • spacing: 10 : Ajoute un espace de 10 pixels entre les éléments
Différence entre Column et Row :
  • Column : Axe principal = vertical (haut-bas), Axe secondaire = horizontal (gauche-droite)
  • Row : Axe principal = horizontal (gauche-droite), Axe secondaire = vertical (haut-bas)
C'est l'inverse ! Mais les paramètres mainAxisAlignment et crossAxisAlignment fonctionnent de la même manière.
Attention aux débordements :
Si vous mettez trop d'éléments dans une Row, ils peuvent dépasser de l'écran (débordement). Flutter affichera une erreur. Nous verrons comment gérer cela dans la prochaine section.

💡 Points clés à retenir

  • Row organise les widgets horizontalement (de gauche à droite)
  • Il utilise children: [] pour accepter plusieurs widgets
  • Pour Row, l'axe principal est horizontal (contrairement à Column)
  • mainAxisAlignment contrôle l'alignement horizontal
  • crossAxisAlignment contrôle l'alignement vertical
Prochaine étape :
Dans la section suivante, nous allons apprendre à combiner Row et Column pour créer des interfaces plus complexes et structurées.

2.4.4 – Combiner Row et Column

Maintenant que vous connaissez Column et Row, vous pouvez les combiner pour créer des interfaces plus complexes. C'est une technique très puissante en Flutter.

🏗️ Construire des interfaces plus riches

En combinant Row et Column, vous pouvez créer des grilles, des tableaux, et des layouts complexes. Par exemple, vous pouvez avoir une Column qui contient plusieurs Row, ou une Row qui contient plusieurs Column.

Exemple concret :
Imaginez une carte de contact avec un nom, un email et un téléphone. Vous pourriez avoir :
  • Une Column principale qui contient toutes les informations
  • Chaque ligne d'information pourrait être une Row avec une icône à gauche et le texte à droite
C'est exactement ce que permet la combinaison de Row et Column.

🧪 Exemple : Column contenant des Row

Voici un exemple simple où une Column contient plusieurs Row :

import 'package:flutter/material.dart';

void main() {
  runApp(const MaPage());
}

class MaPage extends StatelessWidget {
  const MaPage({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Mon Application'),
        ),
        body: SafeArea(
          child: Center(
            child: Column(
              children: [
                Row(
                  children: [
                    Text('Ligne 1 - Élément 1'),
                    Text('Ligne 1 - Élément 2'),
                  ],
                ),
                Row(
                  children: [
                    Text('Ligne 2 - Élément 1'),
                    Text('Ligne 2 - Élément 2'),
                  ],
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }
}

Dans cet exemple :

  • La Column principale organise deux Row verticalement
  • Chaque Row contient deux textes côte à côte
  • Le résultat est une grille simple de 2 lignes et 2 colonnes

⚠️ Attention aux débordements

Quand vous utilisez Row ou Column, il faut faire attention aux débordements. Si vous mettez trop d'éléments ou des éléments trop larges, ils peuvent dépasser de l'écran.

Erreur de débordement :
Si vous voyez une erreur comme "RenderFlex overflowed", cela signifie qu'un Row ou Column a trop de contenu pour tenir dans l'espace disponible. Pour l'instant, utilisez peu d'éléments ou des éléments courts pour éviter ce problème.
Solution simple :
Pour éviter les débordements, vous pouvez :
  • Utiliser moins d'éléments dans vos Row ou Column
  • Utiliser des textes plus courts
  • Envelopper votre Row ou Column dans un SingleChildScrollView (nous verrons cela plus tard)

📏 Utiliser Expanded() pour répartir l'espace

Le widget Expanded est une solution élégante pour éviter les débordements et répartir l'espace disponible dans une Row ou une Column. Il permet à un widget enfant de prendre tout l'espace disponible qui reste après que les autres enfants aient pris leur place.

Analogie :
Imaginez une table avec plusieurs personnes. Si certaines personnes prennent une place fixe, Expanded permet aux autres de se partager équitablement l'espace restant. C'est comme un "étirement" intelligent qui s'adapte à l'espace disponible.

Comment fonctionne Expanded ?

Expanded enveloppe un widget et lui permet de prendre tout l'espace disponible le long de l'axe principal (horizontal pour Row, vertical pour Column).

Voici un exemple avec une Row :

Row(
  children: [
    Container(
      width: 100,
      height: 50,
      color: Colors.red,
    ),
    Expanded(
      child: Container(
        height: 50,
        color: Colors.blue,
      ),
    ),
    Container(
      width: 100,
      height: 50,
      color: Colors.green,
    ),
  ],
)

Dans cet exemple :

  • Le premier Container rouge a une largeur fixe de 100 pixels
  • Le Container bleu est enveloppé dans Expanded : il prendra tout l'espace horizontal restant
  • Le dernier Container vert a aussi une largeur fixe de 100 pixels
Important :
Expanded ne peut être utilisé que comme enfant direct d'une Row, Column, ou Flex (un widget parent de Row et Column). Il ne fonctionne pas dans d'autres contextes.

Exemple complet avec Expanded

Voici un exemple complet qui montre une structure classique avec des zones Top, Left, Center, Right et Bottom. Cet exemple illustre l'utilisation de Expanded à la fois dans une Column et dans une Row :

Structure visuelle utilisant Column, Row, et Expanded dans Flutter
Structure d’interface avec Column, Row et Expanded : la zone centrale occupe tout l’espace restant, encadrée par des zones latérales fixes.
import 'package:flutter/material.dart';

void main() {
  runApp(const MaPage());
}

class MaPage extends StatelessWidget {
  const MaPage({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        appBar: AppBar(title: const Text('Structure Top - Left - Right - Bottom - Center')),
        body: SafeArea(
          child: Column(
            children: [
              // 🔵 TOP
              Container(
                color: Colors.blue[100],
                padding: const EdgeInsets.all(16),
                width: double.infinity,
                child: const Text("ZONE TOP", textAlign: TextAlign.center),
              ),

              // 🔵 CENTER avec Left / Center / Right
              Expanded(
                child: Row(
                  children: [
                    // 🔹 LEFT
                    Container(
                      width: 80,
                      color: Colors.green[100],
                      child: const Center(child: Text("LEFT")),
                    ),

                    // 🔹 CENTER
                    Expanded(
                      child: Container(
                        color: Colors.grey[200],
                        child: const Center(child: Text("CENTER")),
                      ),
                    ),

                    // 🔹 RIGHT
                    Container(
                      width: 80,
                      color: Colors.red[100],
                      child: const Center(child: Text("RIGHT")),
                    ),
                  ],
                ),
              ),

              // 🔵 BOTTOM
              Container(
                color: Colors.yellow[200],
                padding: const EdgeInsets.all(16),
                width: double.infinity,
                child: const Text("ZONE BOTTOM", textAlign: TextAlign.center),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

Analysons la structure de cet exemple :

  • ZONE TOP : Un Container bleu en haut avec width: double.infinity pour prendre toute la largeur disponible
  • ZONE CENTER : Un Expanded qui contient une Row avec trois zones :
    • LEFT : Container vert de 80 pixels de largeur fixe
    • CENTER : Container gris enveloppé dans Expanded qui prend tout l'espace horizontal restant
    • RIGHT : Container rouge de 80 pixels de largeur fixe
  • ZONE BOTTOM : Un Container jaune en bas avec width: double.infinity pour prendre toute la largeur
Explications des nouveaux éléments :
  • double.infinity : Une valeur spéciale qui signifie "toute la largeur disponible". C'est utile pour faire prendre toute la largeur à un widget dans une Column.
  • textAlign: TextAlign.center : Centre le texte horizontalement dans son widget parent.
  • Colors.blue[100] : Utilise une nuance claire de bleu (comme expliqué dans la section sur Container).
  • padding: EdgeInsets.all(16) : Ajoute 16 pixels d'espace à l'intérieur du container.
Double utilisation de Expanded :
Dans cet exemple, Expanded est utilisé deux fois :
  • Dans la Column : Le Expanded qui enveloppe la Row permet à la zone centrale de prendre tout l'espace vertical disponible entre TOP et BOTTOM.
  • Dans la Row : Le Expanded qui enveloppe le container CENTER permet à cette zone de prendre tout l'espace horizontal disponible entre LEFT et RIGHT.
C'est une technique très courante pour créer des layouts flexibles qui s'adaptent à la taille de l'écran.
À retenir :
  • Expanded permet à un widget de prendre l'espace disponible
  • Il fonctionne uniquement dans Row ou Column
  • Il évite les débordements en s'adaptant à l'espace disponible
  • Plusieurs Expanded se partagent équitablement l'espace restant

📊 Utiliser flex pour contrôler la répartition de l'espace

Par défaut, tous les widgets Expanded dans une Row ou Column se partagent équitablement l'espace disponible. Mais vous pouvez contrôler cette répartition en utilisant le paramètre flex.

Qu'est-ce que flex ?
Le paramètre flex définit la proportion d'espace que chaque Expanded doit recevoir. Plus la valeur de flex est grande, plus le widget prendra d'espace.

Voici un exemple avec deux Expanded ayant des valeurs de flex différentes :

Row(
  children: [
    Expanded(
      flex: 2,
      child: Container(color: Colors.red),
    ),
    Expanded(
      flex: 1,
      child: Container(color: Colors.blue),
    ),
  ],
)

Analysons ce code :

  • Expanded(flex: 2, ...) : Le container rouge prendra 2 parts de l'espace disponible
  • Expanded(flex: 1, ...) : Le container bleu prendra 1 part de l'espace disponible
  • Résultat : Le rouge prendra 2/3 de l'espace (2 parts sur 3 au total) et le bleu prendra 1/3 (1 part sur 3)
Comment calculer les proportions ?
Pour calculer la proportion d'espace que chaque widget reçoit :
  • Additionnez toutes les valeurs de flex : ici 2 + 1 = 3
  • Divisez chaque valeur de flex par cette somme
  • Rouge : 2 ÷ 3 = 66,7% de l'espace
  • Bleu : 1 ÷ 3 = 33,3% de l'espace

🧪 Exemple complet : Expanded avec flex

Voici un exemple complet qui montre l'utilisation de flex :

Exemple Expanded avec flex dans Flutter
Deux Expanded dans une Row avec des valeurs de flex différentes : rouge (2 parts), bleu (1 part).
import 'package:flutter/material.dart';

void main() {
  runApp(const MaPage());
}

class MaPage extends StatelessWidget {
  const MaPage({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Expanded avec flex'),
        ),
        body: SafeArea(
          child: Row(
            children: [
              Expanded(
                flex: 2,
                child: Container(
                  color: Colors.red,
                  child: const Center(
                    child: Text(
                      'flex: 2\n(66,7%)',
                      style: TextStyle(color: Colors.white, fontSize: 18),
                    ),
                  ),
                ),
              ),
              Expanded(
                flex: 1,
                child: Container(
                  color: Colors.blue,
                  child: const Center(
                    child: Text(
                      'flex: 1\n(33,3%)',
                      style: TextStyle(color: Colors.white, fontSize: 18),
                    ),
                  ),
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

Dans cet exemple, le container rouge prend deux fois plus d'espace que le container bleu. Si vous changez les valeurs de flex, les proportions changeront automatiquement.

Valeurs de flex courantes :
Voici quelques exemples de proportions courantes :
  • flex: 1 et flex: 1 → 50% / 50% (égal)
  • flex: 2 et flex: 1 → 66,7% / 33,3% (2:1)
  • flex: 3 et flex: 1 → 75% / 25% (3:1)
  • flex: 1, flex: 1, flex: 1 → 33,3% / 33,3% / 33,3% (égal pour 3)
Important :
  • Si vous ne spécifiez pas flex, la valeur par défaut est 1
  • Les valeurs de flex doivent être des nombres entiers positifs (1, 2, 3, etc.)
  • Plusieurs Expanded sans flex se partagent équitablement l'espace (comme si tous avaient flex: 1)

2.4.5 – Présenter du contenu avec Card

Pour finir ce chapitre, découvrons Card, un widget qui permet de présenter du contenu de manière élégante. Les cartes sont très utilisées dans les applications modernes pour organiser l'information.

🎴 Pourquoi Card ?

Card est un widget qui affiche son contenu dans une carte avec :

  • Un fond légèrement surélevé (effet d'ombre)
  • Des coins arrondis
  • Un espacement interne (padding) automatique
  • Une apparence professionnelle et moderne
Exemple visuel d'une Card Flutter
Exemple visuel : une Card mettant en valeur du contenu.
Où avez-vous vu des Card ?
Les cartes sont partout dans les applications modernes. Par exemple, dans une application de météo, chaque prévision est souvent affichée dans une carte. Dans une application de nouvelles, chaque article est dans une carte. C'est un moyen élégant d'organiser l'information.

📝 Exemple minimal : Card + Padding + Text

Voici un exemple simple avec Card et Text :

Card(
  child: Text('Contenu de la carte'),
)

Pour améliorer l'apparence, nous pouvons ajouter du padding (espacement interne) avec le widget Padding :

Card(
  child: Padding(
    padding: EdgeInsets.all(16),
    child: Text('Contenu de la carte'),
  ),
)

Analysons ce code :

  • Card crée la carte avec son style par défaut
  • Padding ajoute de l'espace autour du texte
  • padding: EdgeInsets.all(16) ajoute 16 pixels d'espace de tous les côtés
  • Text affiche le contenu
Padding vs margin :
  • Padding : Espace à l'intérieur d'un widget (entre le bord et le contenu)
  • Margin : Espace à l'extérieur d'un widget (entre le widget et les autres éléments)
Pour une Card, on utilise généralement Padding à l'intérieur pour espacer le contenu des bords de la carte.

🧪 Exemple complet

Voici un exemple complet dans une application :

Exemple visuel d'une Card avec Padding et Text
Exemple visuel : une Card contenant du texte avec du Padding.
import 'package:flutter/material.dart';

void main() {
  runApp(const MaPage());
}

class MaPage extends StatelessWidget {
  const MaPage({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Mon Application'),
        ),
        body: SafeArea(
          child: Center(
            child: Card(
              child: Padding(
                padding: EdgeInsets.all(16),
                child: Text('Bonjour Flutter !'),
              ),
            ),
          ),
        ),
      ),
    );
  }
}

Dans cet exemple, le texte "Bonjour Flutter !" est affiché dans une carte élégante avec un espacement interne.

📦 Card utilisée dans une Column ou Row

Les cartes sont souvent utilisées dans des Column ou Row pour créer des listes de cartes. Voici un exemple avec une Column :

Exemple visuel de plusieurs Cards dans une Column
Exemple visuel : plusieurs Card empilées verticalement dans une Column.
import 'package:flutter/material.dart';

void main() {
  runApp(const MaPage());
}

class MaPage extends StatelessWidget {
  const MaPage({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Mon Application'),
        ),
        body: SafeArea(
          child: Center(
            child: Column(
              children: [
                Card(
                  child: Padding(
                    padding: EdgeInsets.all(16),
                    child: Text('Première carte'),
                  ),
                ),
                Card(
                  child: Padding(
                    padding: EdgeInsets.all(16),
                    child: Text('Deuxième carte'),
                  ),
                ),
                Card(
                  child: Padding(
                    padding: EdgeInsets.all(16),
                    child: Text('Troisième carte'),
                  ),
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }
}

Dans cet exemple, trois cartes sont affichées verticalement dans une Column. Chaque carte contient du texte avec du padding.

Conseil :
Pour espacer les cartes entre elles, vous pouvez ajouter des marges aux cartes ou utiliser mainAxisAlignment: MainAxisAlignment.spaceBetween dans la Column. Pour l'instant, gardons les choses simples.

💡 Points clés à retenir

  • Card présente le contenu de manière élégante avec des ombres et des coins arrondis
  • Padding ajoute de l'espace à l'intérieur d'un widget
  • Les cartes sont souvent utilisées dans des Column ou Row pour créer des listes
  • EdgeInsets.all(16) ajoute 16 pixels d'espace de tous les côtés
Prochaine étape :
Dans la section suivante, nous allons découvrir SizedBox, un widget simple mais très utile pour ajouter de l'espacement entre les éléments.

2.4.6 – Gérer l'espacement avec Padding et SizedBox

Quand vous organisez plusieurs widgets dans une Column ou une Row, il est souvent nécessaire d'ajouter de l'espace. Flutter propose deux widgets pour cela : Padding et SizedBox. Chacun a son utilité et ses cas d'usage. Découvrons-les ensemble.

📦 Introduction à Padding

Padding est un widget qui ajoute de l'espace autour d'un widget existant. Il enveloppe un widget et ajoute de l'espacement à l'intérieur, entre le bord et le contenu.

Analogie :
Imaginez une boîte avec un objet à l'intérieur. Padding est comme ajouter de la mousse autour de l'objet pour qu'il ne touche pas les bords de la boîte. L'espace est créé autour de l'objet, à l'intérieur de la boîte.

Voici la structure de base de Padding :

Padding(
  padding: EdgeInsets.all(16),
  child: Text('Mon texte'),
)

Analysons ce code :

  • Padding( : Le widget qui ajoute de l'espace
  • padding: EdgeInsets.all(16) : Ajoute 16 pixels d'espace de tous les côtés (haut, bas, gauche, droite)
  • child: Text('Mon texte') : Le widget autour duquel l'espace est ajouté
EdgeInsets.all(16) :
EdgeInsets.all(16) ajoute 16 pixels d'espace de tous les côtés. Vous pouvez aussi utiliser :
  • EdgeInsets.only(left: 10, top: 20) : espace spécifique sur certains côtés
  • EdgeInsets.symmetric(horizontal: 16, vertical: 8) : espace symétrique
Pour l'instant, EdgeInsets.all() est le plus simple et le plus courant.

📏 Introduction à SizedBox

SizedBox est un widget qui crée un espace vide de taille définie. Contrairement à Padding, il ne contient pas de widget enfant : c'est un espace vide qui prend de la place.

Analogie :
Imaginez que vous rangez des livres sur une étagère. SizedBox est comme un espace vide entre deux livres. Il ne contient rien, mais il crée une séparation visuelle entre les éléments.

Voici la structure de base de SizedBox :

SizedBox(height: 20)

Pour un espace vertical, utilisez height. Pour un espace horizontal, utilisez width.

SizedBox :
SizedBox est très simple : il crée un espace vide. Il n'a pas besoin de child car il ne contient rien. C'est juste un espace qui prend de la place dans votre layout.

⬆️ Exemples d'espacement vertical

Pour ajouter de l'espace vertical entre des éléments dans une Column, vous pouvez utiliser les deux méthodes :

Avec SizedBox

Column(
  children: [
    Text('Premier élément'),
    SizedBox(height: 20),  // Espace vide entre les deux textes
    Text('Deuxième élément'),
  ],
)

SizedBox(height: 20) crée un espace vertical de 20 pixels entre les deux textes.

Avec Padding

Column(
  children: [
    Padding(
      padding: EdgeInsets.only(bottom: 20),  // Espace en bas du premier texte
      child: Text('Premier élément'),
    ),
    Text('Deuxième élément'),
  ],
)

Padding avec EdgeInsets.only(bottom: 20) ajoute 20 pixels d'espace en bas du premier texte.

EdgeInsets.only(bottom: 20) :
EdgeInsets.only() permet de définir l'espace sur un seul côté. Ici, bottom: 20 ajoute 20 pixels d'espace uniquement en bas. Vous pouvez aussi utiliser top, left, ou right.

➡️ Exemples d'espacement horizontal

Pour ajouter de l'espace horizontal entre des éléments dans une Row, vous pouvez aussi utiliser les deux méthodes :

Avec SizedBox

Row(
  children: [
    Text('Premier'),
    SizedBox(width: 30),  // Espace vide entre les deux textes
    Text('Deuxième'),
  ],
)

SizedBox(width: 30) crée un espace horizontal de 30 pixels entre les deux textes.

Avec Padding

Row(
  children: [
    Padding(
      padding: EdgeInsets.only(right: 30),  // Espace à droite du premier texte
      child: Text('Premier'),
    ),
    Text('Deuxième'),
  ],
)

Padding avec EdgeInsets.only(right: 30) ajoute 30 pixels d'espace à droite du premier texte.

🧪 Exemple complet : espacement vertical avec SizedBox

Voici un exemple complet avec espacement vertical dans une Column utilisant SizedBox :

Exemple visuel SizedBox entre plusieurs éléments
Exemple visuel : des espaces verticaux réalisés avec SizedBox entre chaque texte dans une Column.
import 'package:flutter/material.dart';

void main() {
  runApp(const MaPage());
}

class MaPage extends StatelessWidget {
  const MaPage({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Mon Application'),
        ),
        body: SafeArea(
          child: Center(
            child: Column(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                const Text(
                  'Premier élément',
                  style: TextStyle(fontSize: 20),
                ),
                const SizedBox(height: 30),
                const Text(
                  'Deuxième élément',
                  style: TextStyle(fontSize: 20),
                ),
                const SizedBox(height: 30),
                const Text(
                  'Troisième élément',
                  style: TextStyle(fontSize: 20),
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }
}

Dans cet exemple, les trois textes sont séparés par des espaces de 30 pixels chacun, le tout centré à l'écran.

Valeurs courantes :
Les valeurs d'espacement les plus courantes sont :
  • SizedBox(height: 8) ou width: 8 : espacement très petit
  • SizedBox(height: 16) ou width: 16 : espacement petit
  • SizedBox(height: 24) ou width: 24 : espacement moyen
  • SizedBox(height: 32) ou width: 32 : espacement grand
Vous pouvez bien sûr utiliser n'importe quelle valeur selon vos besoins.

🧪 Exemple complet : espacement horizontal avec SizedBox

Voici un exemple avec espacement horizontal dans une Row utilisant SizedBox :

import 'package:flutter/material.dart';

void main() {
  runApp(const MaPage());
}

class MaPage extends StatelessWidget {
  const MaPage({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Mon Application'),
        ),
        body: SafeArea(
          child: Center(
            child: Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                const Text(
                  'Premier',
                  style: TextStyle(fontSize: 20),
                ),
                const SizedBox(width: 40),
                const Text(
                  'Deuxième',
                  style: TextStyle(fontSize: 20),
                ),
                const SizedBox(width: 40),
                const Text(
                  'Troisième',
                  style: TextStyle(fontSize: 20),
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }
}

Dans cet exemple, les trois textes sont séparés horizontalement par des espaces de 40 pixels chacun.

🔄 Comparatif : SizedBox ou Padding ?

Maintenant que vous connaissez les deux widgets, vous vous demandez peut-être : lequel utiliser et quand ? Voici un comparatif clair pour vous aider à choisir.

Différence fondamentale :
  • SizedBox : Crée un espace vide, un widget invisible qui prend de la place. Il est utilisé pour espacer des éléments entre eux dans une liste.
  • Padding : Ajoute de l'espace autour d'un widget existant. Il enveloppe un widget et ajoute de l'espacement à l'intérieur, entre le bord et le contenu.

Exemple visuel de la différence

Voici deux exemples qui montrent la différence :

// Avec SizedBox : espace ENTRE les éléments
Column(
  children: [
    Text('Premier'),
    SizedBox(height: 20),  // Espace vide entre les deux textes
    Text('Deuxième'),
  ],
)

// Avec Padding : espace AUTOUR d'un élément
Column(
  children: [
    Padding(
      padding: EdgeInsets.all(20),  // Espace autour du texte
      child: Text('Premier'),
    ),
    Text('Deuxième'),
  ],
)
Dans le premier exemple (SizedBox) :
Il y a un espace vide de 20 pixels entre les deux textes. Le SizedBox est un élément séparé dans la liste.
Dans le deuxième exemple (Padding) :
L'espace de 20 pixels est autour du premier texte. Le Padding enveloppe le texte et crée de l'espace à l'intérieur.

Quand utiliser quoi ?

Utilisez SizedBox quand :
  • Vous voulez espacer des éléments entre eux dans une Column ou une Row
  • Vous avez besoin d'un espacement simple et clair
  • Vous voulez séparer visuellement plusieurs widgets dans une liste
Exemple : Séparer trois boutons dans une Column avec SizedBox(height: 16) entre chacun.
Utilisez Padding quand :
  • Vous voulez ajouter de l'espace autour d'un widget spécifique
  • Vous travaillez avec un widget qui a besoin d'espace interne (comme une Card)
  • Vous voulez créer une zone de "respiration" autour d'un contenu
Exemple : Ajouter de l'espace autour du texte dans une Card avec Padding(padding: EdgeInsets.all(16), child: Text(...)).
Conseil pratique :
Dans la plupart des cas, pour espacer des éléments dans une Column ou une Row, utilisez SizedBox. C'est plus simple, plus clair et plus direct. Utilisez Padding quand vous voulez vraiment ajouter de l'espace autour d'un widget spécifique, comme nous l'avons fait avec les Card dans la section précédente.

💡 Points clés à retenir

  • Padding ajoute de l'espace autour d'un widget existant
  • Padding utilise EdgeInsets.all(16) pour ajouter de l'espace de tous les côtés
  • SizedBox crée un espace vide de taille définie
  • SizedBox(height: 20) crée un espace vertical de 20 pixels
  • SizedBox(width: 30) crée un espace horizontal de 30 pixels
  • SizedBox est utilisé pour espacer des éléments entre eux dans une liste
  • Padding est utilisé pour ajouter de l'espace autour d'un widget spécifique
Résumé des structures :
Voici les structures principales :
// Padding : espace autour d'un widget
Padding(
  padding: EdgeInsets.all(16),
  child: Text('Mon texte'),
)

// SizedBox : espace entre les éléments
SizedBox(height: 20)  // Vertical
SizedBox(width: 30)   // Horizontal
Choisissez selon votre besoin : espace autour d'un widget → Padding, espace entre des éléments → SizedBox.
Prochaine étape :
Dans la section suivante, nous allons découvrir Divider, un widget simple mais très utile pour séparer visuellement des éléments dans votre interface.

2.4.7 – Séparer visuellement avec Divider

Quand vous organisez plusieurs éléments dans une Column ou une Row, il est parfois utile de les séparer visuellement avec une ligne. Le widget Divider est parfait pour cela. C'est un widget simple qui affiche une ligne horizontale pour séparer des éléments.

📏 Rôle du Divider

Divider est un widget qui affiche une ligne horizontale fine. Il est utilisé pour séparer visuellement des éléments dans une interface, créant une distinction claire entre différentes sections ou groupes d'informations.

Analogie :
Imaginez un menu de restaurant. Les différentes sections (entrées, plats principaux, desserts) sont souvent séparées par des lignes horizontales. Divider fonctionne de la même manière : il crée une séparation visuelle claire entre des groupes d'éléments.

🧪 Exemple simple : Divider entre deux widgets

Voici comment utiliser Divider dans une Column :

Column(
  children: [
    Text('Premier élément'),
    Divider(),
    Text('Deuxième élément'),
  ],
)

Dans cet exemple, Divider() crée une ligne horizontale entre les deux textes.

Divider() :
Divider() est très simple à utiliser : il ne nécessite aucun paramètre. Par défaut, il affiche une ligne horizontale fine avec un espacement automatique de chaque côté.

🧪 Exemple complet : Divider dans une Column

Voici un exemple complet dans une application :

Exemple visuel Divider dans une Column Flutter
Exemple visuel : Divider() sépare deux widgets dans une Column.
import 'package:flutter/material.dart';

void main() {
  runApp(const MaPage());
}

class MaPage extends StatelessWidget {
  const MaPage({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Mon Application'),
        ),
        body: SafeArea(
          child: Column(
            children: [
              const Padding(
                padding: EdgeInsets.all(16),
                child: Text('Section 1', style: TextStyle(fontSize: 20)),
              ),
              const Divider(),
              const Padding(
                padding: EdgeInsets.all(16),
                child: Text('Section 2', style: TextStyle(fontSize: 20)),
              ),
              const Divider(),
              const Padding(
                padding: EdgeInsets.all(16),
                child: Text('Section 3', style: TextStyle(fontSize: 20)),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

Dans cet exemple, trois sections de texte sont séparées par des Divider, créant une interface claire et organisée.

🎨 Personnalisation (épaisseur, couleur, marges)

Vous pouvez personnaliser l'apparence du Divider avec plusieurs paramètres :

Divider(
  height: 20,
  thickness: 2,
  color: Colors.grey,
  indent: 20,
  endIndent: 20,
)

Analysons ces paramètres :

  • height: 20 : La hauteur totale du Divider (espace vertical qu'il occupe, en pixels)
  • thickness: 2 : L'épaisseur de la ligne (en pixels). Par défaut, c'est 1 pixel
  • color: Colors.grey : La couleur de la ligne. Par défaut, c'est une couleur grise claire
  • indent: 20 : L'espacement à gauche de la ligne (en pixels)
  • endIndent: 20 : L'espacement à droite de la ligne (en pixels)
indent et endIndent :
Ces paramètres permettent de créer une ligne qui ne va pas d'un bord à l'autre. Par exemple, indent: 20 et endIndent: 20 créent une ligne qui commence 20 pixels après le bord gauche et se termine 20 pixels avant le bord droit.

🧪 Exemple complet : Divider personnalisé

Voici un exemple avec un Divider personnalisé :

Exemple Divider personnalisé Flutter
Exemple visuel : un Divider personnalisé sépare deux sections avec couleur, épaisseur, et marges définies.
import 'package:flutter/material.dart';

void main() {
  runApp(const MaPage());
}

class MaPage extends StatelessWidget {
  const MaPage({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Mon Application'),
        ),
        body: SafeArea(
          child: Column(
            children: [
              const Padding(
                padding: EdgeInsets.all(16),
                child: Text('Première section', style: TextStyle(fontSize: 20)),
              ),
              const Divider(
                height: 30,
                thickness: 2,
                color: Colors.blue,
                indent: 40,
                endIndent: 40,
              ),
              const Padding(
                padding: EdgeInsets.all(16),
                child: Text('Deuxième section', style: TextStyle(fontSize: 20)),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

Dans cet exemple, le Divider est personnalisé avec une épaisseur de 2 pixels, une couleur bleue, et des espacements à gauche et à droite de 40 pixels.

💼 Cas d'utilisation courants

Divider est très utile dans plusieurs situations :

1. Séparer des sections dans une liste :
Dans une Column contenant plusieurs sections d'informations, Divider permet de créer une séparation visuelle claire entre chaque section.
2. Dans des Card :
Vous pouvez utiliser Divider à l'intérieur d'une Card pour séparer différents types d'informations (par exemple, un titre, un Divider, puis le contenu).
3. Créer des menus :
Dans une interface de menu ou de navigation, Divider permet de séparer visuellement les différents groupes d'options.

🧪 Exemple : Divider dans une Card

Voici un exemple d'utilisation de Divider dans une Card :

Exemple Divider dans une Card Flutter
Illustration : Divider utilisé pour séparer le titre et le contenu dans une Card.
import 'package:flutter/material.dart';

void main() {
  runApp(const MaPage());
}

class MaPage extends StatelessWidget {
  const MaPage({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Mon Application'),
        ),
        body: SafeArea(
          child: Center(
            child: Card(
              child: Column(
                mainAxisSize: MainAxisSize.min,
                children: [
                  const Padding(
                    padding: EdgeInsets.all(16),
                    child: Text(
                      'Titre de la carte',
                      style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
                    ),
                  ),
                  const Divider(),
                  const Padding(
                    padding: EdgeInsets.all(16),
                    child: Text('Contenu de la carte'),
                  ),
                ],
              ),
            ),
          ),
        ),
      ),
    );
  }
}

Dans cet exemple, une Card contient un titre, un Divider, puis le contenu. Le Divider sépare visuellement le titre du contenu.

mainAxisSize: MainAxisSize.min :
Ce paramètre de Column fait que la colonne prend seulement l'espace nécessaire (minimum) au lieu de prendre tout l'espace disponible. C'est utile dans une Card pour que la carte ne soit pas trop grande.

💡 Points clés à retenir

  • Divider affiche une ligne horizontale pour séparer visuellement des éléments
  • Divider() est très simple à utiliser sans paramètres
  • height définit l'espace vertical total occupé par le Divider
  • thickness définit l'épaisseur de la ligne (par défaut : 1 pixel)
  • color définit la couleur de la ligne
  • indent et endIndent créent des espacements à gauche et à droite
  • Divider est souvent utilisé dans des listes, des Card, ou des menus
Résumé de la structure :
Voici les utilisations principales de Divider :
// Divider simple
Divider()

// Divider personnalisé
Divider(
  height: 20,
  thickness: 2,
  color: Colors.grey,
  indent: 20,
  endIndent: 20,
)
Utilisez Divider pour créer des séparations visuelles claires dans votre interface.
Félicitations ! 🎉
Vous avez terminé ce chapitre ! Vous savez maintenant :
  • ✅ Utiliser Container pour styliser et organiser
  • ✅ Organiser des widgets verticalement avec Column
  • ✅ Organiser des widgets horizontalement avec Row
  • ✅ Combiner Row et Column pour des layouts complexes
  • ✅ Utiliser Expanded pour répartir l'espace
  • ✅ Présenter du contenu avec Card
  • ✅ Gérer l'espacement avec Padding et SizedBox
  • ✅ Séparer visuellement avec Divider
Vous avez maintenant tous les outils de base pour créer des interfaces Flutter structurées et professionnelles. Continuez comme ça ! 💪

🎯 Récapitulatif de la section 2.4
Vous avez appris à organiser votre interface Flutter :
  • Container permet de styliser avec taille, couleur et marges
  • Column organise les widgets verticalement
  • Row organise les widgets horizontalement
  • children: [] permet d'ajouter plusieurs widgets
  • mainAxisAlignment et crossAxisAlignment contrôlent l'alignement
  • Expanded permet de répartir l'espace disponible
  • Card présente le contenu de manière élégante
  • Padding ajoute de l'espace interne
  • SizedBox permet d'espacer des éléments entre eux
  • Divider sépare visuellement des éléments
Prochaine étape : Vous avez maintenant les bases pour créer des interfaces Flutter complètes. Dans les prochains chapitres, nous découvrirons des widgets plus avancés et des fonctionnalités interactives. À bientôt ! 🚀