Dans Flutter, lorsqu'un widget subit un changement d'état, il reconstruit sa description de l'interface utilisateur. Le framework compare ensuite cette nouvelle description à la précédente pour déterminer les modifications minimales nécessaires à appliquer à l'arbre de rendu sous-jacent, puis actualise l'affichage en conséquence.
Il est crucial de comprendre que les éléments qui représentent réellement l'interface à l'écran dans Flutter sont les Element. Un Widget n'est qu'une description des données de configuration pour un Element. Il est important de noter qu'un seul Widget peut correspondre à plusieurs Element.
StatelessWidget
Un StatelessWidget est un composant qui n'a pas d'état interne mutable. Pour en créer un, il suffit d'étendre la classe de base StatelessWidget et d'implémenter la méthode build(). Puisqu'il est dépourvu d'état, un StatelessWidget ne se rend qu'une seule fois lors de son initialisation. Il ne peut pas se mettre à jour ou se redessiner suite à des interactions utilisateur ou des changements temporels.
import 'package:flutter/material.dart';
class MaSimpleApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
// La méthode build prend un BuildContext, qui fournit des informations
// sur la position du widget dans l'arbre des widgets.
return const MaterialApp(
home: Center(
child: Text('Bonjour, Widget Sans État !'),
),
);
}
}
La méthode build() prend un paramètre BuildContext, qui est une référence au positionnement de ce widget dans l'arbre des widgets. Il permet d'accéder aux informations de l'arbre, comme le thème ou les parents, et de naviguer ou de rechercher des widgets ascendants.
StatefulWidget
Un StatefulWidget est un composant doté d'un état interne dynamique qu'il gère via un objet State associé. Ses variables membres n'ont pas besoin d'être déclarées final. Pour mettre à jour l'interface utilisateur suite à une modification de l'état interne, la méthode setState() doit être appelée. Lorsque Flutter détecte un changement d'état, il déclenche un redessin du widget. Pour créer un StatefulWidget, vous devez hériter de la classe StatefulWidget et surcharger la méthode createState().
import 'package:flutter/material.dart';
class CompteurApp extends StatefulWidget {
@override
_CompteurAppState createState() => _CompteurAppState();
}
class _CompteurAppState extends State<CompteurApp> {
int _valeurCompteur = 0;
void _incrementerCompteur() {
setState(() {
// Mettre à jour l'état et déclencher un redessin du widget
_valeurCompteur++;
});
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: const Text('Compteur Dynamique')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text('Vous avez appuyé sur le bouton tant de fois :'),
Text(
'$_valeurCompteur',
style: Theme.of(context).textTheme.headlineMedium,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementerCompteur,
tooltip: 'Incrémenter',
child: const Icon(Icons.add),
),
),
);
}
}
Contrairement au StatelessWidget, le StatefulWidget est associé à un état qui peut changer. Toute modification des variables internes doit être encapsulée dans un appel à setState() pour notifier à Flutter que l'état a changé et qu'un redessin est nécessaire. La classe State<T> (où T est le type du StatefulWidget associé) est la classe qui détient cet état mutable.
MaterialApp
Le MaterialApp est un widget essentiel qui enveloppe l'ensemble de votre application Flutter. Il fournit une configuration de base pour une application Material Design, y compris les thèmes, la navigation et les locales.
| Propriété | Type | Description |
|---|---|---|
title |
String |
Titre affiché dans le sélecteur d'applications Android. Sans effet sur iOS. |
home |
Widget |
Le widget affiché par défaut au démarrage de l'application. |
routes |
Map<String, WidgetBuilder> |
Une table des routes de haut niveau pour la navigation. |
theme |
ThemeData |
Définit le thème visuel global de l'application (couleurs, polices, etc.). |
theme.primarySwatch |
MaterialColor |
Une palette de couleurs Material Design à utiliser comme couleur principale. |
theme.primaryColor |
Color |
La couleur principale unique pour la barre d'applications et autres éléments. |
AppBar
L'AppBar est un widget qui représente la barre d'applications Material Design, généralement affichée en haut d'un Scaffold. Elle peut contenir un titre, des boutons d'action et d'autres éléments de l'interface utilisateur.
| Propriété | Nom | Description |
|---|---|---|
leading |
Widget |
Un widget affiché avant le titre, généralement un bouton de navigation (ex: retour). |
title |
Widget |
Le titre principal affiché dans la barre d'applications. |
actions |
List<Widget> |
Une liste de widgets affichés après le titre, souvent des icônes d'action. |
flexibleSpace |
Widget |
Un widget placé derrière la barre d'applications et son bottom, pouvant s'étirer. |
bottom |
PreferredSizeWidget |
Un widget personnalisé affiché en bas de l'AppBar, souvent un TabBar. |
elevation |
double |
Contrôle l'élévation et l'ombre sous l'AppBar. |
backgroundColor |
Color |
La couleur de fond de la barre d'applications. |
brightness |
Brightness |
Luminosité du matériel de la barre d'applications (obsolète, utilisez systemOverlayStyle). |
textTheme |
TextTheme |
Paramètres du thème de texte pour les widgets enfants de l'AppBar. |
primary |
bool |
Indique si l'AppBar doit être affichée en haut de la barre des tâches (généralement true). |
centerTitle |
bool |
Indique si le titre doit être centré (par défaut false sur Android, true sur iOS). |
titleSpacing |
double |
L'espacement horizontal entre le widget leading et le titre. |
toolbarOpacity |
double |
L'opacité de la barre d'outils, de 1 (opaque) à 0 (transparent). |
Scaffold
Le Scaffold est un widget qui implémente la structure de base d'une application Material Design, comme une AppBar, un Drawer (tiroir de navigation), un SnackBar, et un FloatingActionButton. Il occupe l'intégralité de l'écran disponible.
| Propriété | Type | Description |
|---|---|---|
appBar |
AppBar |
La barre d'applications à afficher en haut de l'écran. |
body |
Widget |
Le contenu principal du Scaffold, occupant l'espace restant sous l'AppBar. |
bottomNavigationBar |
BottomNavigationBar |
La barre de navigation inférieure, affichée en bas de l'écran. |
drawer |
Drawer |
Un panneau coulissant de navigation (tiroir) affiché depuis le côté gauche. |
floatingActionButton |
FloatingActionButton |
Un bouton d'action flottant, généralement placé en bas à droite. |
Voici un exemple d'utilisation du Scaffold avec une barre de navigation inférieure et un tiroir :
import 'package:flutter/material.dart';
void main() => runApp(ApplicationPrincipes());
class ApplicationPrincipes extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Démonstration Scaffold',
theme: ThemeData(primarySwatch: Colors.blue),
home: Scaffold(
appBar: AppBar(
title: const Text('Ma Page d\'Accueil'),
backgroundColor: Colors.blueAccent,
),
body: const Center(
child: Text(
'Contenu de la page principale',
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
),
),
bottomNavigationBar: BottomNavigationBar(
type: BottomNavigationBarType.fixed, // Ou .shifting
currentIndex: 0,
items: const [
BottomNavigationBarItem(
icon: Icon(Icons.home),
label: Text('Accueil')
),
BottomNavigationBarItem(
icon: Icon(Icons.explore),
label: Text('Explorer')
),
BottomNavigationBarItem(
icon: Icon(Icons.settings),
label: Text('Paramètres')
)
],
onTap: (index) {
// Logique de navigation ou de changement d'état ici
print('Élément de la barre inférieure tapé : $index');
},
),
floatingActionButton: FloatingActionButton(
onPressed: () {
print('Bouton flottant pressé !');
},
child: const Icon(Icons.add),
),
drawer: Drawer(
child: ListView(
padding: EdgeInsets.zero,
children: const <Widget>[
DrawerHeader(
decoration: BoxDecoration(
color: Colors.blueAccent,
),
child: Text(
'Menu de Navigation',
style: TextStyle(color: Colors.white, fontSize: 24),
),
),
ListTile(
leading: Icon(Icons.message),
title: Text('Messages'),
),
ListTile(
leading: Icon(Icons.account_circle),
title: Text('Profil'),
),
],
),
),
),
);
}
}
Widgets Texte
Le widget Text est utilisé pour afficher une chaîne de caractères sur l'écran. Il offre de nombreuses options de style et de mise en page.
| Propriété | Type | Description |
|---|---|---|
data |
String |
Le contenu textuel à afficher. |
style |
TextStyle |
Le style visuel à appliquer au texte (police, couleur, taille, etc.). |
strutStyle |
StrutStyle |
Définit les propriétés de la 'strut' (hauteur minimale de ligne). |
textAlign |
TextAlign |
L'alignement horizontal du texte dans son espace disponible. |
textDirection |
TextDirection |
La direction de lecture du texte (gauche-à-droite (LTR) ou droite-à-gauche (RTL)). |
locale |
Locale |
Identifiant pour la sélection de la langue et du formatage utilisateur. |
softWrap |
bool |
Indique si le texte doit s'enrouler sur plusieurs lignes si nécessaire. |
overflow |
TextOverflow |
Comment gérer le texte qui dépasse de son conteneur (troncature, fondu, etc.). |
textScaleFactor |
double |
Facteur d'échelle pour la taille du texte par rapport à la taille par défaut. |
maxLines |
int |
Le nombre maximum de lignes à afficher. Un dépassement entraînera un overflow. |
semanticsLabel |
String |
Une étiquette sémantique pour l'accessibilité, lue par les lecteurs d'écran. |
Widgets Boutons
La bibliothèque de composants Material offre plusieurs widgets de boutons. Il est important de noter que RaisedButton, FlatButton et OutlineButton sont obsolètes et remplacés par des widgets plus modernes comme ElevatedButton, TextButton et OutlinedButton respectivement. FloatingActionButton et IconButton restent valides et largement utilisés.
ElevatedButton(remplaceRaisedButton)TextButton(remplaceFlatButton)OutlinedButton(remplaceOutlineButton)FloatingActionButtonIconButton
Voici un exemple montrant l'utilisation de différents types de boutons :
import 'package:flutter/material.dart';
void main() => runApp(DemoBoutons());
class DemoBoutons extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Exemple de Boutons Flutter',
theme: ThemeData(primarySwatch: Colors.indigo),
home: Scaffold(
appBar: AppBar(title: const Text('Widgets Boutons')),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
ElevatedButton(
onPressed: () => print('Bouton Élevé pressé !'),
child: const Text('Bouton Élevé'),
),
TextButton(
onPressed: () => print('Bouton Texte pressé !'),
child: const Text('Bouton Texte'),
),
OutlinedButton(
onPressed: () => print('Bouton Contour pressé !'),
child: const Text('Bouton Contour'),
),
FloatingActionButton.extended(
onPressed: () => print('Bouton Flottant Étendu pressé !'),
label: const Text('Créer Nouveau'),
icon: const Icon(Icons.add),
),
IconButton(
onPressed: () => print('Bouton Icône pressé !'),
icon: const Icon(Icons.favorite),
color: Colors.red,
iconSize: 30.0,
),
],
),
),
),
);
}
}
Propriétés communes aux boutons (certaines sont désormais gérées par ButtonStyle pour les nouveaux boutons) :
| Propriété | Type | Description |
|---|---|---|
textColor |
Color |
Couleur du texte du bouton (souvent définie via ButtonStyle.foregroundColor). |
disabledTextColor |
Color |
Couleur du texte lorsque le bouton est désactivé. |
color |
Color |
Couleur de fond du bouton (souvent définie via ButtonStyle.backgroundColor). |
highlightColor |
Color |
Couleur de surbrillance lorsque le bouton est pressé. |
splashColor |
Color |
Couleur de l'effet d'ondulation lors d'un clic. |
colorBrightness |
Brightness |
Luminosité du thème du bouton (déprécié). |
padding |
EdgeInsetsGeometry |
Espacement interne du bouton. |
shape |
ShapeBorder |
Forme du bouton (arrondi, rectangulaire, etc.). |
child |
Widget |
Le widget enfant affiché à l'intérieur du bouton. |
onPressed |
VoidCallback |
La fonction de rappel à exécuter lorsque le bouton est pressé. Si null, le bouton est désactivé. |
Widgets Image
Le widget Image est utilisé pour charger et afficher des images de différentes sources. Flutter offre plusieurs constructeurs pour différentes méthodes de chargement :
Image: Le constructeur de base pour charger une image via unImageProvider.Image.asset: Pour charger une image depuis les assets locaux de l'appplication.Image.file: Pour charger une image depuis un fichier sur le système de fichiers.Image.network: Pour charger une image depuis une URL réseau.Image.memory: Pour charger une image depuis un bloc de données en mémoire (Uint8List).
| Propriété | Type | Description |
|---|---|---|
scale |
double |
Le facteur d'échelle pour l'image. |
semanticsLabel |
String |
Une étiquette sémantique pour l'accessibilité, décrivant le contenu de l'image. |
width |
double |
La largeur de l'image. Si null, affiche la largeur naturelle. |
height |
double |
La hauteur de l'image. Si null, affiche la hauteur naturelle. |
color |
Color |
Une couleur à mélanger avec l'image (nécessite colorBlendMode défini). |
fit |
BoxFit |
Le mode d'ajustement de l'image dans son conteneur. |
alignment |
Alignment |
L'alignement de l'image si elle ne remplit pas son conteneur. |
repeat |
ImageRepeat |
Comment répéter l'image si elle est plus petite que l'espace disponible. |
centerSlice |
Rect |
Une découpe centrale pour les images de type '9-patch'. |
matchTextDirection |
bool |
Si true, l'image est inversée pour les directions de texte RTL. |
gaplessPlayback |
bool |
Si true, maintient l'ancienne image pendant le chargement d'une nouvelle. |
filterQuality |
FilterQuality |
La qualité de filtrage de l'image lors de la mise à l'échelle. |
Les modes de remplissage (BoxFit) :
BoxFit.fill: L'image remplit complètement l'espace, pouvant être déformée.BoxFit.contain: L'image est entièrement contenue dans l'espace, en respectant son rapport d'aspect. Peut laisser des espaces vides (letterboxing).BoxFit.cover: L'image couvre l'espace en coupant les bords si nécessaire, sans déformation.BoxFit.fitWidth: L'image remplit la largeur et s'ajuste proportionnellement en hauteur, coupant si elle dépasse.BoxFit.fitHeight: L'image remplit la hauteur et s'ajuste proportionnellement en largeur, coupant si elle dépasse.BoxFit.scaleDown: Similaire àcontain, mais réduit l'image si elle est trop grande pour tenir, sans l'agrandir.BoxFit.none: L'image est affichée à sa taille originale, sans ajustement ni coupure, et n'est pas mise à l'échelle.
Flutter met en cache les images par défaut. La taille maximale du cache d'images est de 1000 images et 100 Mo.
Widgets Icônes
Les widgets d'icônes affichent des symboles graphiques. Flutter propose des icônes Material Design intégrées et la possibilité d'utiliser des images comme icônes.
IconButton: Un widget icône interactif, réagit aux pressions.Icons: Un ensemble prédéfini d'icônes Material Design.IconTheme: Définit le thème par défaut (couleur, taille) pour les icônes enfants.ImageIcon: Affiche une icône à partir d'une image (par exemple, un asset).
| Propriété | Type | Description |
|---|---|---|
size |
double |
La taille de l'icône en pixels logiques. |
color |
Color |
La couleur de l'icône. |
semanticsLabel |
String |
Une étiquette sémantique pour l'accessibilité. |
textDirection |
TextDirection |
La direction de dessin de l'icône (affecte les icônes directionnelles). |
Widgets d'Entrée de Texte (TextField)
Le widget TextField est un champ de texte interactif permettant à l'utilisateur de saisir du texte. Il offre de nombreuses options de personnalisation et de gestion de l'entrée.
| Propriété | Description |
|---|---|
controller |
Contrôleur pour accéder et modifier le texte du champ, ainsi que pour écouter les changements. |
focusNode |
Contrôle si le TextField a le focus d'entrée, gérant l'interaction clavier. |
decoration |
Définit l'apparence visuelle du TextField (hint, label, icône, bordure, etc.) via un InputDecoration. |
textAlign |
Alignement horizontal du texte à l'intérieur du champ. |
textDirection |
Direction du texte à l'intérieur du champ. |
keyboardType |
Définit le type de clavier à afficher (numérique, email, texte, etc.). |
textInputAction |
Définit l'action du bouton 'Entrée' sur le clavier (ex: 'Done', 'Next'). |
style |
Le style du texte saisi dans le champ. |
autofocus |
Si true, le champ obtient automatiquement le focus au démarrage (par défaut false). |
obscureText |
Si true, le texte saisi est masqué (utile pour les mots de passe). |
maxLines |
Le nombre maximum de lignes du champ de texte. Par défaut 1. Si null, supporte plusieurs lignes. |
maxLength |
Le nombre maximum de caractères autorisés. Affiche un compteur de caractères si défini. |
onChanged |
Fonction de rappel appelée chaque fois que le contenu du texte change. |
onEditingComplete |
Fonction de rappel appelée lorsque l'édition est terminée (ex: envoi du formulaire via le clavier). |
onSubmitted |
Fonction de rappel appelée lorsque l'utilisateur soumet le texte (retourne le contenu). |
inputFormatters |
Une liste de formateurs pour contraindre l'entrée du texte (ex: chiffres uniquement). |
enabled |
Si false, le champ de texte est désactivé et ne peut pas être modifié. |
enableInteractiveSelection |
Si true, permet la sélection de texte interactive (copier, couper, coller). |
keyboardAppearance |
Le mode de luminosité du clavier (spécifique à iOS). |
onTap |
Fonction de rappel appelée lorsque le TextField est tapé. |
buildCounter |
Callback pour personnaliser l'affichage du compteur de caractères (via InputDecorator.counter). |
Widgets Formulaire (Form)
Le widget Form est un conteneur pour des champs de saisie de formulaire. Il permet de regrouper et de valider plusieurs FormField (comme TextFormField) simultanément.
| Propriété | Type | Description |
|---|---|---|
child |
Widget |
Le widget enfant du formulaire, contenant les champs. |
autovalidateMode |
AutovalidateMode |
Mode de validation automatique du formulaire (ex: onUserInteraction pour valider à chaque saisie). |
onWillPop |
WillPopCallback |
Fonction de rappel appelée lorsque l'utilisateur tente de quitter la route du formulaire. |
onChanged |
VoidCallback |
Fonction de rappel appelée lorsque l'un des éléments du formulaire change. |
Voici un exemple de formulaire avec des champs de validation :
import 'package:flutter/material.dart';
void main() {
runApp(DemoFormulaire());
}
class DemoFormulaire extends StatefulWidget {
@override
_DemoFormulaireState createState() => _DemoFormulaireState();
}
class _DemoFormulaireState extends State<DemoFormulaire> {
final GlobalKey<FormState> _cleFormulaire = GlobalKey<FormState>();
String? _nomUtilisateurSaisi;
String? _motDePasseSaisi;
@override
Widget build(BuildContext context) {
return MaterialApp(
title: "Formulaire Flutter",
theme: ThemeData(
primarySwatch: Colors.deepPurple,
),
home: Scaffold(
appBar: AppBar(
title: const Text("Composant Formulaire"),
),
body: Form(
key: _cleFormulaire,
autovalidateMode: AutovalidateMode.onUserInteraction, // Validation en temps réel
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
TextFormField(
decoration: const InputDecoration(
labelText: "Nom d'utilisateur",
hintText: "Entrez votre nom d'utilisateur",
prefixIcon: Icon(Icons.person),
border: OutlineInputBorder(),
),
validator: (valeur) {
if (valeur == null || valeur.isEmpty) {
return "Le nom d'utilisateur est requis.";
}
if (valeur.length < 6) {
return "Le nom d'utilisateur doit avoir au moins 6 caractères.";
}
return null; // Retourne null si la validation réussit
},
onSaved: (valeur) {
_nomUtilisateurSaisi = valeur;
},
),
const SizedBox(height: 20),
TextFormField(
decoration: const InputDecoration(
labelText: "Mot de passe",
hintText: "Choisissez un mot de passe fort",
prefixIcon: Icon(Icons.lock),
border: OutlineInputBorder(),
),
obscureText: true,
validator: (valeur) {
if (valeur == null || valeur.isEmpty) {
return "Le mot de passe est requis.";
}
if (valeur.length < 8) {
return "Le mot de passe doit avoir au moins 8 caractères.";
}
return null; // Retourne null si la validation réussit
},
onSaved: (valeur) {
_motDePasseSaisi = valeur;
},
),
const SizedBox(height: 30),
ElevatedButton(
onPressed: () {
if (_cleFormulaire.currentState?.validate() == true) {
_cleFormulaire.currentState?.save(); // Sauvegarde les valeurs des champs
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Connecté en tant que: ${_nomUtilisateurSaisi ?? ''}')),
);
print("Nom d'utilisateur: $_nomUtilisateurSaisi");
print("Mot de passe: $_motDePasseSaisi");
} else {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Veuillez corriger les erreurs du formulaire.')),
);
}
},
style: ElevatedButton.styleFrom(
padding: const EdgeInsets.symmetric(vertical: 15),
backgroundColor: Theme.of(context).primaryColor,
foregroundColor: Colors.white,
textStyle: const TextStyle(fontSize: 18),
),
child: const Text("Connexion"),
),
],
),
),
),
),
);
}
}
Widgets Conteneur (Container)
Le Container est un widget polyvalent qui peut contenir un seul enfant et offrir diverses options de style, de positionnement et de dimensionnement, telles que l'alignement, les marges (margin), les rembourrages (padding) et la décoration.
| Propriété | Type | Description |
|---|---|---|
key |
Key |
Identifiant unique du widget pour la recherche et la mise à jour. |
alignment |
AlignmentGeometry |
Alignement du widget enfant à l'intérieur du conteneur. |
padding |
EdgeInsetsGeometry |
Marge interne entre le bord du conteneur et son enfant. |
color |
Color |
La couleur de fond du conteneur. |
decoration |
Decoration |
Décoration de fond (bordures, ombres, dégradés, etc.). |
foregroundDecoration |
Decoration |
Décoration de premier plan (dessinée par-dessus l'enfant). |
width |
double |
La largeur spécifique du conteneur. |
height |
double |
La hauteur spécifique du conteneur. |
constraints |
BoxConstraints |
Les contraintes de taille imposées au conteneur. |
margin |
EdgeInsetsGeometry |
Marge externe autour du conteneur. |
transform |
Matrix4 |
Une matrice de transformation 4x4 à appliquer au conteneur. |
child |
Widget |
Le widget enfant contenu dans le Container. |
Le padding représente l'espace interne entre le contenu du conteneur et ses propres limites, tandis que le margin est l'espace externe qui sépare le conteneur des autres widgets. Les interactions tactiles sont reconnues dans la zone de padding, mais pas dans celle de margin.
Modèle de Contraintes de Boîte
Le modèle de contraintes de boîte en Flutter permet aux widgets de définir leur propre taille et positionnement en fonction de limites spécifiées par leurs parents. Les widgets courants qui exploitent ce modèle sont ConstrainedBox, SizedBox et UnconstrainedBox.
ConstrainedBox
ConstrainedBox est un widget qui applique des contraintes supplémentaires à son enfant. Il prend un paramètre BoxConstraints qui spécifie les contraintes de largeur et de hauteur minimales et maximales.
// Constructeur principal
const ConstrainedBox({
Key? key,
required BoxConstraints constraints, // Les contraintes à appliquer
Widget? child,
})
SizedBox
SizedBox est utilisé pour donner à son enfant une taille fixe (largeur et/ou hauteur). C'est une façon concise d'appliquer des contraintes de taille spécifique.
// Constructeur principal
const SizedBox({
Key? key,
double? width, // Largeur fixe
double? height, // Hauteur fixe
Widget? child,
})
Le widget SizedBox est une forme spécialisée de ConstrainedBox.
UnconstrainedBox
UnconstrainedBox est un widget qui supprime toutes les contraintes de son parent pour permettre à son enfant de se dimensionner librement selon sa taille intrinsèque. Il est généralement utilisé dans des cas spécifiques où un enfant doit ignorer les contraintes parentales (par exemple, pour provoquer un dépassement visuel intentionnel).