Editer Flutter
Kit de développement multiplateforme créé par Google utilisant le language dart.
Concepts
Widget
Quasi tout est widget (l'appli elle même, les layouts, les boutons etc).
Il est très rapide de détruire et recréer un widget (60 fois par seconde sans problème). On peut détruire/reconstruire le widget parent (par exemple changer la couleur de l'app) sans détruire le widget enfant. Il est assez intelligent pour ne pas recréer un widget si rien n'a changé.
-Les Widgets sont
immuables, ce sont des classes (donc avec méthodes, attributs...).
-Certains sont
visibles, d'autres
invisibles (servent à stocker des données, d'autres widgets, faire du layout...).
-Certains héritent de
StatelessWidget, d'autres
StatefulWidget (car doivent mémoriser des choses un champ texte retiendra la position du curseur, la position de la scrollbar...).
-Un
StatelessWidget doit absolument implémenter une méthode
"build", qui sera appelée à chaque création du Widget. La méthode DOIT contenir des sous widgets qui sont déclarés dans la méthode Build (vu que la méthode est obligatoire et DOIT renvoyer un Widget...).
-Un StatefulWidget doit
absolument contenir une méthode
createState. C'est dans le State qu'on trouvera la méthode "build". Le state est conservé, par contre "build" peut être appelé plusieurs fois par secondes sans souci.
-
Certains widgets sont dans les faits stateless mais n'héritent pas de StatelessWidget car il
font du rendu (héritent de RenderObjectWidget). Ils sont le fond du fond, c'est à dire quasiment les pixels qu'on va écrire sur l'écran, on arrive au bout de l'arbre là. Comme ils n'héritent pas de StatelessWidget ils n'ont pas besoin d'implémenter de méthode Build.
Application exemple en détail
Ci-dessous une analyse perso de l'appli exemple donnée par Google en 2026.
Au moment de la création d'un widget, la méthode build est appelée :
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
print("build appelé"); // S'appellera une seule fois
return ChangeNotifierProvider( // C'est un widget (a "Widget" pour ancêtre), est invisible, informe ses enfants de changements d'états.
create: (context) => MyAppState(), //L'état de l'application. Sera transmis aux enfants.
child: MaterialApp( //ici on créé un autre widget, invisible, qui transmettra des infos de config visuelles (thème, bavigation, police d'écriture...).
title: 'Namer App',
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepOrange),
),
home: MyHomePage(), //La route par défaut de MaterialApp
),
);
}
}
En dessous on peut avoir le ChangeNotifier :
class MyAppState extends ChangeNotifier { // Ce n'est pas un widget. Hérite de Listenable. Notifie ses abonnés en cas de changement.
var current = WordPair.random(); // Il préviendra si on modifie ses attributs
void coucouIciLeChangeNotifier() { // On peut lui mettre une fonction que ses abonnés (enfant de celui qui nous contient) peuvent appeler.
print('ici le change notifier, coucou !');
print( WordPair.random());
current = WordPair.random();
notifyListeners(); // Il faut absolument dire aux abonnés qu'on a changé un truc (concrètement, va voir dans sa liste de callback).
}
}
Et enfin le widget "page principale" :
class MyHomePage extends StatelessWidget // Un autre Widget invisible qui réunit les Widgets de la page d'accueil.
@override
Widget build(BuildContext context) { // Sera appelé quand appState change. Context en entrée fait le lien avec le reste de l'app en donnant le contexte dans lequel est créé cet élément.
var appState = context.watch<MyAppState>(); // On "s'abonne" à MyAppState avec cette variable, mais en vérité c'est l'inverse : cette référence au contexte vient s'enregistrer en tant que callback dans une liste du ChangeNotifier.
// Pour chaque élément, dans le framework on a un "Widget build() => widget.build(this);", et "this" représente le contexte de l'élément en cours.
return Scaffold( // Un widget (Stateful -car mémorise "le menu latéral est-il ouvert", "le clavier déplié" etc- mais osef). Va générer automatique des widgets visuels selon la config qu'on lui donne.
backgroundColor: Color.fromARGB(255, 0, 255, 0), // pour configurer le Widget (de type Material) que va générer Scaffold
body: Column( //Encore un widget invisible, définit les layout.
children: [ // Scaffold va générer un widget pour le font mais on peut lui donner nos propres widgets
Text('Une idée au pif :'), //Ce Widget là sera visible, il utilise RichText qui est un RenderObjectWidget.
Text(appState.current.asLowerCase),
ElevatedButton( // Un widget stateful (est-il pressé, survolé...)
onPressed: () {
print('button pressed!');
appState.coucouIciLeChangeNotifier(); //On appelle le state contenu par le widget ChangeNotifierProvider, qui est notre arrière grand père.
},
child: Text('Next'),
),
],
),
);
}
}
L'arbre sera le suivant :
-MyApp (Le widget racine)
--ChangeNotifierProvider (Un Widget invisible, contient l'état "MyAppState")
---MaterialApp (Widget pour configurer le visuel).
----MyHomePage (Widget invisible qui regroupe les widgets de la home page).
-----Scaffold
------(plein de widgets "par défaut" genre la direction du texte, le fond...)
------Column
------Text
------Text
------ElevatedButton
-------Text
Quand on clique sur le boutton ElevatedButton, il va déclencher la fonction contenue dans onPressed, faire le print et appeler le parent.
Il y a 3 arbres dans Flutter, donc on peut détruire un Widget parent :
-arbre des widgets (éphémère, contient la description de l'UI, c'est une recette)
-arbre des éléments (persistent, contient les States et le BuildContext -la position dans l'arbre et permet de chercher des éléments, se souvient de ce qui existait avant)
-arbre du rendu (gère le layout, le rendu, les interactions).
Constructeurs
Il y a plusieurs types de constructeurs en Dart.
Pour un widget on utilise général le constructeur qui fonctionne avec const :
class GrosseCarte extends StatelessWidget {
final WordPair pair; // un attribut, sera alimenté par le constructeur.
const GrosseCarte({ // Le constructeur !
super.key, // c'est une sorte d'id unique au composant.
required this.pair, //Ici c'est un paramètre qu'on donne en entrée du constructeur, sera stocké dans this.pair.
//En Java on aurait genre "this.pair=param"... sauf que tout est implicite ici.
});
@override
Widget build(BuildContext context) {
return Text(pair.asLowerCase); //
}
}
Test en debug sur son téléphone sans installer Android Studio
-télécharger les https://developer.android.com/studio#command-tools
-rajouter un dossier "latest" avant bin, le chemin doit être "C:\outils\androidsdk\cmdline-tools\latest\bin".
-faire
.\sdkmanager.bat "platforms;android-36" "build-tools;36.0.0" "build-tools;28.0.3" "platform-tools"
-vérifier que tout est vert en faisant
flutter doctor, si besoin taper ce que dit le docteur (licenses...).
-connecter son téléphone en mode usb debugging (faut être en mode dev)
-faire
flutter run, ça va être long car il va télécharger plein de libs dans .gradale.