0% ont trouvé ce document utile (0 vote)
306 vues189 pages

Flutter de Junior À Senior

Transféré par

soumahoro.ss50
Copyright
© © All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd
0% ont trouvé ce document utile (0 vote)
306 vues189 pages

Flutter de Junior À Senior

Transféré par

soumahoro.ss50
Copyright
© © All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd

Flutter de Junior à Senior

📘🔗UnRejoins
ebook 100% gratuit proposé par Aide en Informatique​
notre communauté de passionnés du numérique !

●​ 📘 Tous nos ebooks gratuits sont disponibles ici : Groupe WhatsApp​

●​ 🌐 Notre site web : itinnovdesign.montechnicien.online​

●​ 📱 Notre page Facebook : Aide en Informatique​

👍💬Like nos publications pour nous encourager​

📤 Commente pour poser tes questions : on est là pour y répondre !​

communauté des passionnés d’informatique 💻


Partage nos contenus dans tes groupes et sur ta page : aide-nous à faire grandir la

🙏 Merci pour ton soutien. Grâce à toi, on continue à produire des ressources de qualité,
accessibles à tous.
📘 Flutter de Junior à Senior
🧭 PARTIE 1 – Premiers pas dans le monde du développement mobile
Chapitre 1 – Bienvenue dans le monde du mobile : pourquoi apprendre Flutter ?

●​ C’est quoi une application mobile ?​

●​ Flutter vs les autres technologies (Java/Kotlin, Swift, React Native…)​

●​ Pourquoi Flutter est-il un bon choix pour débuter ?​

●​ Le futur de Flutter (mobile, web, desktop…)​

Chapitre 2 – Comment fonctionne Flutter ? Une vue d’ensemble simple

●​ Flutter = moteur + widgets + code Dart​

●​ Architecture d’une app Flutter​

●​ La magie du hot reload​

●​ L’analogie du théâtre : acteur (widget), script (Dart), scène (UI)​

Chapitre 3 – Préparer sa boîte à outils de développeur Flutter

●​ Matériel recommandé (RAM, disque…)​

●​ Installer Flutter SDK (étape par étape)​

●​ Installer Dart SDK (si besoin)​

●​ Installer Android Studio (et créer un émulateur)​

●​ Installer VS Code (et extensions Flutter + Dart)​

●​ Test de la première installation (commande flutter doctor)​

●​ Développer sur Android/iOS/Web : différences et prérequis​

🚀 PARTIE 2 – Crash Course Dart : le carburant de Flutter


Chapitre 4 – Introduction à Dart : apprendre en pensant comme un humain

●​ Pourquoi Dart et pas JavaScript ?​


●​ Dart et le typage simple​

Chapitre 5 – Les bases de Dart expliquées simplement

●​ Variables et types​

●​ Conditions et opérateurs​

●​ Boucles (for, while, foreach)​

●​ Fonctions​

●​ Collections (List, Map, Set)​

●​ Null safety expliqué avec une analogie du frigo vide​

Chapitre 6 – Programmation orientée objet avec Dart

●​ Classes et objets​

●​ Constructeurs​

●​ Héritage​

●​ Encapsulation​

●​ Interfaces et mixins​

Chapitre 7 – Aller plus loin avec Dart

●​ Lire et écrire dans des fichiers​

●​ Faire des requêtes HTTP (GET, POST)​

●​ Gérer le JSON​

●​ Utiliser des packages​

🏗️ PARTIE 3 – Apprendre Flutter en construisant une app réelle


Projet choisi : TaskZen – Une app de gestion de tâches intelligente

Une app qui permet de créer, modifier, supprimer des tâches, les
marquer comme terminées, avec stockage local puis cloud (Firebase),
gestion d’utilisateurs, notifications, etc. On l’enrichit progressivement.
Chapitre 8 – Créer notre première app Flutter : TaskZen v1

●​ Démarrer une nouvelle app Flutter​

●​ Structure d’un projet Flutter​

●​ Hello World et explication du widget MaterialApp​

●​ Utilisation des widgets de base : Text, Column, Row, Container, Button​

Chapitre 9 – Créer des interfaces modernes avec les Widgets

●​ Comprendre les widgets stateless vs stateful​

●​ Ajout de champs de texte et boutons​

●​ Gérer les événements et la navigation​

●​ Créer une interface de création de tâche​

Chapitre 10 – Navigation dans Flutter

●​ Navigation simple (Navigator.push)​

●​ Passer des données entre écrans​

●​ Gestion des routes nommées​

●​ Splash screen, onboarding, deep links​

Chapitre 11 – Gérer l’état dans Flutter

●​ C’est quoi l’état ?​

●​ setState vs solutions modernes​

●​ Introduction à Provider​

●​ Gérer l’état des tâches avec Provider​

Chapitre 12 – Sauvegarder localement avec SQLite

●​ Pourquoi persister les données ?​

●​ Utiliser sqflite pour stocker les tâches​


●​ CRUD complet (Create, Read, Update, Delete)​

●​ Adapter l’UI à la base de données​

Chapitre 13 – Travailler avec les flux (Streams)

●​ Comprendre les Stream et Future​

●​ Intégration dans Flutter avec StreamBuilder et FutureBuilder​

●​ Exemple : afficher une liste dynamique de tâches​

Chapitre 14 – Connexion à une API distante

●​ Utiliser http pour envoyer/recevoir des données​

●​ Parser une réponse JSON​

●​ Afficher des données récupérées depuis une API​

☁️ PARTIE 4 – Devenir Senior : maîtriser Flutter comme un pro


Chapitre 15 – Intégration Firebase (Cloud Firestore)

●​ Créer un projet Firebase​

●​ Connexion de l’app à Firebase​

●​ Stockage des tâches dans Firestore​

●​ Authentification (email + Google)​

Chapitre 16 – Notifications et interactions avancées

●​ Notifier l’utilisateur (Firebase ou plugin local)​

●​ Créer des rappels de tâches​

●​ Thèmes sombres/clairs et personnalisation UI​

Chapitre 17 – Tester son application Flutter

●​ Pourquoi écrire des tests ?​


●​ Test unitaire​

●​ Test widget​

●​ Test d’intégration​

Chapitre 18 – Optimiser et déployer son app

●​ Nettoyer et optimiser le code​

●​ Gérer les permissions (Android et iOS)​

●​ Générer un APK ou AAB​

●​ Publier sur le Play Store / App Store​

🎓 PARTIE 5 – Aller plus loin


Chapitre 19 – Architecture propre avec Flutter

●​ Clean Architecture simplifiée​

●​ Séparation UI, logique métier, données​

Chapitre 20 – Flutter au-delà du mobile

●​ Déployer pour le Web​

●​ Déployer pour le bureau (desktop)​

●​ Flutter vs autres technologies​

📦 Annexes
●​ Glossaire des termes Flutter/Dart​

●​ Ressources gratuites pour apprendre Flutter​

●​ Astuces et raccourcis utiles​

●​ Exercices supplémentaires pour s’entraîner​


●​ FAQ : questions fréquentes des débutants​
🧭 PARTIE 1 – Premiers pas dans le
monde du développement mobile
📱 Chapitre 1 – Bienvenue dans le monde du mobile :
pourquoi apprendre Flutter ?

🚀 C’est quoi une application mobile ?


Imagine ton smartphone comme une maison. Les applications mobiles, ce sont les
pièces de cette maison : la cuisine pour cuisiner (YouTube), le salon pour te
détendre (Netflix), la chambre pour dormir (Horloge/Alarme), le bureau pour travailler
(Gmail, WhatsApp Business)...​
Chaque application a une fonction précise et un design adapté pour qu’on s’y
sente bien et qu’on accomplisse quelque chose facilement.

Une application mobile est donc un logiciel spécialement conçu pour fonctionner sur
un appareil mobile : téléphone ou tablette. Elle peut être native (faite spécialement
pour Android ou iOS), ou cross-platform (une seule app qui tourne sur plusieurs
systèmes comme Android et iOS).
🤔 Flutter vs les autres technologies
Pour créer une app mobile, les développeurs ont plusieurs outils dans leur coffre à
outils. Voici une petite comparaison imagée :
Technologie C’est comme… Plateformes Langage
utilisé

Java/Kotlin Construire une maison uniquement Android Java,


(Android) en béton pour Android uniquement Kotlin

Swift (iOS) Construire une maison en bois haut iOS Swift


de gamme pour iPhone uniquement

React Native Utiliser une imprimante 3D pour Android et iOS JavaScript


créer une maison pour deux
quartiers différents

Flutter Créer une maison modulaire avec Android, iOS, Dart


des briques personnalisables et Web, Desktop
durables pour tous les quartiers

Flutter, contrairement aux autres, te permet de construire une seule app qui
fonctionne partout (Android, iOS, web, desktop). Tu gagnes du temps, tu dépenses
moins d’énergie, et tu peux mettre à jour plus facilement ton application.

🧠 Pourquoi Flutter est-il un bon choix pour débuter ?


Apprendre Flutter, c’est un peu comme apprendre à cuisiner avec un robot
multifonction :

●​ Il fait tout : il coupe, mélange, cuit, et même nettoie parfois !​

●​ Il est simple à prendre en main, même pour un débutant.​

●​ Il est moderne, utilisé dans de vraies entreprises (Google, BMW, Alibaba…)​

●​ Il a une grosse communauté : si tu bloques, il y a toujours quelqu’un qui a


eu le même problème que toi.​

●​ Il te permet de voir rapidement ce que tu codes (merci au Hot Reload !)​

En résumé :

Tu n’as jamais codé ? Aucun souci. Flutter est un excellent point de


départ.

🔮 Le futur de Flutter : mobile, web, desktop…


Flutter, c’est comme un couteau suisse du développeur moderne.
●​ Tu veux faire une app pour Android ? Facile.​

●​ Tu veux la même app pour iPhone ? Pas de souci.​

●​ Tu veux qu’elle fonctionne aussi dans un navigateur web ? Flutter peut.​

●​ Et pour un ordinateur Windows, Mac ou Linux ? Flutter aussi.​

Avec Flutter, tu investis dans une technologie qui va durer, car elle évolue vers
un monde où le même code pourra créer une application pour tous les appareils.

✅ Petit récap simple


Question Réponse simplifiée

C’est quoi une app Un logiciel qui tourne sur ton téléphone.
mobile ?

Pourquoi apprendre C’est simple, rapide, moderne, et tu codes une fois pour
Flutter ? tous les appareils.

Flutter vs autres ? Flutter est comme une maison modulaire : un seul plan,
plusieurs destinations (Android, iOS, Web, etc.).

Est-ce que Flutter a Oui, un grand ! Il s’étend au web, desktop, et plus encore.
un avenir ?
Chapitre 2 – Comment fonctionne Flutter ? Une vue
d’ensemble simple

🧠 2.1 Flutter = moteur + widgets + code Dart


🧩 C’est quoi exactement Flutter ?
Flutter, c’est comme une voiture bien conçue. Pour qu’elle roule et que tu puisses
aller quelque part, il faut trois éléments essentiels :

1.​ Le moteur : c’est le cœur de la machine, celui qui fait tourner tout le reste.
Dans Flutter, c’est ce qu’on appelle le Flutter Engine. Il s’occupe de
dessiner ce que tu vois à l’écran, de gérer les animations, les boutons, les
gestes (comme les clics, les scrolls)…​

2.​ Les widgets : ce sont les briques de construction de ton application.


Chaque bouton, chaque texte, chaque image, chaque menu… tout est un
widget. Flutter te fournit une énorme boîte de LEGO prête à l’emploi.​

3.​ Le code Dart : c’est toi, le développeur, qui écris le script de ton app avec le
langage Dart. C’est lui qui dit comment organiser les widgets, comment
réagir quand l’utilisateur clique, comment afficher les données, etc.​

🛠️ Une analogie simple : construire une maison


Élément Dans une maison Rôle
Flutter
Flutter Engine Le béton + l’électricité Fait tourner l’ensemble, donne vie

Widgets Les meubles, portes, Ce que l’utilisateur voit et utilise


murs

Code Dart Les plans de Organise tout et dit comment ça


construction fonctionne

🔁 Comment ça marche ensemble ?


Quand tu crées une app Flutter, voici ce qui se passe :

1.​ Tu écris ton code Dart → tu dis à Flutter quoi afficher et comment réagir.​

2.​ Flutter utilise ce code pour assembler des widgets (comme construire ta
maison avec des LEGO).​

3.​ Le Flutter Engine prend tous ces widgets, les dessine à l’écran, gère les
animations, et fait vivre ton app.​

Tu peux t’imaginer comme un chef d’orchestre :

●​ Le langage Dart, c’est ta baguette.​

●​ Les widgets, ce sont les musiciens (boutons, images, textes…).​

●​ Le moteur Flutter, c’est la salle de concert avec les haut-parleurs, les


éclairages et les amplis.​

Sans toi, rien ne fonctionne. Mais avec toi, tout s’assemble harmonieusement.

🏗️ 2.2 Architecture d’une app Flutter


Quand on parle d’architecture dans une application Flutter, c’est comme parler du
plan d’une maison ou du schéma électrique d’un immeuble. C’est la manière
dont les différentes parties de l’application sont organisées et interconnectées
pour fonctionner correctement.

🧱 1. Les fondations : main(), le point de départ


En Flutter, tout commence avec une fonction main(). C’est la porte d’entrée de
ton application, le tout premier endroit où ton code s’exécute.​
Un peu comme le coup de sifflet de départ d’un match, ou le bouton ON d’une
télé.

void main() {​
runApp(MyApp());​
}

Ici, on dit à Flutter : « Démarre mon application avec la classe MyApp. »

🧩 2. La classe MyApp : ton app en tant que widget


En Flutter, toute ton application est un widget, et même chaque élément de
l’écran est un widget.

Tu crées une classe MyApp (souvent un StatelessWidget ou StatefulWidget)


qui décrit la première structure visible de ton application :

class MyApp extends StatelessWidget {​


@override​
Widget build(BuildContext context) {​
return MaterialApp(​
title: 'MonAppFlutter',​
home: AccueilPage(),​
);​
}​
}

●​ MaterialApp est un widget de base qui fournit une structure d’app mobile
moderne (navigation, thèmes, etc.).​

●​ AccueilPage() est le widget qui sera la première page affichée.​

🧠 3. La hiérarchie des widgets (ou "l’arbre des widgets")


Flutter fonctionne comme un arbre généalogique :

●​ Il y a un widget parent (par exemple, MaterialApp),​


●​ qui contient un widget enfant (AccueilPage),​

●​ qui peut contenir d’autres widgets (Text, Column, Button, etc.),​

●​ et ainsi de suite…

➡️ Ce système s’appelle l’arbre des widgets, et c’est la base de la construction


visuelle en Flutter.

Imagine que tu construis une poupée russe : tu ouvres une grande poupée (widget
parent), à l’intérieur il y en a une autre, puis une autre…​
Chaque élément de l’écran est imbriqué dans un autre.

🧬 Résumé simplifié de l’architecture Flutter


Élément Rôle

main() Démarre l’app

runApp(MyAp Lance ton widget principal


p())
MyApp Racine de l’app, configuration de
base

MaterialApp Fournit structure, thème,


navigation

home: Définit la première page à afficher

Widgets Construisent l’interface utilisateur


enfants (UI)

⚡️ 2.3 – La magie du hot reload


🧨 C’est quoi le hot reload ?
Imagine que tu es en train de peindre une fresque murale.​
Normalement, si tu veux changer une petite partie (comme un nuage ou un visage),
tu dois :

1.​ Reprendre la peinture depuis le début,​

2.​ Refaire tout le mur,​

3.​ Attendre que ça sèche…​

😩 C’est long, fatigant, et frustrant.


Mais avec Flutter et le hot reload, c’est comme si tu pouvais modifier juste le
nuage, instantanément, sans refaire tout le reste.
🔄 Hot reload vs Hot restart
Voici une analogie pour bien comprendre la différence :

Action Explication simple Analogie

Hot Recharge uniquement ce qui a Tu changes la couleur d’un


reload changé dans le code, sans perdre bouton sur ton appli, et il est mis
l’état de l’application (les données, à jour directement sans fermer
les clics, les pages ouvertes…). l’appli ni revenir au début.

Hot Redémarre l’application depuis zéro Tu redémarres le jeu vidéo mais


restart mais sans recompilation complète. tu ne réinstalles pas la console.

💡 À quoi ça sert concrètement ?


●​ Gagner du temps
modification.​
🚀 : tu ne redémarres pas toute l’application à chaque
●​ Tester plus vite tes changements : changer une couleur, un texte, la position
d’un bouton… et voir le résultat quasiment instantanément.​

●​ Améliorer ta productivité et rester concentré.​


🔧 Exemple concret
Tu veux changer le texte d’un bouton de "Clique ici" à "Appuie là" :

TextButton(​
onPressed: () {},​
child: Text('Clique ici'),​
)

Tu modifies dans le code :

TextButton(​
onPressed: () {},​
child: Text('Appuie là'),​
)

Tu fais un hot reload, et hop ✨ , le changement apparaît en moins d'une seconde,


sans fermer l'app, sans revenir à l’écran d’accueil.

📌 Ce qu’il faut retenir


Le hot reload est l’un des super-pouvoirs de Flutter. Il te permet de
modifier ton app en temps réel pendant que tu la développes. C’est
comme corriger une faute d’orthographe dans un livre pendant qu’il
est en train d’être lu à haute voix.

🎭 2.4 – L’analogie du théâtre : acteur (widget), script


(Dart), scène (UI)

salle de théâtre 🎭
Pour bien comprendre comment Flutter fonctionne, imagine que tu es dans une
.

Tu regardes une pièce. Il y a :

●​ 🎬 Un script que les acteurs suivent,​


●​ 👨‍🎤 Des acteurs qui jouent leur rôle,​

●​ 🎤 Une scène sur laquelle ils évoluent.​


Eh bien, dans une application Flutter, c’est exactement pareil ! Voici la
correspondance :

Théâtre 🎭 Flutter 📱
Le script Le code Dart

Les acteurs Les widgets Flutter

La scène de L’interface utilisateur


théâtre (UI)

📜 1. Le script (le langage Dart)


Le script, c’est ce que l’auteur a écrit pour guider les acteurs.​
Dans Flutter, ce script, c’est le langage Dart. C’est dans ce langage que tu écris
tout ton code.

C’est lui qui dit :

●​ quoi afficher,​

●​ comment réagir à un clic,​

●​ quelle couleur mettre,​

●​ etc.​

👉 Dart = langage de direction.


🎭 2. Les acteurs (les widgets)
Les widgets, ce sont les acteurs de ton application.​
Chaque élément visible (ou invisible) dans ton app est un widget :

●​ un bouton,​

●​ un texte,​

●​ une image,​

●​ une boîte,​

●​ une animation…​
Même des choses abstraites comme la marge, l’alignement, ou l’espace vide…
sont aussi des widgets !​
Oui oui, même le vide est un acteur ! 😄
👉 Les widgets suivent les instructions du script Dart pour "jouer leur rôle".
🎤 3. La scène (l’interface utilisateur – UI)
Enfin, la scène, c’est ce que le spectateur voit.

Dans Flutter, c’est l’écran de ton téléphone.​


C’est là que les widgets (acteurs) jouent leur rôle (apparition, interaction,
animation…), suivant le script écrit en Dart.

👉 L’UI = la scène de théâtre où tout se passe.


🎯 Résumé imagé
Flutter, c’est une pièce de théâtre interactive où :

●​ Le script est écrit en Dart 🖊️,​


●​ Les acteurs sont des widgets 🎭,​
●​ Et la scène, c’est ton écran 📱.​

Et toi, le développeur, tu es le metteur en scène ! 🎬 ​


Tu diriges la pièce avec ton code. Tu choisis les acteurs, tu écris les répliques, tu
règles la lumière, la musique, l’ambiance… et Flutter se charge de tout exécuter, vite
et proprement.
Chapitre 3 – Préparer sa boîte à outils de développeur
Flutter

🧰 3.1 – Introduction : Préparer sa boîte à outils de


développeur Flutter
Avant de construire une maison, il faut d’abord… une boîte à outils
Des marteaux, des tournevis, une perceuse, des clous, etc.​
🧰, non ?​
Eh bien, développer une application Flutter, c’est pareil.

🛠️ Tu vas devoir assembler les bons outils pour travailler proprement,


efficacement et sans maux de tête.

Ce chapitre est là pour t’aider à t’équiper :

●​ Ton ordinateur est ton chantier 🖥️,​


●​ Tu vas y installer tout ce qu’il faut pour coder en Dart et construire des apps
Flutter,​

●​ On fera tout étape par étape, que tu sois sur Windows, macOS ou Linux.​

📦 Qu’est-ce qu’on va installer ensemble ?


Voici les ingrédients de ta future boîte magique :

1.​ Flutter SDK : le cœur du framework Flutter.​

2.​ Dart SDK : le langage de programmation utilisé par Flutter.​

3.​ Android Studio : un outil pour émuler un téléphone virtuel (émulateur).​

4.​ Visual Studio Code : un éditeur de texte léger pour coder plus rapidement.​

5.​ Les extensions Flutter et Dart pour Visual Studio Code.​

6.​ Commandes de vérification comme flutter doctor pour s’assurer que


tout fonctionne.​

🎯 Objectif de ce chapitre
À la fin de ce chapitre, tu seras prêt à :
●​ Créer ta première app Flutter ✅,​
●​ Lancer un émulateur (ou brancher ton téléphone) ✅,​
●​ Voir ton premier bouton apparaître à l’écran ✅.​
Même si tu n’as jamais rien codé de ta vie, on va le faire ensemble, pas à pas.​
Et si tu es sur un PC peu puissant, pas de panique, je te montrerai aussi des
solutions plus légères.

🧠 3.2 – Matériel recommandé (RAM, disque…)


Avant de se lancer dans le développement mobile avec Flutter, il faut savoir si ton
ordinateur est prêt à encaisser la charge.

Développer une application mobile, c’est comme faire tourner un mini téléphone
dans ton PC. Et ça peut être lourd si ta machine est un peu fatiguée.

🖥️ Les prérequis de base


Voici le minimum recommandé pour un bon confort de travail :

Élément Recommandé Explication terre à terre

💾 RAM 8 Go minimum (16


Go idéal)
Flutter + Android Studio + émulateur =
ça consomme. Avec 4 Go, tu risques
de ramer comme un vieux camion
en montée.

💽 Disque dur SSD obligatoire (au


moins 10 Go
Un SSD, c’est comme une autoroute
pour tes fichiers. Un HDD classique =
d’espace libre) embouteillage garanti.

🧠 Processeur Quad-Core 64-bit Plus ton processeur est rapide, plus


Flutter compile vite et réagit au hot
reload.

📱 Smartphone
Android/iOS
Facultatif mais utile Si ton PC est lent, tu peux brancher
ton propre téléphone au lieu d’utiliser
(optionnel) un émulateur.

🛠️ Cas pratiques selon ton PC


🟢 Tu as un PC récent (8+ Go de RAM, SSD, bon processeur)
✅ Tu peux tout installer : Flutter, Android Studio, VS Code, et utiliser les émulateurs
sans problème.

🟡 Tu as un PC moyen (4 Go de RAM, disque dur classique)


⚠️👉TuUtilise
peux installer Flutter + VS Code, mais évite Android Studio + émulateur.​
ton propre téléphone comme appareil de test, ou Visual Studio Code
avec un terminal.

🔴 Tu as un vieux PC (moins de 4 Go de RAM)


😢👉TuUtilise
risques de souffrir. Mais tout n’est pas perdu !​
un éditeur en ligne (comme DartPad ou Zapp.run pour Flutter Web)
juste pour apprendre, ou pense à upgrader un peu ton matériel.

🧑‍🔧 Résumé
Flutter peut tourner sur une petite machine, mais plus tu avances, plus
les besoins augmentent.​
Il vaut mieux un petit PC bien optimisé (SSD) qu’un gros PC lent avec
disque dur classique.

🧰 3.3 – Installer Flutter SDK (étape par étape)


Le Flutter SDK, c’est comme la boîte à outils magique qui permet de construire
ton application Flutter.​
Sans lui, impossible de poser la première brique de ton app.

🧐 C’est quoi un SDK ?


SDK = Software Development Kit​
En gros, c’est un kit de survie pour développeur Flutter. Il contient
tous les outils de base :

●​ le moteur de rendu (pour afficher les widgets),​

●​ le compilateur (pour transformer ton code en application),​

●​ les outils de debug,​

●​ et les commandes (comme flutter run, flutter doctor, etc).​


📦 Étapes d’installation de Flutter SDK (sur Windows)
Voici les étapes pour Windows (on fera les autres OS ensuite si tu
veux).

🔽 Étape 1 – Télécharger le SDK Flutter


1.​ Va sur le site officiel : 👉 https://flutter.dev​
2.​ Clique sur “Get started”, puis sélectionne Windows.​

3.​ Clique sur “Download Flutter SDK”. Tu vas télécharger un fichier .zip.​

📝 Conseil : Télécharge le SDK dans un dossier facile à retrouver, comme


C:\flutter.

📁 Étape 2 – Extraire le fichier ZIP


1.​ Clique droit sur le .zip téléchargé, choisis “Extraire tout”.​

2.​ Choisis le dossier d’extraction : par exemple C:\flutter.​

⛔ Ne mets pas Flutter dans un dossier avec des espaces (comme C:\Mes
documents\Flutter), ça peut causer des bugs.

🛤️ Étape 3 – Ajouter Flutter à la variable d’environnement PATH


Pour pouvoir utiliser les commandes Flutter (flutter doctor, flutter run,
etc.), il faut dire à Windows où est Flutter.

1.​ Clique droit sur le menu démarrer → Système → Paramètres système


avancés​

2.​ Clique sur Variables d’environnement​

3.​ Dans la section “Variables système”, clique sur Path → Modifier​

Clique sur Nouveau et ajoute :​



C:\flutter\bin
4.​
5.​ Clique sur OK → OK → OK ✅​

🧪 Étape 4 – Vérifier si Flutter est bien installé


1.​ Ouvre l’invite de commandes (tape cmd dans le menu démarrer)​

Tape :​

flutter doctor

2.​

🔍Il vaCeaussi
petit docteur Flutter va scanner ton système et te dire si tout va bien.​
te dire s’il manque des outils (comme Android Studio, Git ou un
émulateur).

✅ À ce stade...
Tu as maintenant Flutter installé sur ton PC ​🎉
Tu peux déjà créer ton premier projet (même si on va encore installer Dart, Android
Studio ou VS Code après).

🧱 3.4 – Installer Dart SDK (si besoin)


🤔 D’abord… C’est quoi Dart ?
Avant tout, comprenons un truc simple :

Flutter, c’est l’outil pour construire l’appli,​


Dart, c’est la langue dans laquelle tu parles à Flutter.

🧠 Analogie simple :
●​ Si Flutter est un maçon, alors Dart est la langue dans laquelle tu lui
donnes les instructions.​

●​ Sans Dart, Flutter ne comprend pas ce que tu veux faire.​

🧑‍🏫 Est-ce que Dart est déjà installé avec Flutter ?


🟢Quand
Bonne nouvelle :​
tu installes Flutter SDK, le Dart SDK est inclus dedans automatiquement
!​
Donc tu n’as rien à faire de plus pour avoir Dart sur ton ordi.

✅ Pour vérifier que Dart est bien là :


1.​ Ouvre un terminal (cmd)​

Tape :​

dart --version
ou
flutter doctor

2.​ → Si Dart est listé, tout est OK ✅​

🔧 Quand est-ce qu’on a besoin d’installer Dart séparément ?


Dans 99% des cas, tu n’en as pas besoin.

Mais tu pourrais vouloir installer Dart séparément si :

●​ tu veux faire du Dart pur sans Flutter, pour des scripts ou serveurs,​

●​ tu veux tester du code Dart en dehors de Flutter.​

📦 Comment installer Dart séparément (facultatif)


Voici juste pour info, si un jour tu veux travailler avec Dart seul :

1.​ Va sur le site : https://dart.dev/get-dart​

2.​ Clique sur “Get Dart” et choisis ton système (Windows, macOS ou Linux)​

3.​ Suis les étapes comme pour Flutter (téléchargement, ajout au PATH, test via
dart --version)​

🎯 Conclusion
●​ ✔️ Si tu fais du Flutter, pas besoin d’installer Dart à part.​
●​ ✔️ Dart est inclus avec Flutter, et prêt à l’emploi.​

●​ ✔️ Tu peux déjà commencer à écrire du code dès maintenant.​

🧰 3.5 – Installer Android Studio (et créer un émulateur)


📦 Pourquoi Android Studio ?
Android Studio, c’est comme la grosse boîte à outils officielle d’Android.​
Même si on ne va pas coder directement dedans, on l’installe pour deux raisons
principales :

1.​ Il contient les outils Android nécessaires à Flutter pour créer et tester des
apps Android (comme les SDKs, le système de build, etc.)​

2.​ Il te permet de créer un téléphone virtuel (émulateur) pour tester ton


application.​

🧑‍🔧 Étape 1 : Télécharger Android Studio


👉
1.​ Va sur le site :​
https://developer.android.com/studio​

2.​ Clique sur Download Android Studio​

3.​ Installe le logiciel comme n’importe quel programme classique :​

○​ Suivant → Suivant → Installer → Terminer​

○​ Laisse toutes les options par défaut, sauf si tu sais ce que tu fais.​

🔧 Étape 2 : Configuration initiale


Au premier lancement :

●​ Choisis Standard Setup​

●​ Laisse Android Studio télécharger les composants nécessaires​


●​ Tu peux ignorer l’étape où il te demande de créer un projet (on ne va pas
l’utiliser directement)​

🛠️ Étape 3 : Installer le SDK Android (si ce n’est pas déjà fait)


Normalement, Android Studio l’installe automatiquement.​
Mais pour vérifier :

1.​ Clique sur "More Actions" en bas → puis "SDK Manager"​

2.​ Vérifie que tu as une version de SDK installée (par exemple, Android API 33
ou 34).​
Si ce n’est pas le cas, coche une version stable et clique sur Apply.​

3.​ Vérifie aussi que le "Android SDK Command-line Tools" est coché.​

📱 Étape 4 : Créer un émulateur Android


Un émulateur, c’est comme un faux téléphone dans ton ordinateur. Très utile si tu
n’as pas de vrai téléphone ou si tu veux tester plusieurs tailles d’écran.

Voici comment le créer :

1.​ Ouvre Android Studio​

2.​ Clique sur “More Actions” → “Virtual Device Manager”​

3.​ Clique sur “Create Virtual Device”​

4.​ Choisis un modèle de téléphone (Pixel 6 par exemple) → Next​

5.​ Choisis une image système (version Android) à télécharger → Next​

6.​ Donne un nom → Finish​

🟢 Une fois terminé, tu verras ton appareil virtuel dans la liste.


✅ Clique sur ▶ (Play) pour démarrer l’émulateur → Un téléphone virtuel s’ouvre
dans une fenêtre !

⚠️ Astuce pour les PC avec peu de RAM


●​ Un émulateur peut consommer beaucoup de ressources (2 à 4 Go RAM).​

●​ Si ton PC a moins de 8 Go de RAM, il vaut mieux :​

○​ Tester ton app directement sur un vrai téléphone Android (connecté


par USB avec le mode développeur activé)​

○​ Ou utiliser VS Code avec de vrais appareils (voir section suivante)​

🖥️ 3.6 – Installer VS Code (et extensions Flutter + Dart)


🎯 Pourquoi VS Code ?
VS Code (Visual Studio Code), c’est un éditeur de texte super intelligent.​
Il est :

●​ léger✅​
●​ rapide ✅​

●​ personnalisable ✅​
●​ gratuit ✅​
Et surtout… il adore travailler avec Flutter et Dart grâce à des extensions
officielles.

🧠 Analogie rapide :
Si Flutter est le maçon, Dart est la langue, alors VS Code est ton
bloc-notes magique avec des assistants qui te corrigent et te donnent
des idées au fur et à mesure !

🧑‍💻 Étape 1 : Télécharger et installer VS Code


1.​ Va sur https://code.visualstudio.com​

2.​ Clique sur Download for Windows / macOS / Linux selon ton système​

3.​ Lance le fichier d’installation et clique sur Suivant jusqu’à Installer​


🧩 Étape 2 : Ajouter les extensions Flutter & Dart
Une fois VS Code installé :

1.​ Ouvre VS Code​

2.​ Clique sur l’icône des Extensions à gauche (ou Ctrl + Shift + X)​

3.​ Recherche :​

○​ Flutter → Clique sur Installer​

○​ (Il va automatiquement installer aussi Dart, c’est normal)​

4.​ Patiente pendant le téléchargement​

🟢 Une fois fait, tu verras une nouvelle option Flutter dans les menus VS Code.
🧪 Étape 3 : Vérifier que tout est prêt
1.​ Ouvre un nouveau terminal dans VS Code (Ctrl + )​

Tape :
flutter doctor

⚠️
2.​ L’outil va scanner ton environnement et te dire ce qui est prêt
manque ​
✅ et ce qui
Exemple :

[✓] Flutter (Channel stable, x.x.x, on Windows, locale fr-FR)​


[✓] Android toolchain - develop for Android devices​
[✓] VS Code (version x.x.x)​
[✓] Connected device

Si tout est vert ✅ → Tu es prêt !


👨‍🔧 Astuce : changer la langue de VS Code
Par défaut, VS Code peut être en anglais.​
Tu veux le mettre en français ?

1.​ Va dans les Extensions​

2.​ Cherche French Language Pack for Visual Studio Code​

3.​ Installe-le puis redémarre VS Code​

🎉 Bravo ! Tu as maintenant un environnement complet pour développer avec


Flutter !


Tu as :​

✅ Flutter installé​



Dart installé​
Android Studio pour les émulateurs​
VS Code pour écrire ton code

🩺 3.7 – Test de la première installation avec flutter


doctor
👨‍⚕️ C’est quoi ce “flutter doctor” ?
Imagine que ton ordinateur est un patient, et que flutter doctor, c’est le
médecin qui fait un bilan de santé complet pour voir s’il est prêt à développer des
apps Flutter.

Il va examiner :

●​ si Flutter est bien installé​

●​ si Dart est là​

●​ si Android Studio est bien configuré​

●​ si un émulateur est prêt​

●​ si VS Code est bien détecté​

●​ et s’il manque quelque chose​


✅ Comment utiliser flutter doctor
1.​ Ouvre ton terminal :​

○​ Sur Windows : ouvre le terminal dans VS Code (`Ctrl + ``) ou via le


menu Démarrer > "Invite de commande" / PowerShell​

○​ Sur macOS/Linux : Terminal classique​

Tape simplement :​

flutter doctor

2.​ Attends quelques secondes, tu verras une sortie comme celle-ci :​

Doctor summary (to see all details, run flutter doctor -v):​
[✓] Flutter (Channel stable, x.x.x, on Windows, locale fr-FR)​
[✓] Android toolchain - develop for Android devices (Android SDK
x.x.x)​
[✓] Chrome - develop for the web​
[✓] Visual Studio Code (version x.x.x)​
[✓] Connected device (1 available)

🟢 Ce que signifient les symboles


Symbol Signification
e

✓ Tout est OK✅


! Avertissement ⚠️ (ce n’est pas bloquant, mais tu peux
améliorer)

✗ Problème ❌ (il faut régler ce point avant de continuer)

🧠 Astuce : diagnostic détaillé


Si tu veux plus de détails :

flutter doctor -v
Cela te montre les versions précises installées, et où se trouvent les fichiers.

🧯 Que faire si tu vois des erreurs ?


Voici les erreurs fréquentes et comment les corriger :

✗ Android licenses not accepted​


→ Tape cette commande :​

flutter doctor --android-licenses

●​ Accepte tout avec y (yes) lorsqu’on te demande​

●​ ✗ No devices available​
→ Tu dois créer un émulateur Android dans Android Studio (voir section
3.5)​
→ Ou brancher un vrai téléphone Android avec le mode développeur
activé​

●​ ✗ VS Code non détecté​


→ Vérifie que tu as bien installé les extensions Flutter & Dart dans VS
Code​

🎉 Et voilà !
Si la commande flutter doctor t’affiche tout en vert, c’est comme si ton hôpital

🚀
était opérationnel, ton personnel médical prêt, et il ne reste plus qu’à accueillir
ton premier patient = ton premier projet Flutter

🌍 3.8 – Développer sur Android, iOS et Web :


différences et prérequis
📱🌐 Mais… Flutter, ça marche partout ?
Oui, et c’est ce qui le rend magique !

Avec un seul code source Flutter, tu peux créer :

●​ une app Android​

●​ une app iOS​


●​ une app Web​

●​ une app de bureau (Windows, macOS, Linux)​

🧠 Analogie simple :
Flutter, c’est comme un cuisinier qui connaît toutes les cuisines du
monde.​
Tu écris une seule recette (ton code Dart), et Flutter adapte le plat
automatiquement pour chaque culture (Android, iOS, Web…).

📦 1. Développer pour Android


✅ Prérequis :
●​ Android Studio installé (avec Android SDK)​

●​ Un téléphone Android ou un émulateur Android​

🔧 Configuration :
●​ Crée un AVD (émulateur) dans Android Studio​

●​ Active le mode développeur sur ton vrai téléphone si tu veux tester en USB​

🟢 Avantage :
●​ Très simple à tester sur Windows, Linux ou Mac​

🍎 2. Développer pour iOS


⚠️Apple
Tu dois avoir un Mac !​
n’autorise la compilation iOS que sur macOS, car Xcode (l’outil officiel) ne
fonctionne que là.

✅ Prérequis :
●​ Un Mac (macOS)​

●​ Xcode installé (via App Store)​

●​ Un compte Apple développeur (gratuit ou payant pour publier)​


🔧 Test :
●​ Simulateur iOS via Xcode​

●​ iPhone réel via câble (avec certificat de développement)​

🚫 Sur Windows :
●​ Impossible de générer une app iOS sans tricher (Mac en cloud ou
Hackintosh, ce qui est illégal selon Apple)​

🌐 3. Développer pour le Web


✅ Prérequis :
●​ Aucun outil spécial à part un navigateur comme Chrome​

●​ Flutter prend en charge automatiquement le web​

🔧 Test :
●​ Dans VS Code ou Android Studio, tu peux lancer ton app dans Chrome
directement​

🧠 Astuce : si tu as installé Chrome, flutter doctor le détecte comme un


device disponible !

🖥️ 4. Développer pour Desktop (Windows/macOS/Linux)


Flutter permet aussi de faire des apps de bureau, mais c’est encore en évolution
selon les plateformes.

Plateform Développement Conditions


e possible ?

Windows ✅ Oui Flutter + Visual Studio (attention : pas VS Code,


mais Visual Studio Community Edition avec C++)

macOS ✅ Oui Flutter + Xcode + outils Apple

Linux ✅ Oui Flutter + GTK libraries


🧠 Récap' rapide des environnements
Plateforme OS de développement Test possible ?
cible requis

Android Windows/macOS/Linux Oui ✅


iOS macOS uniquement Oui ✅

Web Tous Oui ✅

Desktop Windows/macOS/Linux

Oui (avec config)

🚀 En résumé
Tu peux commencer tranquillement sur Android et Web, même avec un PC
Windows.​
Ensuite, si tu veux aller plus loin (publier sur l’App Store par exemple), tu
envisageras d’utiliser un Mac ou un service cloud.

👏 Bravo ! Tu es maintenant officiellement prêt à entamer la création de ta


première app Flutter !​
PARTIE 2 – Crash Course Dart : le carburant de
Flutter

✨ Chapitre 4 – Introduction à Dart : apprendre en


pensant comme un humain
Avant de construire des apps Flutter, il faut comprendre la langue que Flutter parle.
Et cette langue, ce n’est pas JavaScript, ce n’est pas Python… c’est Dart.

🧠PourPense à Flutter comme à une voiture.​


qu’elle roule, elle a besoin d’un carburant compatible.​
Ce carburant, c’est Dart.

🤔 Pourquoi Dart et pas JavaScript ?


C’est une excellente question, surtout si tu as déjà entendu parler de JavaScript, le
langage n°1 du Web.

Alors pourquoi Flutter n’a-t-il pas choisi JavaScript ? Voici des réponses simples :

🥊 JavaScript ⚡ Dart
Interprété, lent à l'exécution native Compilé en code natif ultra-rapide
Pas pensé pour le mobile Pensé dès le départ pour Flutter et les
apps modernes

Typage très permissif → erreurs à Typage plus strict → erreurs détectées


l'exécution avant de lancer

Syntaxe un peu bizarre parfois (le Syntaxe propre et claire, proche du C,


this, les ==, etc.) Java, Kotlin…

🧪 Analogie simple :
JavaScript, c’est comme une boîte à outils universelle. Elle fait beaucoup
de choses, mais parfois, ce n’est pas très précis.​
Dart, c’est la boîte à outils conçue spécifiquement pour Flutter :
chaque outil est calibré, rapide et simple d’usage.

🎯 Dart, un langage qui pense comme un humain


Dart a été conçu pour être simple, prévisible, lisible, et performant.

Tu ne vas pas passer des heures à chercher pourquoi ton app plante : Dart
t’explique clairement ce qui ne va pas dès que tu écris.​
C’est comme un prof gentil qui te dit :

“Hé, tu as oublié une virgule là”​


“Tu déclares une variable qui pourrait être vide, tu veux gérer ça ?”

🔠 Dart et le typage simple (mais intelligent)


Dart est fortement typé, mais il sait aussi être souple.

Exemple :

int age = 25; // ici, tu dis clairement : "age est un


entier"​
String nom = "Alice"; // ici, tu dis : "nom est une chaîne de
caractères"
Mais tu peux aussi laisser Dart deviner le type :

var ville = "Yaoundé"; // Dart comprend tout seul que c'est une
String

Et pour les valeurs qui peuvent ne pas exister (null), Dart te demande de préciser :

String? pays; // peut être vide ou nul (null safety)

🧠 Note : Dart préfère que tu sois clair sur tes intentions. C’est comme un
assistant qui te dit :

“Je suis prêt à t’aider, mais dis-moi clairement ce que tu veux faire.”

🚀 En résumé
●​ Dart est un langage simple, moderne, rapide et sécurisé​

●​ Il est conçu spécifiquement pour Flutter​

●​ Il est proche du C/Java dans sa syntaxe, donc tu n’es pas perdu​

●​ Il est typé (comme C# ou Java) mais reste flexible​


📚 Chapitre 5 – Les bases de Dart expliquées
simplement
Avant de faire une application mobile, il faut apprendre à parler le langage de
Flutter : Dart.​
Et comme tout langage, on commence par les bases : apprendre à dire « Bonjour
», à poser des questions, à faire des choix, etc.

Ce chapitre est donc un cours accéléré de Dart version simplifiée. Pas besoin
d’être bon en maths, ni de venir d’une école d’ingénieur. On va tout expliquer avec
des analogies de la vie réelle, comme si on apprenait à parler une nouvelle langue
ensemble.

🧠 Imagine que Dart est une langue comme le français, et coder, c’est rédiger une
recette de cuisine :

●​ Tu vas définir des ingrédients (variables),​

●​ Choisir une direction (conditions),​

●​ Répéter des étapes (boucles),​

●​ Et organiser tout ça dans des fonctions (petites recettes indépendantes).​


À la fin, tout ça formera une application.​

5.2 🔡 Variables et types


Une variable, c’est comme une boîte dans laquelle tu ranges quelque chose pour
t’en servir plus tard.

🎁UneAnalogie simple :​
variable, c’est comme une boîte étiquetée dans ton placard :

●​ Une boîte "chaussettes" contiendra des chaussettes,​

●​ Une boîte "CD" contiendra des CD,​

●​ Une boîte "âge" contiendra un nombre.​

En Dart, pour créer une boîte (variable), tu écris :

int age = 25; // boîte nommée 'age' qui contient un nombre


entier​
String nom = "Alice"; // boîte 'nom' qui contient du texte​
double taille = 1.75; // boîte 'taille' qui contient un nombre à
virgule​
bool actif = true; // boîte 'actif' qui contient vrai/faux

💡 Chaque boîte a un type :


●​ int → entier​

●​ double → nombre décimal​

●​ String → texte​

●​ bool → vrai ou faux​

Tu peux aussi laisser Dart deviner le type :

var ville = "Douala"; // Dart comprend que c'est une String​


var temperature = 28.5; // ici, c'est un double

Mais attention : une fois que Dart a deviné, il ne change plus d’avis.

❌ Erreur ! Dart avait compris que c'était un


var age = 30;​
age = "trente"; //
int

🧪 Test rapide
void main() {​
String prenom = "Jean";​
int nombre = 42;​
bool estConnecte = false;​

print("Bonjour $prenom !");​
print("Tu as choisi le nombre $nombre");​
print("Connecté ? $estConnecte");​
}

📦 Résultat à l’écran :
Bonjour Jean !

Tu as choisi le nombre 42

Connecté ? false

5.3 🔀 Conditions et opérateurs


Quand tu écris du code, tu dois souvent prendre des décisions :​
« Si l’utilisateur appuie sur le bouton, alors je fais X, sinon je fais Y. »​
En Dart, ces décisions se prennent avec des conditions et des opérateurs.

🧭 L’analogie du carrefour
Imagine que tu roules et que tu arrives à un carrefour :

●​ Feu vert ? ➜ tu avances.​

●​ Feu rouge ? ➜ tu t’arrêtes.​

●​ Feu orange ? ➜ tu ralentis ou tu passes vite.​

Le code fait la même chose : il regarde une condition (la couleur du feu) et choisit
un chemin.

1️⃣ Les mots‑clefs if, else if, else


void main() {​
int age = 20;​

if (age >= 18) {​
print("Tu es majeur, tu peux conduire.");​
} else if (age >= 16) {​
print("Permis moto seulement !");​
} else {​
print("Trop jeune pour conduire.");​
}​
}
●​ if : le premier test.​

●​ else if : un test supplémentaire si le premier est faux.​

●​ else : le plan B final.​

2️⃣ Les opérateurs de comparaison

Opérateur Signifie… Exemple (a = 5, b Résulta


= 7) t

== égal à a == 5 true

!= différent de a != b true

< inférieur à a < b true

> supérieur à a > b false

<= inférieur ou égal a <= 5 true


à

>= supérieur ou b >= 10 false


égal à

3️⃣ Les opérateurs logiques

Opérateu Nom Utilité Exemple


r
&& ET logique Vrai si les deux conditions (age > 18) && (ville
sont vraies == "Paris")

|| OU Vrai si au moins une est `(age > 18)


logique vraie

! NON Inverse la valeur !(age > 18)


logique

🧠 Astuce mémo :
●​ && = intersection des routes : il faut les deux feux verts.​

●​ \|\| = deux chemins parallèles : un seul feu vert suffit.​

●​ ! = demi‑tour : tu prends la direction opposée.​

4️⃣ Le switch : la multiprise des conditions

Quand tu as beaucoup de cas possibles, switch rend le code plus lisible :

void main() {​
String jour = "samedi";​

switch (jour) {​
case "lundi":​
case "mardi":​
case "mercredi":​
case "jeudi":​
case "vendredi":​
print("C'est un jour de semaine");​
break;​
case "samedi":​

🎉
case "dimanche":​
print("C'est le week-end ");​
break;​
default:​
print("Jour inconnu");​
}​
}

🧪 Mini‑exercice
Essaye d’écrire un programme qui :

1.​ Demande une note sur 20 (ex. 14).​

2.​ Affiche :​

○​ >=16 : « Excellent »​

○​ >=12 : « Bon travail »​

○​ >=8 : « Peut mieux faire »​

○​ sinon : « À revoir ».​

(Tu peux tester dans dartpad.dev !)

5.4 🔁 Boucles (for, while, foreach) – Répéter des actions en Dart


Quand tu programmes, il y a plein de moments où tu dois répéter une tâche
plusieurs fois.

📦Imagine
Exemple terre à terre :​
que tu dois mettre 10 bouteilles dans un carton.​
Tu ne vas pas écrire une ligne de code pour chaque bouteille…​

👉
Tu vas plutôt dire à la machine :​
« Répète 10 fois : mettre une bouteille ».

C’est là qu’entrent en jeu les boucles.

1️⃣ La boucle for – Pour un nombre connu de répétitions


void main() {​
for (int i = 1; i <= 5; i++) {​
print("Bouteille $i placée dans le carton");​
}​
}
🧠 Décryptage :
●​ int i = 1 : on commence à 1.​

●​ i <= 5 : tant que i est inférieur ou égal à 5, on continue.​

●​ i++ : on augmente i de 1 à chaque tour.​

Résultat :

Bouteille 1 placée dans le carton​


Bouteille 2 placée dans le carton​
...​
Bouteille 5 placée dans le carton

2️⃣ La boucle while – Répète tant qu’une condition est vraie


void main() {​
int bouteilles = 1;​

while (bouteilles <= 3) {​
print("Bouteille $bouteilles emballée");​
bouteilles++;​
}​
}

🧠« Pendant
C’est comme dire :​
que le carton n’est pas plein, continue à mettre des bouteilles. »

3️⃣ La boucle do…while – Exécute au moins une fois


void main() {​
int tentatives = 1;​

do {​
print("Tentative numéro $tentatives");​
tentatives++;​
} while (tentatives <= 3);​
}

🧠 Différence : ici, même si la condition est fausse au départ, le bloc sera


exécuté une fois.

4️⃣ La boucle for…in – Parcourir une liste d’éléments (foreach)


void main() {​
List<String> fruits = ["pomme", "banane", "orange"];​

for (String fruit in fruits) {​
print("Je mange une $fruit");​
}​
}

📦 Utile quand tu veux lire chaque élément d’une liste, comme un scanner qui lit
chaque article dans un sac.

🧪 Mini‑exercice
1.​ Crée une liste de 5 prénoms.​

2.​ Utilise une boucle for…in pour afficher :​

○​ "Bonjour, [prénom]!" pour chaque personne de la liste.​

Tu peux tester tout ça en ligne sur dartpad.dev.

5.5 🧩 Fonctions – Organiser son code comme une boîte à outils


Imagine que tu fais souvent la même chose dans la vie :​
préparer un café, envoyer un email, calculer une somme...

Plutôt que de réécrire chaque étape à chaque fois, tu crées une routine
réutilisable.

En programmation, cette routine, c’est une fonction.​


Elle te permet de regrouper du code, de lui donner un nom, et de le réutiliser
facilement.
🧠 Analogie terre à terre
Une fonction, c’est comme une machine à laver :

●​ Tu y mets du linge (les paramètres)​

●​ Tu appuies sur le bouton (tu appelles la fonction)​

●​ Elle fait son travail (le traitement)​

●​ Elle te rend du linge propre (le résultat ou le retour)​

1️⃣ Créer une fonction simple


void direBonjour() {​
print("Bonjour !");​
}

void = la fonction ne retourne rien (juste une action).​


direBonjour() = son nom.​
{} = le corps de la fonction.

💡 Appeler la fonction :
void main() {​
direBonjour(); // Affiche : Bonjour !​
}

2️⃣ Fonction avec paramètres


void saluer(String nom) {​
print("Salut, $nom !");​
}

Appel avec un argument :


void main() {​
saluer("Alice"); // Affiche : Salut, Alice !​
}

3️⃣ Fonction avec un retour (return)


int addition(int a, int b) {​
return a + b;​
}

Appel et utilisation du résultat :

void main() {​
int total = addition(5, 3);​
print("Le total est $total"); // Affiche : Le total est 8​
}

📌 int indique que la fonction retourne un entier.


4️⃣ Paramètres optionnels et valeurs par défaut
void saluerAvecStyle(String nom, [String? emoji = " 👋"]) {​
print("Salut $nom $emoji");​
}

Appels possibles :

👋​
🔥"); 🔥
saluerAvecStyle("Nina"); // Salut Nina
saluerAvecStyle("Léo", " // Salut Léo

5️⃣ Fonctions anonymes (lambda) – Fonction sans nom


var noms = ["Ali", "Bob", "Chloé"];​

noms.forEach((nom) {​
print("Hello $nom");​
});
Utile pour des actions courtes, comme des callbacks ou des événements.

🧪 Mini‑exercice
1.​ Crée une fonction multiplier(a, b) qui retourne le produit de deux
nombres.​

2.​ Crée une fonction direAuRevoir(nom) qui affiche : "Au revoir, [nom] !"​

✅ Maintenant tu sais comment créer tes propres outils avec les fonctions !
5.6 📚 Collections (List, Map, Set) – Organiser plein de données
Quand tu programmes, tu dois souvent gérer plusieurs données à la fois.​
Flutter/Dart propose plusieurs façons de stocker et manipuler ces groupes de
données, appelés collections.

1️⃣ List – Une liste ordonnée d’éléments

Pense à une liste de courses ou à une rangée de boîtes.​


Chaque élément a une position (un indice), et tu peux accéder à chaque élément
par sa position.

void main() {​
List<String> fruits = ["pomme", "banane", "orange"];​

print(fruits[0]); // Affiche "pomme"​
print(fruits.length); // Affiche 3 (taille de la liste)​

fruits.add("mangue"); // Ajouter un élément à la fin​
print(fruits); // ["pomme", "banane", "orange", "mangue"]​
}

2️⃣ Set – Une collection d’éléments uniques, sans ordre précis

Imagine un sac de billes où chaque bille est différente, et où l’ordre ne compte pas.

void main() {​
Set<String> couleurs = {"rouge", "vert", "bleu"};​

couleurs.add("jaune");​
couleurs.add("vert"); // Ne sera pas ajouté, car "vert" est
déjà là​

print(couleurs); // Peut afficher les couleurs dans un ordre
quelconque​
}

3️⃣ Map – Collection clé/valeur, comme un dictionnaire

Imagine un carnet d’adresses :

●​ la clé est le nom d’une personne​

●​ la valeur est son numéro de téléphone.​

void main() {​
Map<String, String> annuaire = {​
"Alice": "0102030405",​
"Bob": "0607080910"​
};​

print(annuaire["Alice"]); // Affiche "0102030405"​

annuaire["Chloé"] = "0506070809"; // Ajouter un nouveau contact​
print(annuaire);​
}

🧠 Résumé rapide
Collection Ordre Doublons Accès par...

List Oui Oui Indice (position)


Set Non Non -

Map Non Clés Clé (ex: nom ou


uniques ID)

🧪 Mini‑exercice
1.​ Crée une List de 4 villes que tu aimes.​

2.​ Crée un Set de 3 couleurs, ajoute une couleur déjà présente.​

3.​ Crée une Map avec 2 pays et leurs capitales.​

4.​ Affiche le contenu de chaque collection.​

✅ Tu es prêt pour gérer plein de données dans tes apps Flutter !


5.7 🛑 Null Safety – L’analogie du frigo vide
Imagine ton frigo chez toi.​
Tu ouvres la porte et tu veux prendre un yaourt.

●​ Si le yaourt est là, tu peux le manger.​

●​ Si le yaourt est absent (vide), que fais-tu ? Tu peux pas manger ce qui
n’existe pas !​

En programmation, le null c’est comme ce frigo vide.

Ça veut dire qu’il n’y a rien à cet endroit.

Pourquoi c’est important ?


Quand tu écris du code, tu veux éviter d’essayer d’utiliser quelque chose qui n’existe
pas (null).​
Sinon, ton application plante avec une erreur appelée NullPointerException.

Dart a une solution : la null safety.

●​ Par défaut, une variable ne peut pas être nulle.​

●​ Si tu veux autoriser qu’elle soit vide, tu dois le dire clairement avec un ?.​

Exemple sans null safety (dangereux)


String nom; // Pas initialisé, valeur = null​

void main() {​
print(nom.length); // Erreur, car nom est null !​
}

Exemple avec null safety (sécurisé)


String? nom; // Peut être null​

void main() {​
if (nom != null) {​
print(nom.length); // OK, on vérifie avant​
} else {​
print("Pas de nom dans le frigo !");​
}​
}

Analogies simples

Situation dans la vraie Null safety Dart


vie

Frigo avec un yaourt Variable qui contient une valeur


Frigo vide Variable avec valeur null

Vérifier si le yaourt est là Vérifier si la variable n’est pas null avant de


l’utiliser

Pourquoi Dart fait ça ?

Pour éviter que ton app crashe à cause d’un “frigo vide” non prévu.

🧪 Mini-exercice
1.​ Déclare une variable String? message qui peut être null.​

2.​ Écris un code qui affiche message.length seulement si message n’est pas
null. Sinon, affiche “Message vide”.​

✅ Avec la null safety, tu réduis les bugs liés aux valeurs manquantes et tu rends ton
app plus robuste !
Chapitre 6 – Programmation orientée objet avec Dart

6.1 Classes et objets – Comprendre la base de la POO avec Dart

Imagine que tu veux représenter un véhicule dans ton programme.​


Un objet est une instance concrète de ce véhicule, par exemple une voiture rouge,
une voiture bleue, etc.

La classe est comme un plan ou un modèle, un gabarit.

Elle définit les caractéristiques et comportements que tous les véhicules doivent
avoir.

Exemple simple en Dart :


class Vehicule {​
String couleur;​
int roues;​

void demarrer() {​
print("Le véhicule démarre");​
}​
}

Créer un objet à partir de cette classe :


void main() {​
Vehicule voiture = Vehicule();​
voiture.couleur = "rouge";​
voiture.roues = 4;​

print("Ma voiture est de couleur ${voiture.couleur} et a
${voiture.roues} roues.");​
voiture.demarrer();​
}
En résumé

●​ Classe = plan de construction​

●​ Objet = instance concrète avec des valeurs spécifiques​

●​ Attributs = caractéristiques (ex: couleur, nombre de roues)​

●​ Méthodes = comportements (ex: démarrer, freiner)​

6.2 Constructeurs – Comment créer un objet avec ses valeurs dès le


départ

Quand tu crées un objet, souvent tu veux lui donner des valeurs tout de suite.

C’est là que le constructeur entre en jeu.

Le constructeur, c’est une fonction spéciale dans la classe qui sert à


initialiser un objet.

Exemple avec constructeur simple en Dart :


class Vehicule {​
String couleur;​
int roues;​

// Constructeur​
Vehicule(this.couleur, this.roues);​

void demarrer() {​
print("Le véhicule démarre");​
}​
}

Utilisation :
void main() {​
Vehicule voiture = Vehicule("bleu", 4);​
print("Ma voiture est de couleur ${voiture.couleur} et a
${voiture.roues} roues.");​
voiture.demarrer();​
}

Pourquoi c’est utile ?

●​ Tu évites de créer un objet avec des attributs non initialisés.​

●​ Tu peux créer des objets avec des valeurs personnalisées dès leur
création.​

Bonus : constructeur nommé (pour plus de clarté)


class Vehicule {​
String couleur;​
int roues;​

Vehicule(this.couleur, this.roues);​

// Constructeur nommé​
Vehicule.avecRoues6(this.couleur) {​
roues = 6;​
}​
}

Utilisation constructeur nommé :


void main() {​
Vehicule camion = Vehicule.avecRoues6("vert");​
print("Camion couleur ${camion.couleur} avec ${camion.roues}
roues.");​
}

6.3 Héritage – Réutiliser et spécialiser du code facilement

Imagine que tu as une classe Vehicule et que tu veux créer une classe plus
spécifique comme Voiture ou Moto.

Au lieu de réécrire tout ce qui est commun (couleur, nombre de roues, démarrer…),
tu peux hériter de la classe Vehicule.
L’héritage, c’est comme un enfant qui reçoit les traits de ses parents,
mais peut aussi avoir ses propres particularités.

Exemple en Dart :
class Vehicule {​
String couleur;​
int roues;​

Vehicule(this.couleur, this.roues);​

void demarrer() {​
print("Le véhicule démarre");​
}​
}​

// Voiture hérite de Vehicule​
class Voiture extends Vehicule {​
String marque;​

Voiture(String couleur, int roues, this.marque) : super(couleur,
roues);​

void klaxonner() {​
print("La voiture klaxonne !");​
}​
}

Utilisation :
void main() {​
Voiture maVoiture = Voiture("rouge", 4, "Toyota");​
print("Ma voiture est une ${maVoiture.marque} de couleur
${maVoiture.couleur}");​
maVoiture.demarrer(); // méthode héritée​
maVoiture.klaxonner(); // méthode spécifique​
}

En résumé

●​ Classe parent (superclasse) : classe générale (Vehicule)​


●​ Classe enfant (sous-classe) : classe plus spécifique (Voiture)​

●​ Héritage permet de réutiliser le code et d’ajouter des fonctionnalités


spécifiques.​

6.4 Encapsulation – Protéger les données de l’objet pour éviter les


erreurs

Imagine que ta voiture a un réservoir d’essence. Tu ne veux pas que quelqu’un


mette n’importe quoi dedans, ou que l’essence devienne négative.

L’encapsulation, c’est comme une porte verrouillée sur les données de ta classe :
on contrôle ce qu’on peut voir et modifier.

En Dart, on rend les attributs privés avec un underscore _ devant leur


nom.

On crée des méthodes publiques (getters et setters) pour accéder et modifier ces
données en toute sécurité.

Exemple :
class CompteBancaire {​
double _solde = 0; // privé​

// Getter pour lire le solde​
double get solde => _solde;​

// Setter pour déposer de l'argent (seulement si positif)​
void deposer(double montant) {​
if (montant > 0) {​
_solde += montant;​
print("Dépôt de $montant effectué.");​
} else {​
print("Montant invalide !");​
}​
}​

// Méthode pour retirer de l'argent si possible​
void retirer(double montant) {​
if (montant > 0 && montant <= _solde) {​
_solde -= montant;​
print("Retrait de $montant effectué.");​
} else {​
print("Retrait impossible !");​
}​
}​
}

Utilisation :
void main() {​
CompteBancaire compte = CompteBancaire();​
compte.deposer(100);​
print("Solde actuel : ${compte.solde}");​
compte.retirer(50);​
print("Solde après retrait : ${compte.solde}");​
}

Pourquoi c’est important ?

●​ Protéger les données sensibles​

●​ Éviter les modifications incorrectes​

●​ Contrôler l’accès et les règles métier​

6.5 Interfaces et mixins – Partager du comportement entre classes

Interfaces :

Imagine que tu définis un contrat que plusieurs classes doivent respecter, sans dire
comment elles doivent le faire.

Par exemple, un contrat Volant pourrait dire que la classe doit avoir une méthode
voler(), mais chaque classe (Avion, Oiseau) va implémenter cette méthode à sa
façon.

En Dart, toutes les classes peuvent être utilisées comme interfaces. Tu peux
forcer une classe à implémenter certaines méthodes.
Exemple d’interface en Dart :
abstract class Volant {​
void voler();​
}​

class Oiseau implements Volant {​
@override​
void voler() {​
print("L'oiseau vole avec ses ailes");​
}​
}​

class Avion implements Volant {​
@override​
void voler() {​
print("L'avion vole avec ses moteurs");​
}​
}

Mixins :

Les mixins sont comme des boîtes à outils que tu peux ajouter à une classe pour
lui donner des fonctionnalités sans hériter d’une autre classe.

Par exemple, tu peux avoir un mixin Nageur qui ajoute la capacité de nager à
plusieurs classes (Poisson, Humain).

Exemple de mixin en Dart :


mixin Nageur {​
void nager() {​
print("Je sais nager !");​
}​
}​

class Poisson with Nageur {}​

class Humain with Nageur {}​

void main() {​
Poisson poisson = Poisson();​
poisson.nager(); // Je sais nager !​

Humain humain = Humain();​
humain.nager(); // Je sais nager !​
}

En résumé :

●​ Interface : un contrat que la classe doit respecter (méthodes à implémenter)​

●​ Mixin : un ajout de fonctionnalités réutilisables sans changer la hiérarchie des


classes​
Chapitre 7 – Aller plus loin avec Dart
7.1 Lire et écrire dans des fichiers

Imagine que tu as un cahier où tu peux écrire des notes (écrire dans un fichier) et
plus tard, relire ce que tu as écrit (lire un fichier).

En programmation mobile, on utilise souvent des fichiers pour sauvegarder des


données simples localement.

En Dart, pour manipuler des fichiers, on utilise la bibliothèque dart:io.

Exemple simple d’écriture dans un fichier :


import 'dart:io';​

void ecrireFichier() async {​
final fichier = File('monfichier.txt');​

// Écrire du texte dans le fichier​
await fichier.writeAsString('Bonjour Flutter, je stocke ce texte
dans un fichier !');​
print('Écriture terminée.');​
}

Exemple simple de lecture d’un fichier :


import 'dart:io';​

void lireFichier() async {​
try {​
final fichier = File('monfichier.txt');​
String contenu = await fichier.readAsString();​
print('Contenu du fichier : $contenu');​
} catch (e) {​
print('Erreur lors de la lecture : $e');​
}​
}
Points importants :

●​ Ces opérations sont asynchrones : on utilise async et await pour ne pas


bloquer l’application.​

●​ Le chemin du fichier peut changer selon la plateforme (mobile, web, desktop).


Sur mobile, on utilise souvent des plugins comme path_provider pour
accéder aux dossiers sûrs.​

7.2 Faire des requêtes HTTP (GET, POST)

Imagine que ton application mobile est comme une personne qui envoie des lettres
(requêtes) à un serveur (poste), et reçoit des réponses (lettres de retour).

Ces échanges permettent de récupérer des données (ex : météo, news) ou


d’envoyer des infos (ex : formulaire, commande).

En Dart, on utilise souvent le package http pour faire ces échanges.

Exemple d’une requête GET (récupérer des données) :


import 'package:http/http.dart' as http;​
import 'dart:convert';​

void fetchData() async {​
final url =
Uri.parse('https://jsonplaceholder.typicode.com/posts/1');​
final response = await http.get(url);​

if (response.statusCode == 200) {​
// Décoder la réponse JSON en objet Dart​
var data = jsonDecode(response.body);​
print('Titre du post : ${data['title']}');​
} else {​
print('Erreur serveur : ${response.statusCode}');​
}​
}

Exemple d’une requête POST (envoyer des données) :


import 'package:http/http.dart' as http;​
import 'dart:convert';​

void sendData() async {​
final url =
Uri.parse('https://jsonplaceholder.typicode.com/posts');​
final response = await http.post(​
url,​
headers: {'Content-Type': 'application/json'},​
body: jsonEncode({'title': 'Mon titre', 'body': 'Le contenu',
'userId': 1}),​
);​

if (response.statusCode == 201) {​
print('Données envoyées avec succès !');​
var data = jsonDecode(response.body);​
print('ID créé : ${data['id']}');​
} else {​
print('Erreur lors de l\'envoi : ${response.statusCode}');​
}​
}

Points importants :

●​ GET = demander des infos au serveur.​

●​ POST = envoyer des infos au serveur.​

●​ Toujours gérer les codes réponses (200, 201 = succès).​

●​ JSON est le format d’échange standard, donc on encode/décode souvent les


données avec jsonEncode et jsonDecode.​

7.3 Gérer le JSON


Imagine que JSON, c’est comme un langage universel que les applications utilisent
pour s’échanger des données, un peu comme un passeport que tout le monde
comprend.

Qu’est-ce que le JSON ?

●​ JSON signifie JavaScript Object Notation.​

●​ C’est un format texte, léger et facile à lire, qui organise les données en paires
clé-valeur, comme un petit dictionnaire.​

●​ Exemple simple :​

{​
"nom": "Alice",​
"age": 25,​
"estEtudiant": true​
}

Comment gérer le JSON en Dart ?

Dart propose les fonctions jsonEncode() et jsonDecode() du package


dart:convert pour convertir des données Dart en JSON, et inversement.

Exemple : décoder un JSON (texte → objet Dart)


import 'dart:convert';​

void decoderJson() {​
String jsonString = '{"nom": "Alice", "age": 25, "estEtudiant":
true}';​

Map<String, dynamic> utilisateur = jsonDecode(jsonString);​
print('Nom : ${utilisateur['nom']}');​
print('Âge : ${utilisateur['age']}');​
print('Étudiant : ${utilisateur['estEtudiant']}');​
}
Exemple : encoder un objet Dart en JSON (objet → texte)
import 'dart:convert';​

void encoderJson() {​
Map<String, dynamic> utilisateur = {​
'nom': 'Bob',​
'age': 30,​
'estEtudiant': false,​
};​

String jsonString = jsonEncode(utilisateur);​
print('JSON encodé : $jsonString');​
}

Points importants :

●​ Les objets Dart courants pour JSON sont : Map (pour objets) et List (pour
tableaux).​

●​ JSON est la langue la plus courante pour communiquer avec des API.​

●​ Penser toujours à décoder les réponses JSON pour les utiliser en Dart.​

7.4 Utiliser des packages

Qu’est-ce qu’un package ?

Imagine que tu construis une maison. Tu pourrais tout fabriquer toi-même : les
fenêtres, les portes, les meubles… Mais ce serait très long !​
Alors tu vas au magasin de bricolage et tu achètes des fenêtres toutes faites, des
portes prêtes à poser, des outils spécialisés.

En programmation, un package est comme ce magasin : c’est un ensemble de code


réutilisable, écrit par d’autres développeurs, que tu peux simplement importer dans
ton projet pour gagner du temps et ajouter des fonctionnalités.

Pourquoi utiliser des packages ?


●​ Pour ne pas réinventer la roue : pas besoin de coder une fonctionnalité
complexe déjà disponible.​

●​ Pour profiter de solutions testées et fiables.​

●​ Pour accélérer ton développement et te concentrer sur l’essentiel.​

Où trouver des packages Dart/Flutter ?

●​ Sur pub.dev, le dépôt officiel des packages Dart/Flutter.​

●​ Tu peux chercher par fonctionnalité, popularité, ou nouveautés.​

Comment utiliser un package dans ton projet Flutter ?

1.​ Ouvre le fichier pubspec.yaml à la racine de ton projet Flutter.​

2.​ Sous la section dependencies:, ajoute le nom du package et la version


souhaitée. Exemple :​

dependencies:​
http: ^0.13.0​
shared_preferences: ^2.0.0

3.​ Enregistre le fichier, puis dans le terminal, lance la commande :​

flutter pub get

Cela télécharge et installe les packages.

4.​ Dans ton code Dart, importe le package pour l’utiliser :​

import 'package:http/http.dart' as http;


Exemple concret : utiliser le package http pour faire des requêtes réseau

(On a déjà vu ça en 7.2, mais c’est un bon exemple de package utile.)

Attention

●​ Choisir des packages bien maintenus et populaires, pour éviter des bugs ou
abandon.​

●​ Toujours consulter la documentation du package pour comprendre comment


l’utiliser.​
🏗️ PARTIE 3 – Apprendre Flutter en construisant une
app réelle
Projet choisi : TaskZen – Une app de gestion de tâches intelligente

Tu as maintenant les bases de Dart et tu comprends comment Flutter fonctionne. Il


est temps de passer aux choses sérieuses : coder une vraie application de A à Z.

Dans cette partie, on ne va plus apprendre juste pour apprendre.​


On va apprendre en construisant une vraie app utile, étape par étape.
Chaque nouveau concept Flutter sera directement mis en pratique dans
TaskZen.

🎯 Objectif de TaskZen
TaskZen est une application de gestion de tâches (to-do list) moderne, simple
mais évolutive.

Voici ce qu’elle va permettre progressivement :

●​ Ajouter, modifier, supprimer des tâches ;​

●​ Marquer les tâches comme terminées ;​

●​ Afficher les tâches sous différentes vues ;​

●​ Sauvegarder les données localement (SQLite), puis dans le cloud (Firebase) ;​

●​ Gérer des utilisateurs (authentification) ;​

●​ Envoyer des notifications de rappel ;​

●​ Et bien plus encore !​

📈 Philosophie de la progression
Chaque chapitre de cette partie correspond à une brique fonctionnelle
de l’app.

On va construire TaskZen version par version :

●​ TaskZen v1 : Interface simple, tâches en mémoire ;​


●​ TaskZen v2 : Persistance avec SQLite ;​

●​ TaskZen v3 : Connexion à Firebase ;​

●​ TaskZen v4 : Authentification, notifications, polish.​

À la fin, tu auras appris comment créer une app Flutter complète, adaptable à
n’importe quel autre projet réel.

🔧 8.1 – Démarrer une nouvelle app Flutter


C’est ici que notre aventure concrète commence. On va créer ensemble l’ossature
de l’application TaskZen, en partant de zéro.

🎯 Objectif de cette étape


Créer le squelette de l’application Flutter sur ton poste de travail, que tu pourras
ensuite enrichir au fil des chapitres.

✅ Étapes pour démarrer une app Flutter


Ces étapes supposent que ton environnement est bien prêt (Flutter SDK
installé, VS Code ou Android Studio fonctionnel, flutter doctor OK).

📂 1. Créer le projet
Ouvre un terminal ou le terminal intégré de VS Code, puis tape :

flutter create taskzen

📝 Ce nom (taskzen) est celui du dossier et du projet. Tu peux en


choisir un autre, mais garde-le simple, sans espace.

Flutter va créer toute la structure de base.

📁 2. Naviguer dans le projet


Entre dans le dossier :
cd taskzen

Tu peux maintenant l’ouvrir dans ton éditeur :

code . # Si tu utilises VS Code

▶️ 3. Lancer l'app par défaut


Branche ton téléphone, démarre ton émulateur, ou choisis un simulateur web, puis
exécute :

flutter run

Tu verras s’afficher l’application Flutter de base (le fameux compteur avec le +).

Félicitations 🎉 Tu viens de créer ta première app Flutter fonctionnelle !


📌 Ce qu’il faut retenir
●​ Flutter te donne une base fonctionnelle avec juste une commande ;​

●​ Cette base utilise MaterialApp et quelques widgets de base ;​

●​ On va maintenant remplacer le code par défaut par notre propre code pour
construire TaskZen v1.​

🧱 8.2 – Structure d’un projet Flutter


Lorsque tu crées une app Flutter avec flutter create, Flutter génère une
architecture de base. Comprendre cette structure, c’est comme comprendre le plan
d’une maison avant de commencer les travaux.

🗂️ Vue d’ensemble des dossiers et fichiers importants


Voici les dossiers principaux et leur rôle :
📁 lib/
C’est le cœur de ton application.​

👉
Tout ton code Dart (logique, UI, services…) sera ici.​
Par défaut, on y trouve un fichier main.dart, point d’entrée de
l’app.

📁 android/ et 📁 ios/
Ce sont les dossiers natifs Android et iOS, générés automatiquement.​
Ils permettent à Flutter de communiquer avec les plateformes mobiles

⚠️
(accès caméra, stockage, etc.).​
Tu n’as pas besoin de les modifier au début.

📁 web/ (si tu as activé le support web)


Contient les fichiers nécessaires pour déployer ton app sur un navigateur.

📁 test/
Tu y mets tes tests automatisés. Très utile pour vérifier que ton app
fonctionne bien.

📄 Les fichiers importants


📄 pubspec.yaml
Un des fichiers les plus importants.​
Il sert à :

●​ Lister les packages que tu utilises​

●​ Ajouter des assets (images, polices…)​

●​ Définir le nom, la version et les métadonnées de ton app​

Exemple d’un bout de pubspec.yaml :

name: taskzen​
description: Une app de gestion de tâches intelligente​
version: 1.0.0+1​

dependencies:​
flutter:​
sdk: flutter​
provider: ^6.0.0 # Exemple de package​

flutter:​
assets:​
- assets/images/

📄 .gitignore
Liste des fichiers à ignorer dans Git (build, secrets, etc.)

📄 main.dart
Le point d’entrée de l’app (équivalent du main() en C ou Java).​
Flutter démarre ici.

🎯 Objectif à ce stade
👉 Comprendre où écrire ton code, où chercher les erreurs, où ajouter des
ressources.​
Quand on débute, on travaille à 90% dans le dossier lib/, et un peu dans
pubspec.yaml.

👋 8.3 – Hello World et explication du widget


MaterialApp
Créer un Hello World en Flutter, c’est comme allumer le moteur d’une voiture neuve
pour la première fois. Tu vois si tout démarre correctement et tu commences à
explorer les boutons du tableau de bord.

🚀 1. Écrire son premier Hello World


Ouvre ton fichier lib/main.dart, et remplace tout le contenu par ce code
minimaliste :

import 'package:flutter/material.dart';​

void main() {​
runApp(MyApp());​
}​

class MyApp extends StatelessWidget {​
@override​
Widget build(BuildContext context) {​
return MaterialApp(​
title: 'TaskZen',​
home: Scaffold(​

👋
appBar: AppBar(​
title: Text('Hello TaskZen '),​
),​
body: Center(​
child: Text('Bienvenue dans ton premier Hello World
Flutter !'),​
),​
),​
);​
}​
}

🧠 Astuce : sauvegarde le fichier (Ctrl + S), et si tu as bien configuré


ton émulateur ou ton téléphone, Flutter affiche ta première UI
instantanément grâce au hot reload !

🏗️ 2. Le rôle de MaterialApp
MaterialApp, c’est un widget de très haut niveau qui fournit :

●​ une coquille pour ton application,​

●​ le thème global (couleurs, polices),​

●​ la navigation entre les pages (routes),​

●​ la gestion du titre, langue, etc.​

Autrement dit, c’est le chef d’orchestre de ton app Flutter. Tu ne fais presque
jamais une app sans MaterialApp.
🧱 3. Et les autres widgets dans ce Hello World ?
Voici une mini-autopsie du code :

Widget Rôle

runApp Démarre l’app Flutter avec le widget racine (ici


() MyApp)

MyApp Ton widget principal qui retourne un


MaterialApp

Scaffo Fournit une structure de base : barre d’app,


ld corps…

AppBar Affiche la barre en haut (nom de l’app, actions,


etc.)

Center Centre le widget enfant

Text Affiche simplement du texte

🧠 À retenir
●​ Flutter est entièrement basé sur des widgets : tout est un widget, même un
simple texte.​

●​ MaterialApp est la base de ton app, comme main() dans un programme


en C ou Python.​
🧩 8.4 – Utilisation des widgets de base dans Flutter
Les widgets de base, c’est comme les briques LEGO de ton app Flutter. Tu les
empiles, tu les combines, tu les imbriques pour construire ton interface.

Dans cette section, on va découvrir les plus courants :

●​ Text​

●​ Column et Row​

●​ Container​

●​ ElevatedButton​

📝 1. Text – Afficher du texte


Le widget le plus simple ! Exemple :

Text('Bonjour tout le monde');

Tu peux personnaliser le texte avec le style :

Text(​
'Hello TaskZen',​
style: TextStyle(fontSize: 20, color: Colors.blue),​
);

📐 2. Column et Row – Organiser les éléments


●​ Column aligne verticalement les widgets (comme une pile).​

●​ Row aligne horizontalement.​

Column(​
children: [​
Text('Titre'),​
Text('Sous-titre'),​
ElevatedButton(onPressed: () {}, child: Text('Valider')),​
],​
)

Tu peux centrer les éléments :

Column(​
mainAxisAlignment: MainAxisAlignment.center,​
crossAxisAlignment: CrossAxisAlignment.center,​
children: [ /* ... */ ],​
)

📦 3. Container – Une boîte personnalisable


Container est un widget multifonction :

●​ pour ajouter du padding​

●​ des marges​

●​ une couleur​

●​ une taille fixe​

●​ ou même une décoration​

Container(​
padding: EdgeInsets.all(16),​
margin: EdgeInsets.symmetric(vertical: 10),​
color: Colors.amber,​
child: Text('Je suis dans une boîte !'),​
)

🖱️ 4. ElevatedButton – Un bouton moderne


Flutter propose plusieurs types de boutons. Le plus classique est :

ElevatedButton(​
onPressed: () {​
print('Tu as cliqué !');​
},​
child: Text('Clique-moi'),​
)

Tu peux aussi personnaliser le style avec style:


ElevatedButton.styleFrom(...).

📦 Exemple complet avec tous ces widgets


import 'package:flutter/material.dart';​

void main() => runApp(MyApp());​

class MyApp extends StatelessWidget {​
@override​
Widget build(BuildContext context) {​
return MaterialApp(​
home: Scaffold(​
appBar: AppBar(title: Text('Widgets de base')),​
body: Padding(​
padding: EdgeInsets.all(20),​
child: Column(​
mainAxisAlignment: MainAxisAlignment.center,​
children: [​
Text('Bienvenue sur TaskZen !',​
style: TextStyle(fontSize: 24, fontWeight:
FontWeight.bold)),​
SizedBox(height: 20),​
Container(​
color: Colors.blue[50],​
padding: EdgeInsets.all(12),​
child: Text('Ceci est un Container'),​
),​
SizedBox(height: 20),​
ElevatedButton(​
onPressed: () {},​
child: Text('Appuie ici'),​
),​
],​
),​
),​
),​
);​
}​
}

🎯 À retenir
●​ Tout est un widget.​

●​ Column et Row sont des layouts.​

●​ Container te permet de styliser et positionner un widget.​

●​ Text affiche du texte, ElevatedButton interagit avec l’utilisateur.​


Chapitre 9 – Créer des interfaces modernes avec les
Widgets

📦 9.1 – Comprendre les widgets Stateless vs


Stateful : l’âme de Flutter
Avant de faire des interfaces "vivantes", il faut comprendre une chose essentielle en

👉
Flutter :​
Il existe deux grands types de widgets : les Stateless et les Stateful.

🪵 1. Le widget Stateless – Comme une planche de bois


Imagine que tu crées une enseigne en bois pour ton magasin. Tu la peins une fois,
tu la poses, et elle ne change jamais.

📌 C’est ça un widget Stateless : il est figé, immuable. Il ne bouge pas.


Exemple simple :

class MonTitre extends StatelessWidget {​


@override​
Widget build(BuildContext context) {​
return Text('Bienvenue sur TaskZen !');​
}​
}
🔹🔹TuSi netu veux
peux pas modifier ce texte une fois qu’il est affiché.​
le changer ? Tu dois reconstruire un autre widget.

🪴 2. Le widget Stateful – Comme une plante vivante


Maintenant, imagine un pot de fleurs. Tu peux l’arroser, il pousse, il change avec le
temps.

📌 C’est ça un widget Stateful : il a un état interne qui peut évoluer.


Par exemple, un compteur :

class MonCompteur extends StatefulWidget {​


@override​
_MonCompteurState createState() => _MonCompteurState();​
}​

class _MonCompteurState extends State<MonCompteur> {​
int _compteur = 0;​

void _incrementer() {​
setState(() {​
_compteur++;​
});​
}​

@override​
Widget build(BuildContext context) {​
return Column(​
children: [​
Text('Compteur : $_compteur'),​
ElevatedButton(​
onPressed: _incrementer,​
child: Text('Ajouter 1'),​
),​
],​
);​
}​
}

🔹🔹LeEt bouton modifie l’état interne (_compteur),​


Flutter redessine automatiquement l’interface quand l’état change.
🧠 Flutter en 1 phrase
🗣️ “Flutter reconstruit l’interface à chaque fois qu’un état change, mais il
le fait de manière ultra rapide et optimisée.”

🪛 Quand utiliser quoi ?


Besoin Widget à
utiliser

Afficher un texte, une image statique, une page figée StatelessWi


dget

Gérer un clic, un compteur, une saisie utilisateur, des données StatefulWid


qui changent get

🏁 Dans TaskZen, on fera quoi ?


✔️✔️L’écran d’accueil peut être Stateless.​
Le formulaire de création de tâche (avec champs texte, bouton, liste de tâches)
sera Stateful, car l’utilisateur va :

●​ Ajouter une tâche (→ ça change l’état),​

●​ Supprimer une tâche (→ encore un changement),​

●​ Marquer comme terminée (→ encore un changement).​

Bref, tout ce qui change dans l’interface = StatefulWidget.

🔥 À retenir
●​ StatelessWidget = figé, comme une photo.​

●​ StatefulWidget = dynamique, comme une vidéo.​


●​ Flutter est rapide car il reconstruit seulement les morceaux nécessaires à
chaque changement d’état.​

🧱 9.2 – Ajout de champs de texte et boutons


Dans une app de gestion de tâches, il faut au minimum :

●​ Un champ de texte pour saisir la description d’une tâche,​

●​ Un bouton pour l’ajouter à la liste.​

On va donc construire une interface de base qui fait ça 👇


🎨 Objectif de cette étape
Créer une interface comme ceci :

[ Entrez votre tâche ici ]​


[ Ajouter la tâche ]

Et chaque fois qu'on clique sur le bouton, on ajoute cette tâche à une liste
temporaire (en mémoire seulement, pour l’instant).

🔧 Étape 1 : TextField + ElevatedButton


Voici un exemple complet d’un widget Stateful qui contient un TextField (champ
de texte) et un ElevatedButton :

import 'package:flutter/material.dart';​

class AjoutTacheWidget extends StatefulWidget {​
@override​
_AjoutTacheWidgetState createState() =>
_AjoutTacheWidgetState();​
}​

class _AjoutTacheWidgetState extends State<AjoutTacheWidget> {​
final TextEditingController _controller =
TextEditingController();​
List<String> _taches = [];​

void _ajouterTache() {​
final texte = _controller.text;​
if (texte.isNotEmpty) {​
setState(() {​
_taches.add(texte);​
_controller.clear(); // On vide le champ​
});​
}​
}​

@override​
Widget build(BuildContext context) {​
return Padding(​
padding: const EdgeInsets.all(16.0),​
child: Column(​
children: [​
TextField(​
controller: _controller,​
decoration: InputDecoration(​
labelText: 'Entrez votre tâche',​
border: OutlineInputBorder(),​
),​
),​
SizedBox(height: 12),​
ElevatedButton(​
onPressed: _ajouterTache,​
child: Text('Ajouter la tâche'),​
),​
SizedBox(height: 20),​
..._taches.map((tache) => ListTile(title:
Text(tache))).toList(),​
],​
),​
);​
}​
}​

🔍 Explication terre à terre


●​ TextEditingController = comme un carnet dans lequel on note ce que
l’utilisateur tape.​

●​ _controller.text = on lit ce que l’utilisateur a saisi.​

●​ setState() = on dit à Flutter « Hey ! Redessine l’interface, car la liste a


changé ! »​

●​ _controller.clear() = on efface le champ pour permettre de saisir une


nouvelle tâche.​

🎁 Bonus – Affichage dynamique des tâches


Chaque fois qu'on ajoute une tâche, on la met dans la liste _taches.​
Ensuite, on les affiche avec :

..._taches.map((tache) => ListTile(title: Text(tache))).toList()

👉 Ce morceau transforme chaque élément de la liste en un petit widget visuel


(ListTile) avec le texte de la tâche.

🧠 À retenir
●​ Les champs de texte permettent d’interagir avec l’utilisateur.​

●​ Les boutons déclenchent des actions.​

●​ Les widgets dynamiques comme TextField, ElevatedButton, et


ListView sont essentiels dans toute app Flutter.​

🚦 9.3 – Gérer les événements et la navigation


Une app sérieuse ne se limite pas à un seul écran.​
Il faut naviguer entre plusieurs pages (écrans) pour :

●​ Ajouter une tâche,​

●​ Afficher les détails d’une tâche,​


●​ Modifier un paramètre,​

●​ Revenir à l’écran d’accueil...​

d’écrans, comme une pile d’assiettes .🥞


Flutter propose un système de navigation simple et puissant, basé sur une pile

🧠 Notion clé : la pile d’écrans (stack)


Imagine chaque écran comme une assiette.​
Quand tu ouvres un nouvel écran, tu le poses au-dessus.​
Quand tu veux revenir, tu retires la dernière assiette (retour arrière).

✨ Exemple simple avec Navigator.push


Voici un exemple avec deux écrans : Accueil et Détail.

1. L'écran d'accueil avec un bouton pour aller au détail :


class AccueilScreen extends StatelessWidget {​
@override​
Widget build(BuildContext context) {​
return Scaffold(​
appBar: AppBar(title: Text('Accueil')),​
body: Center(​
child: ElevatedButton(​
onPressed: () {​
Navigator.push(​
context,​
MaterialPageRoute(builder: (context) =>
DetailScreen()),​
);​
},​
child: Text('Voir les détails'),​
),​
),​
);​
}​
}

2. L’écran de détail :
class DetailScreen extends StatelessWidget {​
@override​
Widget build(BuildContext context) {​
return Scaffold(​
appBar: AppBar(title: Text('Détail')),​
body: Center(​
child: ElevatedButton(​
onPressed: () {​
Navigator.pop(context); // Revenir à l'écran précédent​
},​
child: Text('Retour'),​
),​
),​
);​
}​
}

🧠 Explication terre à terre


●​ Navigator.push() = On ouvre un nouvel écran (on empile).​

●​ Navigator.pop() = On ferme l’écran actuel (on dépile).​

●​ MaterialPageRoute = C’est comme un billet d’entrée vers un nouvel


écran.​

💬 Exemple concret dans TaskZen


Dans TaskZen, on aura :

●​ Un écran principal avec la liste des tâches​

●​ Un bouton “+ Ajouter une tâche” qui ouvre un écran de création​

ElevatedButton(​
onPressed: () {​
Navigator.push(​
context,​
MaterialPageRoute(builder: (context) => AjoutTacheScreen()),​
);​
},​
child: Text('Ajouter une tâche'),​
)

Et dans AjoutTacheScreen, une fois la tâche ajoutée, on peut revenir à la page


précédente avec :

Navigator.pop(context);

📌 Astuce bonus : Passer des données entre écrans


Tu peux aussi envoyer des infos à l’autre écran :

Navigator.push(​
context,​
MaterialPageRoute(​
builder: (context) => DetailScreen(tache: 'Ma super tâche'),​
),​
);

Et dans DetailScreen, tu les reçois via le constructeur :

class DetailScreen extends StatelessWidget {​


final String tache;​

DetailScreen({required this.tache});​

@override​
Widget build(BuildContext context) {​
return Text('Tâche : $tache');​
}​
}

✅ Résumé
Action Méthode Flutter

Ouvrir un nouvel Navigator.push()


écran

Fermer l’écran Navigator.pop()


courant

Envoyer des Via constructeur


données

Navigation fluide Grâce aux widgets Scaffold,


AppBar, Body

🧱 9.4 – Créer une interface de création de tâche


🎯Créer
Objectif :​
une page dédiée où l’utilisateur pourra :

●​ Entrer le titre de la tâche,​

●​ (Optionnellement) entrer une description,​

●​ Valider avec un bouton "Ajouter".​

🖼️ Aperçu de l’interface
[ Titre de la tâche ]​
[ Description (facultatif) ]​
[ Ajouter la tâche ]

🔧 Étape par étape – Code complet


import 'package:flutter/material.dart';​

class AjoutTacheScreen extends StatefulWidget {​
@override​
_AjoutTacheScreenState createState() =>
_AjoutTacheScreenState();​
}​

class _AjoutTacheScreenState extends State<AjoutTacheScreen> {​
final TextEditingController _titreController =
TextEditingController();​
final TextEditingController _descController =
TextEditingController();​

void _soumettreTache() {​
final titre = _titreController.text.trim();​
final description = _descController.text.trim();​

if (titre.isEmpty) {​
ScaffoldMessenger.of(context).showSnackBar(​
SnackBar(content: Text('Le titre est obligatoire')),​
);​
return;​
}​

🔁

// À ce stade, on pourrait envoyer la tâche à une base ou à
un parent​
print('Nouvelle tâche : $titre');​
print('Description : $description');​

Navigator.pop(context); // Revenir à la liste après ajout​
}​

@override​
Widget build(BuildContext context) {​
return Scaffold(​
appBar: AppBar(title: Text('Nouvelle tâche')),​
body: Padding(​
padding: const EdgeInsets.all(16.0),​
child: Column(​
children: [​
TextField(​
controller: _titreController,​
decoration: InputDecoration(​
labelText: 'Titre de la tâche *',​
border: OutlineInputBorder(),​
),​
),​
SizedBox(height: 12),​
TextField(​
controller: _descController,​
maxLines: 3,​
decoration: InputDecoration(​
labelText: 'Description (facultatif)',​
border: OutlineInputBorder(),​
),​
),​
SizedBox(height: 20),​
ElevatedButton(​
onPressed: _soumettreTache,​
child: Text('Ajouter la tâche'),​
),​
],​
),​
),​
);​
}​
}

🧠 Explication terre à terre


●​ Deux champs de texte : titre (obligatoire), description (facultative).​

●​ Validation simple : si le titre est vide → on affiche un message.​

●​ ScaffoldMessenger : permet d’afficher des messages temporaires


(SnackBars).​

●​ Navigator.pop(context) : on revient à l’écran précédent après ajout.​


💡 Et ensuite ?
Dans cette version, on fait un simple print() dans la console.

Mais plus tard, on va :

●​ Ajouter cette tâche à une liste persistante, dans la mémoire ou SQLite ;​

●​ Passer l’objet tâche à l’écran précédent via le


Navigator.pop(context, maTache);​

●​ Et créer une vraie classe Tache avec un ID, une date, un statut, etc.​

📌 À retenir
Élément Ce que ça fait

TextEditingCont Contrôle et lit les champs de texte


roller

TextField Affiche un champ de saisie

SnackBar Message temporaire à l'utilisateur

Navigator.pop() Revenir à l’écran précédent

trim() Supprime les espaces avant/après le


texte
Chapitre 10 – Navigation dans Flutter

🔰 10.1 – Navigation simple avec Navigator.push


Dans le chapitre précédent, nous avons vu cette forme de navigation :

Navigator.push(​
context,​
MaterialPageRoute(builder: (context) => AjoutTacheScreen()),​
);

Cette méthode est parfaite pour les petits projets, mais devient vite peu lisible si
l’application a :

●​ Beaucoup d’écrans,​

●​ Des transitions personnalisées,​

●​ Une navigation conditionnelle.​

📦 L'approche TaskZen
Dans TaskZen v1, on peut encore utiliser cette méthode simple, mais on va
préparer la migration vers des routes nommées dès maintenant.

✏️ Exemple : aller de la page d'accueil à la page d’ajout de tâche


// Dans le widget d'accueil :​
ElevatedButton(​
onPressed: () {​
Navigator.push(​
context,​
MaterialPageRoute(builder: (context) => AjoutTacheScreen()),​
);​
},​
child: Text('Ajouter une tâche'),​
);

Et dans AjoutTacheScreen, on revient :


Navigator.pop(context);

🔄 10.2 – Passer des données entre écrans


Souvent, on veut :

●​ Envoyer des infos à l'écran suivant (ex. : une tâche à modifier),​

●​ Récupérer un résultat depuis un écran (ex. : tâche nouvellement créée).​

▶️ Envoyer une donnée (vers un écran)


Navigator.push(​
context,​
MaterialPageRoute(​
builder: (context) => DetailTacheScreen(tache: maTache),​
),​
);

Dans DetailTacheScreen :

class DetailTacheScreen extends StatelessWidget {​


final Tache tache;​

DetailTacheScreen({required this.tache});​

@override​
Widget build(BuildContext context) {​
return Text('Titre : ${tache.titre}');​
}​
}​

🔁 Récupérer une donnée (depuis un écran)


final nouvelleTache = await Navigator.push(​
context,​
MaterialPageRoute(builder: (context) => AjoutTacheScreen()),​
);​

// puis​
if (nouvelleTache != null) {​
print('Tâche ajoutée : ${nouvelleTache.titre}');​
}​

Dans AjoutTacheScreen :

Navigator.pop(context, tache); // retourne l'objet

🛣️ 10.3 – Routes nommées : pour une navigation plus


claire
Quand ton app devient grande, tu veux centraliser tes écrans. Flutter permet ça avec
les routes nommées.

✅ Étapes pour activer les routes nommées :


1. Déclare les routes dans MaterialApp :
void main() {​
runApp(MaterialApp(​
initialRoute: '/',​
routes: {​
'/': (context) => AccueilScreen(),​
'/ajouter': (context) => AjoutTacheScreen(),​
'/detail': (context) => DetailTacheScreen(tache: null), //
par défaut​
},​
));​
}

2. Naviguer :
Navigator.pushNamed(context, '/ajouter');

3. Avec paramètres ?
Flutter ne permet pas directement d’envoyer un objet avec une route nommée.​
Tu peux passer par onGenerateRoute pour ça (niveau plus avancé – on y
reviendra dans TaskZen v3 ou v4).

💦 10.4 – Splash Screen, Onboarding, Deep Links


🌀 Splash screen
Écran d’intro au démarrage.

●​ Géré dans AndroidManifest.xml + launch_background.xml​

●​ Flutter affiche ensuite ton main.dart​

Tu peux aussi faire un faux splash en Flutter :

class SplashScreen extends StatelessWidget {​


@override​
Widget build(BuildContext context) {​
Future.delayed(Duration(seconds: 2), () {​
Navigator.pushReplacementNamed(context, '/');​
});​

return Scaffold(​
body: Center(child: Text('Bienvenue sur TaskZen')),​
);​
}​
}

🧭 Onboarding
Présente l’app aux nouveaux utilisateurs (slides avec “Suivant”,
“Terminer”)

Flutter a des packages comme introduction_screen ou tu peux créer


manuellement des pages de type PageView.

🔗 Deep links (liens directs vers une partie de l'app)


Exemple : cliquer sur taskzen://detail/42 ouvre directement la page de la
tâche #42.​
Géré avec firebase_dynamic_links ou uni_links.

On verra cela dans TaskZen v4 avec Firebase et les notifications.

✅ Résumé
Fonctionnalité Flutter Exemple TaskZen

Navigation Navigator.push Aller vers AjoutTacheScreen


simple

Routes nommées Navigator.pushNamed /ajouter, /detail,


/profil

Retour avec Navigator.pop(context, Ajouter une tâche, retourner la


données valeur) tâche

Splash screen Future.delayed + push Accueil temporaire

Onboarding PageView ou package Slide de bienvenue pour


nouveaux

Deep Links uni_links, Ouvrir directement une tâche


firebase_dynamic_links
Chapitre 11 – Gérer l’état dans Flutter

🧠 11.1 – C’est quoi l’état ?


Dans une application Flutter (et dans tout framework réactif moderne), l’interface
graphique dépend d’un “état”.

🎯 Définition simple
L’état, c’est l’ensemble des données qui définissent ce que
l’interface doit afficher à un instant donné.

Autrement dit :

●​ Une liste de tâches → c’est un état.​

●​ Le texte tapé dans un champ → c’est un état.​

●​ Le thème clair ou sombre sélectionné → encore un état.​

🧪 Exemple TaskZen
Supposons qu’on ait ce petit morceau d’UI :

Text("Vous avez ${taches.length} tâches à faire")

Ici, la variable taches (une liste) définit ce qui est affiché.​


Si taches change (ex : on ajoute une tâche), alors l’interface doit se mettre à

➡️
jour pour afficher le nouveau nombre.​
C’est ça, gérer l’état.

🧱 Deux types d’état


1.​ État local​

○​ Il concerne un seul widget.​

○​ Exemple : le champ de texte dans un formulaire.​


2.​ État global / partagé​

○​ Utilisé par plusieurs parties de l’interface.​

○​ Exemple : la liste complète des tâches, utilisée dans plusieurs écrans.​

📦 Flutter est réactif, mais...


Flutter ne sait pas automatiquement que tu as modifié une variable.​
Il faut lui dire de reconstruire l’interface. C’est là que des outils comme
setState() ou Provider interviennent.

🔄 Une analogie simple


Imaginons une ardoise sur laquelle tu écris le nombre de tâches à faire.​
Ton appli, c’est comme un professeur qui lit l’ardoise.​
Mais si tu ajoutes une tâche et que tu n’effaces pas et ne réécris pas l’ardoise, le
prof continuera de lire l’ancien chiffre.

👉 Gérer l’état, c’est comme mettre à jour l’ardoise proprement, puis


prévenir le prof pour qu’il relise.

Dans la section suivante, on va voir :

●​ Comment on actualise cette ardoise (avec setState ou Provider),​

●​ Pourquoi certaines méthodes sont meilleures que d'autres.​

🔁 11.2 – setState vs solutions modernes


🧱 setState : la base de la gestion d’état
C’est l’outil le plus simple et intégré nativement à Flutter pour mettre à jour
l’interface.

🔍 Comment ça fonctionne ?
Tu déclares une variable dans un widget Stateful, et quand elle change, tu appelles
setState() pour dire à Flutter de reconstruire l’interface.

📦 Exemple avec TaskZen


class MaListeDeTaches extends StatefulWidget {​
@override​
_MaListeDeTachesState createState() => _MaListeDeTachesState();​
}​

class _MaListeDeTachesState extends State<MaListeDeTaches> {​
List<String> taches = [];​

void ajouterTache(String tache) {​
setState(() {​
taches.add(tache);​
});​
}​

@override​
Widget build(BuildContext context) {​
return Column(​
children: [​
Text("Tâches : ${taches.length}"),​
ElevatedButton(​
onPressed: () => ajouterTache("Nouvelle tâche"),​
child: Text("Ajouter"),​
),​
],​
);​
}​
}

📌 Que fait setState() ici ?


Il :

●​ Signale à Flutter : "Hey, mes données ont changé !"​

●​ Flutter reconstruit tout le widget avec les nouvelles données.​

❌ Limites de setState
setState est simple et parfait pour :

●​ des démos,​

●​ des projets très petits,​

●​ ou pour gérer un état local très isolé.​

Mais dans une app comme TaskZen, avec :

●​ des données partagées entre plusieurs écrans,​

●​ des actions asynchrones (ex : lecture dans une base ou une API),​

●​ de la réactivité complexe (ex : notifier tous les widgets quand une tâche
change),​

… alors setState devient vite compliqué à maintenir.

✅ Solutions modernes : Provider (et d’autres)


Flutter propose un écosystème riche d’outils pour centraliser et mieux gérer l’état :

Solution Points forts

Provider ✅ Simple, intégré à Flutter, très populaire


Riverpod ✅ Version améliorée et plus souple de
Provider

Bloc / 🔁 Logique séparée, bon pour gros projets


Cubit

GetX, MobX ⚡ Très réactif, mais parfois verbeux /


magique
🤔 Pourquoi on choisit Provider pour TaskZen ?
●​ Il est facile à comprendre pour un débutant.​

●​ Il est largement utilisé dans les tutos, entreprises, et documentations.​

●​ Il permet de partager facilement l’état des tâches dans toute l’application.​

🛠️ À retenir
setState() 🟡 Pour un widget simple, local
Provider (ou 🟢 Pour gérer un état partagé,
complexe
autre)

Dans la prochaine section, on va apprendre à utiliser Provider pour gérer


proprement les tâches dans TaskZen.

☘️ 11.3 – Introduction à Provider


Dans une application Flutter comme TaskZen, dès qu’on veut partager des
données (l’état) entre plusieurs widgets ou écrans, il faut éviter les bricolages
avec setState() partout.

👉 C’est là qu’entre en jeu Provider, un outil léger et puissant pour centraliser et


notifier automatiquement l’interface quand l’état change.

🧠 C’est quoi Provider (en version simple) ?


Provider est comme un serveur de données dans ton app.

Tu lui dis : “Voici mes données (ex: la liste de tâches)”,​


puis n’importe quel widget dans l’app peut consommer ces données​
et sera automatiquement prévenu si elles changent.

📦 Étapes simples pour utiliser Provider


1. 📥 Installer Provider
Ajoute ceci dans le fichier pubspec.yaml :

dependencies:​
flutter:​
sdk: flutter​
provider: ^6.1.0

Puis exécute :

flutter pub get

2. 🧱 Créer une classe de gestion d’état


Exemple : TaskProvider pour gérer les tâches dans TaskZen.

import 'package:flutter/material.dart';​

class TaskProvider extends ChangeNotifier {​
List<String> _taches = [];​

List<String> get taches => _taches;​

void ajouterTache(String tache) {​
_taches.add(tache);​
notifyListeners(); // prévient les widgets abonnés​
}​

void supprimerTache(String tache) {​
_taches.remove(tache);​
notifyListeners();​
}​
}

🔔 notifyListeners() = "Hey, les widgets, mettez-vous à jour !"


3. 🏁 Envelopper l’app avec Provider
Dans main.dart :

void main() {​
runApp(​
ChangeNotifierProvider(​
create: (_) => TaskProvider(),​
child: MyApp(),​
),​
);​
}

ChangeNotifierProvider permet d’injecter notre TaskProvider


dans toute l’app.

4. 🧩 Accéder aux données dans un widget


import 'package:provider/provider.dart';

class ListeDesTaches extends StatelessWidget {​


@override​
Widget build(BuildContext context) {​
final taches = context.watch<TaskProvider>().taches;​

return ListView.builder(​
itemCount: taches.length,​
itemBuilder: (context, index) => ListTile(​
title: Text(taches[index]),​
),​
);​
}​
}​

🔍 context.watch<T>() permet de réagir aux changements


automatiquement.

5. ➕ Ajouter une tâche


ElevatedButton(​
onPressed: () {​
context.read<TaskProvider>().ajouterTache("Nouvelle tâche");​
},​
child: Text("Ajouter une tâche"),​
)

🔁 read<T>() = j’utilise le Provider sans écouter les changements.


🎯 Résultat ?
✔️✔️L’état est centralisé dans un seul fichier.​

✔️ Les widgets se mettent à jour automatiquement quand les données changent.​


Ton code est plus propre, maintenable et évolutif.

✅ 11.4 – Gérer l’état des tâches avec Provider


(TaskZen)
L’objectif ici est de :

●​ Centraliser la gestion des tâches,​

●​ Réagir automatiquement dans l’interface dès qu’on ajoute ou supprime une


tâche,​

●​ Préparer le terrain pour SQLite, Firebase, etc.​

🧱 Étape 1 : Créer le fichier task_provider.dart


Dans ton dossier lib/providers/, crée ce fichier :

import 'package:flutter/material.dart';​

class Task {​
final String titre;​
bool estTerminee;​

Task({required this.titre, this.estTerminee = false});​
}​

class TaskProvider extends ChangeNotifier {​
List<Task> _taches = [];​

List<Task> get taches => _taches;​

void ajouterTache(String titre) {​
_taches.add(Task(titre: titre));​
notifyListeners();​
}​

void supprimerTache(Task tache) {​
_taches.remove(tache);​
notifyListeners();​
}​

void basculerEtatTache(Task tache) {​
tache.estTerminee = !tache.estTerminee;​
notifyListeners();​
}​
}​

🧠 Task est notre modèle simple de tâche. On y reviendra avec SQLite


pour le persister.

🧱 Étape 2 : Enregistrer le provider dans main.dart


Dans main.dart, importe et enveloppe l’app :

import 'package:flutter/material.dart';​
import 'package:provider/provider.dart';​
import 'providers/task_provider.dart';​
import 'screens/home_screen.dart';​

void main() {​
runApp(​
ChangeNotifierProvider(​
create: (_) => TaskProvider(),​
child: TaskZenApp(),​
),​
);​
}​

class TaskZenApp extends StatelessWidget {​
@override​
Widget build(BuildContext context) {​
return MaterialApp(​
title: 'TaskZen',​
home: HomeScreen(),​
);​
}​
}

🧩 Étape 3 : Utiliser le provider dans home_screen.dart


Dans lib/screens/home_screen.dart :

import 'package:flutter/material.dart';​
import 'package:provider/provider.dart';​
import '../providers/task_provider.dart';​

class HomeScreen extends StatelessWidget {​
final TextEditingController _controller =
TextEditingController();​

@override​
Widget build(BuildContext context) {​
final provider = Provider.of<TaskProvider>(context);​
final taches = provider.taches;​

return Scaffold(​
appBar: AppBar(title: Text('Mes Tâches')),​
body: Column(​
children: [​
Padding(​
padding: EdgeInsets.all(12),​
child: TextField(​
controller: _controller,​
decoration: InputDecoration(​
labelText: 'Nouvelle tâche',​
suffixIcon: IconButton(​
icon: Icon(Icons.add),​
onPressed: () {​
if (_controller.text.isNotEmpty) {​
provider.ajouterTache(_controller.text);​
_controller.clear();​
}​
},​
),​
),​
),​
),​
Expanded(​
child: ListView.builder(​
itemCount: taches.length,​
itemBuilder: (context, index) {​
final t = taches[index];​
return ListTile(​
title: Text(​
t.titre,​
style: TextStyle(​
decoration: t.estTerminee ?
TextDecoration.lineThrough : null,​
),​
),​
leading: Checkbox(​
value: t.estTerminee,​
onChanged: (_) =>
provider.basculerEtatTache(t),​
),​
trailing: IconButton(​
icon: Icon(Icons.delete),​
onPressed: () => provider.supprimerTache(t),​
),​
);​
},​
),​
),​
],​
),​
);​
}​
}

✅ Résultat (TaskZen v1 complet)


●​ ✅ Ajouter une tâche​
●​ ✅ Marquer comme terminée​

●​ ✅ Supprimer une tâche​

●​ ✅ L’interface réagit automatiquement (Provider)​

🧠 Et ensuite ?
Cette version de TaskZen garde les tâches en mémoire (temporairement).​
Si tu fermes l’app, tout disparaît.

👉 C’est ce qu’on va résoudre dans le prochain chapitre avec SQLite.

🔄 11.5 – Gérer l’état avec Bloc


🧠 Bloc, c’est quoi ?
Bloc (Business Logic Component) est une architecture de gestion d’état
basée sur un flux d’événements et de réactions contrôlées.

Imagine que ton app est comme une usine :

●​ Un événement entre (ex : ajouter une tâche),​

●​ Le Bloc traite cet événement,​

●​ Un état mis à jour sort et est envoyé à l’interface.​

Bloc est très utilisé dans les grandes apps scalables car :
●​ Il sépare clairement la logique de l’interface,​

●​ Il centralise l’état,​

●​ Il facilite les tests unitaires.​

🔧 Étape 1 : Installer les packages nécessaires


Dans pubspec.yaml :

dependencies:​
flutter_bloc: ^8.1.3​
equatable: ^2.0.5

Puis :

flutter pub get

🧱 Étape 2 : Créer les fichiers Bloc


Dans lib/bloc/, crée 3 fichiers :

✅ task_event.dart
import 'package:equatable/equatable.dart';​

abstract class TaskEvent extends Equatable {​
const TaskEvent();​
}​

class AjouterTache extends TaskEvent {​
final String titre;​
const AjouterTache(this.titre);​

@override​
List<Object> get props => [titre];​
}​

class SupprimerTache extends TaskEvent {​
final int index;​
const SupprimerTache(this.index);​

@override​
List<Object> get props => [index];​
}

✅ task_state.dart
import 'package:equatable/equatable.dart';​

class TaskState extends Equatable {​
final List<String> taches;​

const TaskState(this.taches);​

@override​
List<Object> get props => [taches];​
}

✅ task_bloc.dart
import 'package:flutter_bloc/flutter_bloc.dart';​
import 'task_event.dart';​
import 'task_state.dart';​

class TaskBloc extends Bloc<TaskEvent, TaskState> {​
TaskBloc() : super(const TaskState([])) {​
on<AjouterTache>((event, emit) {​
final updated =
List<String>.from(state.taches)..add(event.titre);​
emit(TaskState(updated));​
});​

on<SupprimerTache>((event, emit) {​
final updated =
List<String>.from(state.taches)..removeAt(event.index);​
emit(TaskState(updated));​
});​
}​
}​

🧩 Étape 3 : Intégrer le Bloc dans l’app


Dans main.dart :

import 'package:flutter_bloc/flutter_bloc.dart';​
import 'bloc/task_bloc.dart';​

void main() {​
runApp(​
BlocProvider(​
create: (_) => TaskBloc(),​
child: TaskZenApp(),​
),​
);​
}

🎯 Étape 4 : Utiliser Bloc dans un écran


Exemple simplifié :

BlocBuilder<TaskBloc, TaskState>(​
builder: (context, state) {​
return ListView.builder(​
itemCount: state.taches.length,​
itemBuilder: (context, index) {​
final tache = state.taches[index];​
return ListTile(​
title: Text(tache),​
trailing: IconButton(​
icon: Icon(Icons.delete),​
onPressed: () {​
context.read<TaskBloc>().add(SupprimerTache(index));​
},​
),​
);​
},​
);​
},​
)

Et pour ajouter une tâche :

ElevatedButton(​
onPressed: () {​
context.read<TaskBloc>().add(AjouterTache("Nouvelle tâche"));​
},​
child: Text("Ajouter"),​
)

✅ Résumé Bloc
Avantages Inconvénients

💼 Très structuré 📚 Courbe d’apprentissage plus raide


♻️ Facilement testable 📄 Plus de code “boilerplate”
🔄 Flux d’événements
clairs
🧱 Peut être surdimensionné pour petits
projets

⚡️ 11.6 – Gérer l’état avec GetX


🚀 GetX, c’est quoi ?
GetX est un framework tout-en-un pour Flutter, qui facilite non seulement la gestion
d’état, mais aussi la navigation, les injections de dépendances, etc.
Pour la gestion d’état, GetX mise sur la réactivité : les variables observables (Rx)
qui déclenchent automatiquement la mise à jour de l’interface quand elles changent.

🔧 Étape 1 : Ajouter GetX dans pubspec.yaml


dependencies:​
get: ^4.6.5

Puis :

flutter pub get

🧑‍🔧 Étape 2 : Créer un contrôleur


Le contrôleur contiendra la logique et les variables observables.

Exemple simple task_controller.dart :

import 'package:get/get.dart';​

class TaskController extends GetxController {​
var taches = <String>[].obs;​

void ajouterTache(String titre) {​
taches.add(titre);​
}​

void supprimerTache(int index) {​
taches.removeAt(index);​
}​
}​

🔗 Étape 3 : Utiliser le contrôleur dans l’interface


Dans le widget :
import 'package:get/get.dart';​
import 'task_controller.dart';​

class TaskListPage extends StatelessWidget {​
final TaskController controller = Get.put(TaskController());​

@override​
Widget build(BuildContext context) {​
return Scaffold(​
appBar: AppBar(title: Text('TaskZen')),​
body: Obx(() {​
return ListView.builder(​
itemCount: controller.taches.length,​
itemBuilder: (context, index) {​
return ListTile(​
title: Text(controller.taches[index]),​
trailing: IconButton(​
icon: Icon(Icons.delete),​
onPressed: () {​
controller.supprimerTache(index);​
},​
),​
);​
},​
);​
}),​
floatingActionButton: FloatingActionButton(​
onPressed: () {​
controller.ajouterTache('Nouvelle tâche');​
},​
child: Icon(Icons.add),​
),​
);​
}​
}

🔍 Comment ça marche ?
●​ taches est une liste observable (.obs), dès qu’elle change, tous les
widgets Obx qui la regardent se reconstruisent automatiquement.​

●​ Pas besoin de setState() ou de beaucoup de boilerplate.​

●​ Le contrôleur est accessible partout grâce à Get.put() (injection de


dépendance simple).​

✅ Résumé GetX
Avantages Inconvénients

📦 Très simple à utiliser 🔒 Peu conventionnel, pas toujours évident pour


grands projets

⚡ Réactivité native très


efficace
📚 Moins adapté aux architectures complexes

🔗 Navigation, injection
intégrées
Chapitre 12 – Sauvegarder localement avec SQLite

12.1 – Pourquoi persister les données ?


Dans une application comme TaskZen, tu as besoin que tes tâches restent
disponibles même après avoir fermé ou redémarré l’application.

Pourquoi ?
●​ Données temporaires en mémoire disparaissent dès que tu fermes l’app.​

●​ Persister les données permet de sauvegarder localement ce que l’utilisateur


a créé ou modifié.​

●​ Cela améliore l’expérience utilisateur : ses tâches sont conservées, même


sans connexion internet.​

●​ C’est la base avant de passer à une sauvegarde plus avancée (cloud,


Firebase).​

Exemple concret :
Imagine que tu ajoutes une tâche "Appeler le médecin" puis tu quittes l’app. Si tu ne
sauvegardes pas, la tâche sera perdue. En sauvegardant dans une base locale, elle
sera toujours là à la prochaine ouverture.

12.2 – Utiliser sqflite pour stocker les tâches


Qu’est-ce que sqflite ?

●​ C’est la bibliothèque Flutter la plus populaire pour utiliser SQLite, une


base de données locale légère et embarquée.​

●​ SQLite fonctionne comme un mini serveur de base de données intégré


directement dans l’app, pas besoin d’installer quoi que ce soit.​

●​ sqflite permet de créer des tables, insérer, lire, mettre à jour et


supprimer des données.​

Pourquoi SQLite ?
●​ C’est simple à mettre en place et efficace pour stocker des données
structurées.​

●​ Parfait pour une app comme TaskZen qui a des listes de tâches à gérer.​

Étapes clés avec sqflite dans Flutter :

1.​ Ajouter la dépendance dans pubspec.yaml :​

dependencies:​
sqflite: ^2.0.0+4​
path: ^1.8.3

2.​ Initialiser la base de données :​


On ouvre (ou crée) une base locale avec un chemin sur l’appareil.​

3.​ Créer une table "tasks" avec des colonnes (id, titre, description, statut).​

4.​ Effectuer des opérations CRUD :​

○​ insert() pour ajouter une tâche,​

○​ query() pour lire,​

○​ update() pour modifier,​

○​ delete() pour supprimer.​

Exemple simple d’init de base dans Flutter :


import 'package:sqflite/sqflite.dart';​
import 'package:path/path.dart';​

Future<Database> initDatabase() async {​
String path = join(await getDatabasesPath(), 'taskzen.db');​
return openDatabase(​
path,​
onCreate: (db, version) {​
return db.execute(​
'CREATE TABLE tasks(id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT, isDone INTEGER)',​
);​
},​
version: 1,​
);​
}

12.3 – CRUD complet (Create, Read, Update, Delete)


Le CRUD, c’est l’ensemble des opérations de base pour gérer des données dans
une base SQLite. Voici comment on les applique à nos tâches dans TaskZen :

1. Create (Créer une tâche)

On ajoute une nouvelle tâche dans la table tasks.

Future<void> insertTask(Database db, Map<String, dynamic> task)


async {​
await db.insert(​
'tasks',​
task,​
conflictAlgorithm: ConflictAlgorithm.replace,​
);​
}

Exemple d’une tâche à insérer :

{​
'title': 'Acheter du lait',​
'isDone': 0, // 0 = non fait, 1 = fait​
}

2. Read (Lire les tâches)

On récupère toutes les tâches pour les afficher.

Future<List<Map<String, dynamic>>> getTasks(Database db) async {​


return await db.query('tasks');​
}​
3. Update (Modifier une tâche)
Pour changer le statut d’une tâche ou modifier son titre :

Future<void> updateTask(Database db, Map<String, dynamic> task)


async {​
await db.update(​
'tasks',​
task,​
where: 'id = ?',​
whereArgs: [task['id']],​
);​
}

4. Delete (Supprimer une tâche)

Pour effacer une tâche de la base :

Future<void> deleteTask(Database db, int id) async {​


await db.delete(​
'tasks',​
where: 'id = ?',​
whereArgs: [id],​
);​
}

Résumé visuel :

Opération Méthode SQLite Exemple Flutter

Create INSERT db.insert('tasks', task)


Read SELECT db.query('tasks')

Update UPDATE db.update('tasks', task,


where...)

Delete DELETE db.delete('tasks',


where...)

12.4 – Adapter l’UI à la base de données


Maintenant que nos données sont stockées dans SQLite, il faut faire en sorte que
l’interface utilisateur (UI) reflète les données réelles. En clair : afficher les tâches
qui viennent de la base, et mettre à jour l’affichage dès qu’on ajoute, modifie ou
supprime une tâche.

Les étapes clés :

1. Charger les tâches depuis SQLite au démarrage

●​ Lors du lancement de l’app, on récupère la liste des tâches avec la fonction


getTasks().​

●​ Cette liste sera stockée dans un état (State) pour que Flutter puisse réagir
aux changements.​

2. Afficher la liste des tâches avec un widget dynamique

●​ Utiliser un widget comme ListView.builder pour afficher toutes les


tâches récupérées.​

●​ Chaque tâche sera représentée par un widget (exemple : ListTile) avec un


titre, et une checkbox pour marquer comme terminée.​

3. Gérer les ajouts, modifications et suppressions

●​ Quand on ajoute une tâche via un formulaire, on insère dans SQLite, puis on
recharge la liste et on appelle setState() pour rafraîchir l’UI.​
●​ Même logique pour modifier ou supprimer : mise à jour en base puis
rafraîchissement de l’interface.​

4. Exemple simplifié :
class TaskListPage extends StatefulWidget {​
@override​
_TaskListPageState createState() => _TaskListPageState();​
}​

class _TaskListPageState extends State<TaskListPage> {​
List<Map<String, dynamic>> _tasks = [];​
Database? _db;​

@override​
void initState() {​
super.initState();​
_initDbAndLoadTasks();​
}​

Future<void> _initDbAndLoadTasks() async {​
_db = await initDatabase(); // fonction d'init vue
précédemment​
_loadTasks();​
}​

Future<void> _loadTasks() async {​
final tasks = await getTasks(_db!);​
setState(() {​
_tasks = tasks;​
});​
}​

// Méthodes pour ajouter, modifier, supprimer tâches ici...​

@override​
Widget build(BuildContext context) {​
return Scaffold(​
appBar: AppBar(title: Text('TaskZen')),​
body: ListView.builder(​
itemCount: _tasks.length,​
itemBuilder: (context, index) {​
final task = _tasks[index];​
return ListTile(​
title: Text(task['title']),​
trailing: Checkbox(​
value: task['isDone'] == 1,​
onChanged: (value) async {​
// Mettre à jour statut dans la DB​
await updateTask(_db!, {​
'id': task['id'],​
'title': task['title'],​
'isDone': value! ? 1 : 0,​
});​
_loadTasks(); // rafraîchir UI​
},​
),​
);​
},​
),​
floatingActionButton: FloatingActionButton(​
onPressed: () {​
// Logique pour ajouter une tâche​
},​
child: Icon(Icons.add),​
),​
);​
}​
}

En résumé

●​ La base SQLite contient les données “officielles”.​

●​ L’UI reflète ces données et se met à jour en fonction.​

●​ C’est la base pour construire une app TaskZen réactive et persistante.​


Chapitre 13 – Travailler avec les flux (Streams)

13.1 – Comprendre les Stream et Future


C’est quoi un Future ?

Un Future, c’est comme une promesse. Tu demandes à Flutter de faire quelque


chose qui prend du temps (ex : récupérer des données), et Flutter te promet de te
donner la réponse plus tard.

●​ Pendant que Flutter prépare la réponse, ton app peut continuer à tourner.​

●​ Quand la réponse arrive, Flutter exécute une action (callback) pour te donner
le résultat.​

Exemple simple : demander la météo à une API, et attendre la réponse.

C’est quoi un Stream ?

Un Stream est comme une rivière de données qui arrive petit à petit, au fil du temps.

●​ Tu t’abonnes à ce flux.​

●​ Tu reçois les données au fur et à mesure qu’elles arrivent.​

●​ Idéal pour gérer des données dynamiques ou continues (ex : notifications,


chat, mises à jour temps réel).​

Analogie terre à terre

●​ Future = commander un plat au resto, tu attends que le plat arrive (une fois).​

●​ Stream = un robinet qui coule, tu peux recevoir plusieurs verres d’eau


(données) un par un.​

Dans Flutter
●​ Pour afficher un Future, on utilise un widget FutureBuilder.​

●​ Pour afficher un Stream, on utilise un widget StreamBuilder.​

13.2 – Intégration dans Flutter avec StreamBuilder et


FutureBuilder
Pourquoi ces widgets ?

Dans Flutter, quand tu as un Future ou un Stream, tu ne veux pas juste récupérer


les données, tu veux aussi gérer l’affichage selon l’état :

●​ Chargement (en attente de la donnée)​

●​ Donnée reçue (affichage normal)​

●​ Erreur (affichage d’un message)​

FutureBuilder et StreamBuilder font tout ça pour toi.

Exemple simple avec FutureBuilder

Supposons que tu veux afficher une tâche récupérée après un délai (Future) :

Future<String> fetchTask() async {​


await Future.delayed(Duration(seconds: 2)); // Simule un délai​
return "Acheter du lait";​
}​

Widget build(BuildContext context) {​
return FutureBuilder<String>(​
future: fetchTask(),​
builder: (context, snapshot) {​
if (snapshot.connectionState == ConnectionState.waiting) {​
return CircularProgressIndicator();​
} else if (snapshot.hasError) {​
return Text("Erreur : ${snapshot.error}");​
} else if (snapshot.hasData) {​
return Text("Tâche : ${snapshot.data}");​
} else {​
return Text("Aucune donnée");​
}​
},​
);​
}​

Exemple simple avec StreamBuilder

Imaginons un flux de tâches (Stream) qui arrive au fil du temps :

Stream<String> taskStream() async* {​


await Future.delayed(Duration(seconds: 1));​
yield "Tâche 1";​
await Future.delayed(Duration(seconds: 2));​
yield "Tâche 2";​
}​

Widget build(BuildContext context) {​
return StreamBuilder<String>(​
stream: taskStream(),​
builder: (context, snapshot) {​
if (snapshot.connectionState == ConnectionState.waiting) {​
return CircularProgressIndicator();​
} else if (snapshot.hasError) {​
return Text("Erreur : ${snapshot.error}");​
} else if (snapshot.hasData) {​
return Text("Nouvelle tâche : ${snapshot.data}");​
} else {​
return Text("Aucune donnée");​
}​
},​
);​
}

En résumé

●​ FutureBuilder = pour un événement unique dans le futur​


●​ StreamBuilder = pour un flux continu de données​
Chapitre 14 – Connexion à une API distante

14.1 – Utiliser http pour envoyer/recevoir des données


Pourquoi utiliser le package http ?

Quand on crée une app comme TaskZen, on a souvent besoin de communiquer


avec des services web (API) pour récupérer ou envoyer des données.

Flutter ne fournit pas cette fonctionnalité directement, mais il existe un package très
simple à utiliser : http.

Ajouter le package http

Dans ton fichier pubspec.yaml, ajoute :

dependencies:​
http: ^0.13.5

Puis lance dans le terminal :

flutter pub get

Faire une requête GET simple

Voici un exemple pour récupérer des données depuis une API publique (ici, une
fausse API qui retourne des tâches) :

import 'package:http/http.dart' as http;​


import 'dart:convert';​

Future<List<String>> fetchTasks() async {​
final response = await
http.get(Uri.parse('https://jsonplaceholder.typicode.com/todos'));​

if (response.statusCode == 200) {​
final List<dynamic> jsonData = json.decode(response.body);​
// On récupère uniquement les titres des tâches​
return jsonData.map((task) => task['title'] as
String).toList();​
} else {​
throw Exception('Échec du chargement des tâches');​
}​
}​

Explications simples

●​ http.get envoie une requête GET.​

●​ On récupère la réponse dans response.​

●​ Si le code HTTP est 200 (succès), on décode le JSON avec json.decode.​

●​ On transforme la liste JSON en une liste Dart simple (titres des tâches).​

●​ Sinon, on lance une erreur.​

14.2 – Parser une réponse JSON


Pourquoi parser le JSON ?

Quand une API nous renvoie des données, elles sont souvent au format JSON, un
format texte très courant.

Pour pouvoir utiliser ces données dans Flutter (Dart), il faut les convertir en objets
Dart (parsing).

Exemple concret avec TaskZen

Supposons que l’API nous renvoie une liste de tâches sous cette forme JSON :

[​
{​
"id": 1,​
"title": "Acheter du lait",​
"completed": false​
},​
{​
"id": 2,​
"title": "Faire les courses",​
"completed": true​
}​
]

On va créer une classe Dart qui correspond à cette structure :

class Task {​
final int id;​
final String title;​
final bool completed;​

Task({required this.id, required this.title, required
this.completed});​

// Méthode pour créer une instance Task à partir d'un JSON (Map)​
factory Task.fromJson(Map<String, dynamic> json) {​
return Task(​
id: json['id'],​
title: json['title'],​
completed: json['completed'],​
);​
}​
}​

Transformer la réponse HTTP en liste de tâches Dart

Voici comment modifier la fonction fetchTasks pour parser proprement la réponse


JSON en liste d’objets Task :

Future<List<Task>> fetchTasks() async {​


final response = await
http.get(Uri.parse('https://jsonplaceholder.typicode.com/todos'));​

if (response.statusCode == 200) {​
final List<dynamic> jsonData = json.decode(response.body);​
return jsonData.map((json) => Task.fromJson(json)).toList();​
} else {​
throw Exception('Échec du chargement des tâches');​
}​
}

Résumé simple

●​ Le JSON est une chaîne de caractères.​

●​ On la décode en une structure de données (liste ou map).​

●​ On crée des objets Dart à partir de chaque élément JSON.​

●​ Cela facilite la manipulation dans le code.​

14.3 – Afficher des données récupérées depuis une API


Maintenant qu’on sait comment récupérer des données d'une API et les parser en
objets Dart (Task), il est temps de les afficher dans notre application Flutter.

Objectif
Afficher la liste des tâches (fictives ou réelles) obtenues depuis une API distante,
directement dans l’interface de TaskZen.

Utiliser un FutureBuilder

Flutter propose un widget très pratique pour gérer des appels asynchrones (comme
une requête HTTP) : FutureBuilder.

Voici un exemple simple :

class TaskListPage extends StatelessWidget {​


const TaskListPage({Key? key}) : super(key: key);​

@override​
Widget build(BuildContext context) {​
return Scaffold(​
appBar: AppBar(title: Text('Tâches depuis l\'API')),​
body: FutureBuilder<List<Task>>(​
future: fetchTasks(),​
builder: (context, snapshot) {​
if (snapshot.connectionState == ConnectionState.waiting)
{​
return Center(child: CircularProgressIndicator());​
} else if (snapshot.hasError) {​
return Center(child: Text('Erreur :
${snapshot.error}'));​
} else if (!snapshot.hasData || snapshot.data!.isEmpty)
{​
return Center(child: Text('Aucune tâche trouvée.'));​
} else {​
final tasks = snapshot.data!;​
return ListView.builder(​
itemCount: tasks.length,​
itemBuilder: (context, index) {​
final task = tasks[index];​
return ListTile(​
title: Text(task.title),​
trailing: Icon(​
task.completed ? Icons.check_circle :
Icons.circle_outlined,​
color: task.completed ? Colors.green :
Colors.grey,​
),​
);​
},​
);​
}​
},​
),​
);​
}​
}

Ce que fait ce code :

●​ FutureBuilder appelle fetchTasks() et attend la réponse.​

●​ Pendant le chargement → CircularProgressIndicator.​


●​ Si erreur → affiche le message d’erreur.​

●​ Si succès → affiche une ListView avec les tâches.​

Résultat visuel dans TaskZen

Tu obtiens une belle liste des tâches, dynamique, issue de l’API. C’est une vraie
connexion réseau, donc chaque fois que l’utilisateur ouvre cette page, il voit les
données à jour.
☁️ PARTIE 4 – Devenir Senior : maîtriser Flutter
comme un pro

Chapitre 15 – Intégration Firebase (Cloud


Firestore)

🔥 15.1 – Créer un projet Firebase


🧠 Pourquoi Firebase ?
Firebase est une plateforme de Google qui fournit plusieurs services backend «

➡️
prêts à l'emploi » pour nos apps :​

➡️ base de données (Firestore),​

➡️
➡️
authentification,​
notifications,​
stockage de fichiers, etc.

C’est parfait pour faire évoluer TaskZen vers une version connectée et
collaborative.

🔧 Étape 1 – Créer un projet Firebase


1.​ Va sur https://console.firebase.google.com​

2.​ Clique sur Ajouter un projet​

3.​ Donne un nom à ton projet (ex : taskzen-firebase)​

4.​ Désactive Google Analytics (optionnel, selon les besoins)​

5.​ Clique sur Créer un projet​

⏳ Attends quelques secondes… Firebase configure tout pour toi.


🧩 Étape 2 – Ajouter Firebase à ton app Flutter (Android)
1.​ Dans le dashboard de Firebase, clique sur l’icône Android pour enregistrer
ton app.​
2.​ Remplis les champs :​

○​ Nom du package : identique à celui dans


android/app/build.gradle (ex : com.montaskzen.app)​

○​ (optionnel) Nom d’application : TaskZen​

3.​ Clique sur Enregistrer l’app​

📦 Étape 3 – Télécharger le fichier google-services.json


1.​ Une fois ton app enregistrée, Firebase te propose de télécharger un fichier
google-services.json​

2.​ Place ce fichier dans ton projet Flutter ici :​


android/app/google-services.json​

⚙️ Étape 4 – Modifier les fichiers Android


Dans android/build.gradle (niveau racine), ajoute :

buildscript {​
dependencies {​
classpath 'com.google.gms:google-services:4.3.15' // ou
version la plus récente​
}​
}

Dans android/app/build.gradle, en bas du fichier, ajoute :

apply plugin: 'com.google.gms.google-services'

🧪 Étape 5 – Installer les dépendances Firebase dans Flutter


Ajoute dans ton fichier pubspec.yaml :

dependencies:​
firebase_core: ^latest

Et dans ton code Dart (main.dart), initialise Firebase :

void main() async {​


WidgetsFlutterBinding.ensureInitialized();​
await Firebase.initializeApp();​
runApp(TaskZenApp());​
}

N'oublie pas d’importer :

import 'package:firebase_core/firebase_core.dart';

✅Tu Ton app Flutter est maintenant connectée à ton projet Firebase !​
peux maintenant utiliser tous les services Firebase (Firestore, Auth, etc.).

🔗 15.2 – Connexion de l’app à Firebase


Maintenant que ton projet Firebase est bien créé et connecté à ton app Flutter, on va
intégrer Cloud Firestore, la base de données NoSQL en temps réel proposée par
Firebase.

Notre objectif ici est de permettre à TaskZen de sauvegarder les tâches dans le
cloud et de pouvoir les consulter depuis n’importe quel appareil.

🗃️ Étape 1 – Ajouter Firestore dans pubspec.yaml


Ajoute la dépendance cloud_firestore :

dependencies:​
cloud_firestore: ^latest

Puis exécute :

flutter pub get


📁 Étape 2 – Créer une collection "tasks"
Dans la console Firebase :

1.​ Clique sur "Firestore Database" dans le menu gauche​

2.​ Clique sur "Créer une base de données"​

○​ Mode de démarrage : sélectionne Mode test ( ⚠️ accès libre)​


○​ Emplacement : choisis une région proche (ex : europe-west1)​

3.​ Une fois la base créée, clique sur "Commencer la collection"​

○​ Nom de la collection : tasks​

○​ Ajoute un premier document exemple avec des champs comme :​

■​ title: "Apprendre Flutter"​

■​ completed: false​

📄 Étape 3 – Créer un modèle de tâche en Dart


Dans ton app, crée un fichier task_model.dart :

class Task {​
String id;​
String title;​
bool completed;​

Task({required this.id, required this.title, this.completed =
false});​

factory Task.fromMap(Map<String, dynamic> data, String
documentId) {​
return Task(​
id: documentId,​
title: data['title'] ?? '',​
completed: data['completed'] ?? false,​
);​
}​

Map<String, dynamic> toMap() {​
return {​
'title': title,​
'completed': completed,​
};​
}​
}

📥 Étape 4 – Lire les tâches depuis Firestore


Crée un service task_service.dart :

import 'package:cloud_firestore/cloud_firestore.dart';​
import 'task_model.dart';​

class TaskService {​
final CollectionReference _tasksRef =
FirebaseFirestore.instance.collection('tasks');​

Stream<List<Task>> getTasks() {​
return _tasksRef.snapshots().map((snapshot) {​
return snapshot.docs.map((doc) {​
return Task.fromMap(doc.data() as Map<String, dynamic>,
doc.id);​
}).toList();​
});​
}​
}​

🧪 Étape 5 – Afficher les tâches dans ton app (extrait)


Dans ta TaskListView, tu peux utiliser un StreamBuilder :

final taskService = TaskService();​



StreamBuilder<List<Task>>(​
stream: taskService.getTasks(),​
builder: (context, snapshot) {​
if (snapshot.hasData) {​
final tasks = snapshot.data!;​
return ListView.builder(​
itemCount: tasks.length,​
itemBuilder: (context, index) {​
final task = tasks[index];​
return ListTile(​
title: Text(task.title),​
trailing: Icon(​
task.completed ? Icons.check_circle :
Icons.circle_outlined,​
color: task.completed ? Colors.green : null,​
),​
);​
},​
);​
} else {​
return CircularProgressIndicator();​
}​
},​
)

🔥 Bravo ! Tu viens de connecter TaskZen à Firestore et de lire les tâches en live


depuis le cloud.

📝 15.3 – Stockage des tâches dans Firestore (ajouter,


modifier, supprimer)
Maintenant que TaskZen lit les tâches depuis Firestore, il est temps de les
enregistrer, les modifier et les supprimer dynamiquement dans la base.

✅ Étape 1 – Ajouter une tâche


Dans task_service.dart, ajoute une méthode addTask :

Future<void> addTask(Task task) async {​


await _tasksRef.add(task.toMap());​
}
Exemple d’utilisation dans un formulaire de création :

final newTask = Task(id: '', title: 'Nouvelle tâche');​


await taskService.addTask(newTask);

🔄 Étape 2 – Modifier une tâche


Ajoute dans task_service.dart :

Future<void> updateTask(Task task) async {​


await _tasksRef.doc(task.id).update(task.toMap());​
}

Tu peux l’utiliser pour cocher/décocher une tâche :

task.completed = !task.completed;​
await taskService.updateTask(task);

❌ Étape 3 – Supprimer une tâche


Toujours dans task_service.dart :

Future<void> deleteTask(String taskId) async {​


await _tasksRef.doc(taskId).delete();​
}

Utilisation :

await taskService.deleteTask(task.id);

🧪 Exemple d’interface (mini)


Dans un ListTile, tu peux ajouter des actions :

ListTile(​
title: Text(task.title),​
trailing: Row(​
mainAxisSize: MainAxisSize.min,​
children: [​
IconButton(​
icon: Icon(Icons.check),​
onPressed: () => taskService.updateTask(task),​
),​
IconButton(​
icon: Icon(Icons.delete),​
onPressed: () => taskService.deleteTask(task.id),​
),​
],​
),​
)

Et un bouton flottant pour ajouter une tâche :

FloatingActionButton(​
child: Icon(Icons.add),​
onPressed: () async {​
final task = Task(id: '', title: 'Nouvelle tâche');​
await taskService.addTask(task);​
},​
),

🧠 Tips pour plus tard :


●​ Tu peux ajouter une validation du titre avant l'ajout​

●​ Utiliser un TextEditingController pour permettre la saisie personnalisée​

●​ Ajouter un système d’édition avec une boîte de dialogue ou une page dédiée​

✅ Tu sais maintenant ajouter, modifier et supprimer des tâches en ligne avec


Firestore !

🔐 15.4 – Authentification (email + Google)


L’objectif ici est de permettre aux utilisateurs de TaskZen de se connecter avec leur
email/mot de passe ou via leur compte Google, et de lier leurs tâches à leur
propre compte.

⚙️ Étape 1 – Activer l’authentification dans Firebase


1.​ Va sur https://console.firebase.google.com​

2.​ Clique sur Authentication > Méthode de connexion​

3.​ Active :​

○​ Email/mot de passe​

○​ Google (tu devras renseigner un email support et éventuellement


un nom de domaine autorisé pour tester)​

🧩 Étape 2 – Ajouter les dépendances Flutter


Ajoute dans pubspec.yaml :

dependencies:​
firebase_auth: ^latest​
google_sign_in: ^latest

Puis :

flutter pub get

🔌 Étape 3 – Initialiser Firebase Auth


Tu peux créer un fichier auth_service.dart :

import 'package:firebase_auth/firebase_auth.dart';​
import 'package:google_sign_in/google_sign_in.dart';​

class AuthService {​
final FirebaseAuth _auth = FirebaseAuth.instance;​

Stream<User?> get userStream => _auth.authStateChanges();​

Future<UserCredential> signInWithEmail(String email, String
password) {​
return _auth.signInWithEmailAndPassword(email: email,
password: password);​
}​

Future<UserCredential> signUpWithEmail(String email, String
password) {​
return _auth.createUserWithEmailAndPassword(email: email,
password: password);​
}​

Future<UserCredential> signInWithGoogle() async {​
final googleUser = await GoogleSignIn().signIn();​
final googleAuth = await googleUser!.authentication;​

final credential = GoogleAuthProvider.credential(​
accessToken: googleAuth.accessToken,​
idToken: googleAuth.idToken,​
);​

return _auth.signInWithCredential(credential);​
}​

Future<void> signOut() async {​
await _auth.signOut();​
await GoogleSignIn().signOut();​
}​
}​

🧪 Étape 4 – Interface de connexion simple (exemple)


Tu peux créer une page LoginPage avec un bouton de login Google et un
formulaire email :

ElevatedButton(​
onPressed: () async {​
try {​
await AuthService().signInWithGoogle();​
} catch (e) {​
print("Erreur login Google : $e");​
}​
},​
child: Text('Se connecter avec Google'),​
),

Ou un formulaire classique :

TextField(controller: _emailCtrl)​
TextField(controller: _passwordCtrl, obscureText: true)​
ElevatedButton(​
onPressed: () async {​
await AuthService().signInWithEmail(_emailCtrl.text,
_passwordCtrl.text);​
},​
child: Text('Connexion'),​
)

👤 Étape 5 – Protéger l’accès aux tâches


Dans ton écran d’accueil, tu peux afficher les tâches uniquement si l’utilisateur est
connecté :

StreamBuilder<User?>(​
stream: AuthService().userStream,​
builder: (context, snapshot) {​
if (snapshot.hasData) {​
return TaskListPage(); // l'utilisateur est connecté​
} else {​
return LoginPage(); // il doit se connecter​
}​
},​
)

Et dans TaskService, tu peux filtrer les tâches par utilisateur (en liant chaque
tâche à son uid lors de l’ajout).
💡 Optionnel :
●​ Ajouter une page de déconnexion​

●​ Ajouter la gestion des erreurs (email incorrect, utilisateur inexistant, etc.)​

●​ Stocker le uid dans chaque tâche pour filtrer par utilisateur​

🎉 Bravo, TaskZen est maintenant multi-utilisateur sécurisé avec Firebase Auth !


🔔 16.1 – Notifier l’utilisateur (Firebase ou plugin local)
Objectif : permettre à TaskZen d’envoyer des notifications push ou locales pour
rappeler les tâches à effectuer.

On va couvrir 2 approches :

1.​ Notifications locales : via un plugin Flutter (pas besoin d’internet)​

2.​ Notifications push : via Firebase Cloud Messaging (FCM) (requiert une
configuration serveur ou backend cloud)​

✅ Étape 1 – Notifications locales avec


flutter_local_notifications

🔧 1.1 Installer la dépendance


Ajoute dans pubspec.yaml :

dependencies:​
flutter_local_notifications: ^latest

Puis :

flutter pub get

🔧 1.2 Configuration Android et iOS


Android :​
Dans AndroidManifest.xml, ajoute les permissions :

<uses-permission
android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>

Et dans la balise <application> :

<meta-data​

android:name="com.google.firebase.messaging.default_notification_c
hannel_id"​
android:value="task_channel"/>
iOS :​
Dans Info.plist, ajoute :

<key>UIBackgroundModes</key>​
<array>​
<string>fetch</string>​
<string>remote-notification</string>​
</array>

📱 1.3 Initialiser le plugin


Dans un fichier notification_service.dart :

import
'package:flutter_local_notifications/flutter_local_notifications.d
art';​

class NotificationService {​
final _notifications = FlutterLocalNotificationsPlugin();​

Future<void> init() async {​
const android =
AndroidInitializationSettings('@mipmap/ic_launcher');​
const ios = DarwinInitializationSettings();​
const settings = InitializationSettings(android: android, iOS:
ios);​

await _notifications.initialize(settings);​
}​

Future<void> showNotification(String title, String body) async {​
const androidDetails = AndroidNotificationDetails(​
'task_channel', 'TaskZen Notifications',​
importance: Importance.max,​
priority: Priority.high,​
);​
const iosDetails = DarwinNotificationDetails();​

const details = NotificationDetails(android: androidDetails,
iOS: iosDetails);​
await _notifications.show(0, title, body, details);​
}​
}

Dans main.dart, initialise :

await NotificationService().init();

📲 1.4 Utilisation
await NotificationService().showNotification(​
'Rappel',​
'N'oublie pas ta tâche "Acheter du pain" !',​
);

☁️ Étape 2 – Notifications push avec Firebase Cloud Messaging


(optionnel)

Pour aller plus loin (facultatif maintenant, mais essentiel pour une vraie app en
production) :

●​ Ajoute le package firebase_messaging​

●​ Configure FCM sur la console Firebase​

●​ Gère les tokens d’enregistrement​

●​ Reçois les notifications même quand l’app est fermée​

Mais pour l’instant, les notifications locales suffisent pour les rappels de base
(sans serveur).

📌 Résumé :
●​ Les notifications locales sont parfaites pour des rappels d’événements
dans l’app​

●​ Les notifications FCM sont utiles pour envoyer à distance, depuis un


serveur ou Firebase console​
⏰ 16.2 – Créer des rappels de tâches
Objectif : permettre à l’utilisateur de recevoir une notification automatique à l’heure
choisie pour chaque tâche.

🛠️ Étape 1 – Ajouter un champ “date de rappel” à une tâche


Quand l’utilisateur crée une tâche, on lui donne la possibilité de choisir une date et
une heure de rappel (optionnelle).

Exemple dans un formulaire :

DateTime? _reminderDate;​

ElevatedButton(​
onPressed: () async {​
final picked = await showDatePicker(​
context: context,​
initialDate: DateTime.now(),​
firstDate: DateTime.now(),​
lastDate: DateTime.now().add(Duration(days: 365)),​
);​
if (picked != null) {​
final time = await showTimePicker(​
context: context,​
initialTime: TimeOfDay.now(),​
);​
if (time != null) {​
_reminderDate = DateTime(​
picked.year,​
picked.month,​
picked.day,​
time.hour,​
time.minute,​
);​
}​
}​
},​
child: Text('Choisir un rappel'),​
),
📦 Étape 2 – Enregistrer la tâche avec la date de rappel
Dans ton modèle de tâche (ex. TaskModel), ajoute une propriété :

DateTime? reminderDate;

Et dans Firestore ou en local, tu stockes ce champ.

🔔 Étape 3 – Programmer une notification différée


Utilise flutter_local_notifications pour planifier la notification :

Dans NotificationService :

Future<void> scheduleNotification(int id, String title, String


body, DateTime scheduledDate) async {​
const androidDetails = AndroidNotificationDetails(​
'task_channel', 'TaskZen Notifications',​
importance: Importance.max,​
priority: Priority.high,​
);​
const iosDetails = DarwinNotificationDetails();​

await _notifications.zonedSchedule(​
id,​
title,​
body,​
tz.TZDateTime.from(scheduledDate, tz.local),​
NotificationDetails(android: androidDetails, iOS: iosDetails),​
androidScheduleMode: AndroidScheduleMode.exactAllowWhileIdle,​
uiLocalNotificationDateInterpretation:​
UILocalNotificationDateInterpretation.absoluteTime,​
matchDateTimeComponents: DateTimeComponents.dateAndTime,​
);​
}

⚠️ Important :
●​ Il faut ajouter la lib timezone (flutter pub add timezone)​

●​ Appelle tz.initializeTimeZones(); dans main() après avoir importé :​

import 'package:timezone/data/latest.dart' as tz;

🎯 Étape 4 – Lors de la création de la tâche


Si la tâche a une date de rappel, on planifie la notification :

if (_reminderDate != null) {​
await NotificationService().scheduleNotification(​
taskId.hashCode,​
'Rappel : ${task.title}',​
'Tu as une tâche à faire',​
_reminderDate!,​
);​
}

✅ Bonus : supprimer ou modifier un rappel


Quand une tâche est modifiée ou supprimée, pense à annuler la notification :

await _notifications.cancel(taskId.hashCode);

📌 Résumé :
●​ L’utilisateur peut ajouter une date de rappel​

●​ Tu stockes cette date​

●​ Tu programmes une notification locale différée​

●​ Tu peux annuler ou modifier les rappels si besoin​

🎨 16.3 – Thèmes sombres/clairs et personnalisation UI


Objectif : permettre à TaskZen de s’adapter automatiquement au thème du
système ou à un choix utilisateur, tout en assurant une cohérence visuelle.

🧱 Étape 1 – Définir des thèmes personnalisés


Dans main.dart ou un fichier dédié (theme.dart), tu peux définir deux thèmes :

final ThemeData lightTheme = ThemeData(​


brightness: Brightness.light,​
primarySwatch: Colors.indigo,​
scaffoldBackgroundColor: Colors.white,​
appBarTheme: AppBarTheme(backgroundColor: Colors.indigo),​
textTheme: TextTheme(bodyMedium: TextStyle(color:
Colors.black87)),​
);​

final ThemeData darkTheme = ThemeData(​
brightness: Brightness.dark,​
primarySwatch: Colors.indigo,​
scaffoldBackgroundColor: Colors.black,​
appBarTheme: AppBarTheme(backgroundColor: Colors.black),​
textTheme: TextTheme(bodyMedium: TextStyle(color:
Colors.white)),​
);​

🌗 Étape 2 – Gérer automatiquement le thème selon le système


Dans MaterialApp :

return MaterialApp(​
title: 'TaskZen',​
theme: lightTheme,​
darkTheme: darkTheme,​
themeMode: ThemeMode.system, // Ou .light, .dark​
home: HomePage(),​
);
📌 Avec ThemeMode.system, Flutter s’adapte automatiquement au thème du
téléphone de l’utilisateur.

🧠 Étape 3 – Permettre à l’utilisateur de choisir le thème (bonus avancé)


Tu peux proposer un switch manuel dans les paramètres.

1.​ Crée un ThemeProvider avec Provider ou GetX, ou un ValueNotifier.​

Exemple simple avec ValueNotifier<ThemeMode> :

final themeNotifier = ValueNotifier<ThemeMode>(ThemeMode.system);

2.​ Dans MaterialApp, utilise :​

ValueListenableBuilder<ThemeMode>(​
valueListenable: themeNotifier,​
builder: (_, mode, __) {​
return MaterialApp(​
title: 'TaskZen',​
theme: lightTheme,​
darkTheme: darkTheme,​
themeMode: mode,​
home: HomePage(),​
);​
},​
);

3.​ Et dans les paramètres :​

DropdownButton<ThemeMode>(​
value: themeNotifier.value,​
onChanged: (mode) {​
themeNotifier.value = mode!;​
},​
items: [​
DropdownMenuItem(value: ThemeMode.system, child:
Text("Système")),​
DropdownMenuItem(value: ThemeMode.light, child:
Text("Clair")),​
DropdownMenuItem(value: ThemeMode.dark, child:
Text("Sombre")),​
],​
)​

✨ Étape 4 – Personnalisation UI bonus


Quelques idées que tu peux ajouter pour rendre l'app "senior ready" :

●​ Animations douces avec AnimatedContainer, Hero,


AnimatedSwitcher​

●​ UI adaptative pour tablette/desktop avec LayoutBuilder​

●​ Accessibilité : taille de texte réglable​

●​ Typographie personnalisée avec Google Fonts (google_fonts)​

📌 Résumé :
●​ Tu définis un thème clair et un thème sombre​

●​ Tu les appliques dans MaterialApp​

●​ Tu peux laisser le système choisir ou laisser l’utilisateur décider​


Chapitre 17 – Tester son application Flutter

🧪 17.1 – Pourquoi écrire des tests ?


« Un bon développeur code.​
Un développeur senior teste son code. »

🚨 Pourquoi les tests sont-ils importants ?


1.​ Éviter les régressions :​
Chaque fois que tu ajoutes une nouvelle fonctionnalité ou corrige un bug, un
autre bug peut apparaître. Les tests t’alertent immédiatement.​

2.​ Travailler sans peur :​


Tu peux refactorer ton code sans stresser. Les tests te diront si tu as tout
cassé.​

3.​ Documentation vivante :​


Les tests décrivent le comportement attendu de ton code. C’est comme un
manuel vivant.​

4.​ Gagner du temps à long terme :​


Moins de bugs, moins de débogage, plus de temps pour développer des
vraies features.​

🧠 Types de bugs évités grâce aux tests


Cas Exemple sans test Risque

Régression Tu modifies la logique d’ajout de La suppression ne marche


tâche plus

Mauvaise L’utilisateur entre un titre vide L’app plante


saisie

Intégration Tu changes le nom d’une colonne L’UI ne charge plus les


SQLite données
📦 Quels types de tests existe-t-il dans Flutter ?
Flutter te permet trois types de tests complémentaires :

1.​ Test unitaire : tester une fonction ou une classe (logique pure)​

2.​ Test widget : tester un widget dans une UI isolée​

3.​ Test d’intégration : tester l’application entière (comme si c’était un humain)​

On va les voir un par un avec TaskZen 💪


🧪 En résumé :
●​ Les tests te donnent confiance​

●​ Ils rendent ton app fiable, pro et maintenable​

●​ C’est obligatoire en production, même pour une petite app​

🧪 17.2 – Test unitaire


Objectif : Tester la logique métier de ton application TaskZen, sans
interface utilisateur.

🔧 C’est quoi un test unitaire ?


Un test unitaire vérifie une fonction ou une classe isolée pour s'assurer qu'elle
fait exactement ce qu'elle est censée faire.

Exemple : vérifier que addTask() ajoute bien une tâche dans la liste.

📁 1. Organisation des fichiers de test


Par convention, on crée un dossier /test à la racine :

/lib​
/models/task.dart​
/services/task_service.dart​
/test​
/services/task_service_test.dart​

🧪 2. Exemple de test unitaire pour une fonction


Imaginons que tu as une classe TaskService avec une fonction addTask() :

class TaskService {​
List<String> tasks = [];​

void addTask(String task) {​
if (task.isEmpty) throw Exception("Task cannot be empty");​
tasks.add(task);​
}​
}

Voici comment la tester :

import 'package:flutter_test/flutter_test.dart';​
import 'package:taskzen/services/task_service.dart';​

void main() {​
group('TaskService Test', () {​
late TaskService taskService;​

setUp(() {​
taskService = TaskService();​
});​

test('should add a valid task', () {​
taskService.addTask('Apprendre Flutter');​
expect(taskService.tasks.length, 1);​
expect(taskService.tasks.first, 'Apprendre Flutter');​
});​

test('should throw exception when adding empty task', () {​
expect(() => taskService.addTask(''), throwsException);​
});​
});​
}

✅ 3. Exécuter les tests


Dans ton terminal (à la racine du projet) :

flutter test

Tu verras une sortie du type :

00:00 +2: All tests passed!

📌 Résumé :
Étape Actio
n

✅ Crée une classe à tester (logique métier)


✅ Crée un fichier de test correspondant dans
/test

✅ Utilise flutter_test avec test(),


group(), expect()

✅ Lance avec flutter test dans le terminal

🧠 Avantage :
●​ Les tests unitaires sont rapides, faciles à écrire et détectent des bugs avant
même d'ouvrir l'app.​

🧪 17.3 – Test widget


🎯 Objectif
Tester un widget Flutter isolé, pour vérifier son apparence et son comportement
dans une interface, sans lancer toute l’application.

🔍 Pourquoi faire un test widget ?


●​ Vérifier que ton widget affiche bien ce qu’on attend​

●​ Tester des interactions simples (clics, saisies)​

●​ Détecter rapidement des erreurs visuelles ou fonctionnelles​

●​ Assurer que l’UI ne casse pas lors de modifications​

🧩 Exemple simple avec TaskZen


Testons un widget TaskItem qui affiche le titre d’une tâche.

import 'package:flutter_test/flutter_test.dart';​
import 'package:flutter/material.dart';​
import 'package:taskzen/widgets/task_item.dart';​

void main() {​
testWidgets('TaskItem displays task title', (WidgetTester
tester) async {​
// Construire notre widget​
await tester.pumpWidget(MaterialApp(​
home: Scaffold(​
body: TaskItem(title: 'Apprendre Flutter'),​
),​
));​

// Chercher un texte dans le widget​
expect(find.text('Apprendre Flutter'), findsOneWidget);​
});​
}

⚙️ Détails
●​ testWidgets est la fonction pour tester un widget.​

●​ tester.pumpWidget lance le widget dans un environnement de test.​

●​ find.text() recherche un widget texte précis.​

●​ expect() vérifie que ce texte est présent.​

📌 Résumé
Étape Action

✅ Créer un widget simple à tester


✅ Utiliser testWidgets() pour écrire le test
✅ Lancer le widget dans un MaterialApp via
pumpWidget()

✅ Vérifier la présence d’éléments avec find et


expect
🧪 17.4 – Test d’intégration
🎯 Objectif
Tester l’application entière, de bout en bout, comme un utilisateur final :

●​ Vérifier que toutes les pages fonctionnent ensemble​

●​ Tester la navigation, les interactions complexes​

●​ S’assurer que l’app ne plante pas dans des scénarios réels​

🔍 Pourquoi faire un test d’intégration ?


●​ Valider le bon fonctionnement global​

●​ Trouver des bugs liés à l’interaction entre widgets et services​

●​ Automatiser les scénarios d’utilisation (ex: création, modification, suppression


de tâches)​

⚙️ Exemple simple avec TaskZen


Testons un scénario où l’utilisateur ajoute une tâche puis vérifie qu’elle apparaît dans
la liste.

import 'package:flutter_test/flutter_test.dart';​
import 'package:integration_test/integration_test.dart';​
import 'package:taskzen/main.dart' as app;​

void main() {​
IntegrationTestWidgetsFlutterBinding.ensureInitialized();​

testWidgets('Ajouter une tâche et vérifier la liste', (tester)
async {​
app.main(); // Lancer l'app​
await tester.pumpAndSettle();​

// Trouver le champ de texte et entrer une tâche​
final Finder input = find.byKey(Key('taskInput'));​
await tester.enterText(input, 'Nouvelle tâche');​
await
tester.testTextInput.receiveAction(TextInputAction.done);​
await tester.pumpAndSettle();​

// Cliquer sur le bouton ajouter​
final Finder addButton = find.byKey(Key('addTaskButton'));​
await tester.tap(addButton);​
await tester.pumpAndSettle();​

// Vérifier que la tâche apparaît dans la liste​
expect(find.text('Nouvelle tâche'), findsOneWidget);​
});​
}

🛠️ Points importants
●​ Utiliser integration_test package pour ce type de test.​

●​ Attribuer des Key uniques aux widgets importants (champ texte, bouton,
liste).​

●​ Exécuter via la commande :​

flutter test integration_test/app_test.dart

ou

flutter drive --driver=test_driver/integration_test.dart


--target=integration_test/app_test.dart

(selon la configuration).

📌 Résumé
Étape Actio
n

✅ Tester l’app complète comme un


utilisateur

✅ Automatiser la saisie, clics, navigation


✅ Vérifier les résultats visuels et
fonctionnels

✅ Utiliser le package
integration_test
Chapitre 18 – Optimiser et déployer son app

18.1 – Nettoyer et optimiser le code

Pourquoi nettoyer et optimiser ?


●​ Rendre le code plus lisible et maintenable​

●​ Éliminer les parties inutiles ou redondantes​

●​ Améliorer la performance et la taille de l’app​

●​ Faciliter les mises à jour futures​

Conseils pratiques

●​ Supprimer les imports inutilisés​

●​ Organiser les fichiers et dossiers logiquement​

●​ Refactorer le code en petites fonctions/classe​

●​ Utiliser des outils comme dart analyze et flutter analyze​

●​ Supprimer les print/debug statements​

●​ Utiliser le lazy loading quand possible​

●​ Optimiser les images et assets (taille, format)​

Exemples concrets dans TaskZen

●​ Extraire les widgets complexes dans des fichiers séparés​

●​ Mettre la logique métier dans des classes dédiées (Provider, Bloc, etc.)​

●​ Utiliser const partout où c’est possible​

●​ Minimiser les rebuilds de widgets (éviter de reconstruire toute l’arborescence


pour un petit changement)​
18.2 – Gérer les permissions (Android et iOS)
Pourquoi gérer les permissions ?

Les applications mobiles ont souvent besoin d’accéder à des ressources sensibles
du téléphone, par exemple :

●​ Appareil photo​

●​ Microphone​

●​ Localisation​

●​ Stockage​

●​ Notifications​

Pour respecter la vie privée de l’utilisateur et les règles des stores (Google Play, App
Store), il faut demander ces permissions explicitement.

Comment gérer les permissions dans Flutter ?


Flutter ne gère pas nativement les permissions. On utilise un package externe, le
plus courant est permission_handler.

Étapes pour gérer les permissions :

1.​ Ajouter la dépendance permission_handler dans pubspec.yaml​

dependencies:​
permission_handler: ^10.2.0

2.​ Configurer les fichiers spécifiques à chaque plateforme​

●​ Android : dans android/app/src/main/AndroidManifest.xml,


ajouter les permissions nécessaires. Par exemple :​
<uses-permission android:name="android.permission.CAMERA" />​
<uses-permission
android:name="android.permission.ACCESS_FINE_LOCATION" />

●​ iOS : dans ios/Runner/Info.plist, ajouter les clés correspondant aux


permissions avec une description pour l’utilisateur. Par exemple :​

<key>NSCameraUsageDescription</key>​
<string>Cette app a besoin d'accéder à la caméra pour prendre des
photos.</string>​
<key>NSLocationWhenInUseUsageDescription</key>​
<string>Cette app a besoin de votre localisation pour vous
proposer des tâches basées sur votre position.</string>

3.​ Demander la permission dans le code Flutter​

Exemple simple pour demander la permission d’accès à la caméra :

import 'package:permission_handler/permission_handler.dart';​

Future<bool> requestCameraPermission() async {​
var status = await Permission.camera.status;​
if (!status.isGranted) {​
var result = await Permission.camera.request();​
return result.isGranted;​
}​
return true;​
}

4.​ Gérer les cas de refus​

●​ Afficher un message expliquant pourquoi la permission est nécessaire​

●​ Rediriger l’utilisateur vers les paramètres si nécessaire​

●​ Gérer le cas où l’utilisateur refuse définitivement​


Exemple dans TaskZen
Si on veut notifier l’utilisateur, il faut la permission des notifications. Avant d’envoyer
une notification, on demande la permission :

Future<bool> requestNotificationPermission() async {​


var status = await Permission.notification.status;​
if (!status.isGranted) {​
var result = await Permission.notification.request();​
return result.isGranted;​
}​
return true;​
}

18.3 – Générer un APK ou AAB


Pourquoi générer un APK ou AAB ?

●​ APK (Android Package) : fichier d’installation classique d’une application


Android.​

●​ AAB (Android App Bundle) : format recommandé par Google pour la


publication sur le Play Store, il permet d’optimiser la taille de l’application
selon l’appareil de l’utilisateur.​

Préparer la génération

1.​ Vérifier que l’application fonctionne parfaitement en mode release​

flutter build apk --release

ou

flutter build appbundle --release


2.​ Signer l’application​

Pour publier sur le Play Store, il faut signer l’application avec une clé privée.

●​ Créer une clé de signature avec keytool (inclus dans le JDK) :​

keytool -genkey -v -keystore ~/monkeystore.jks -keyalg RSA


-keysize 2048 -validity 10000 -alias mon_alias

●​ Configurer la signature dans le fichier android/app/build.gradle :​

android {​
...​
signingConfigs {​
release {​
keyAlias 'mon_alias'​
keyPassword 'mon_password'​
storeFile file('chemin/vers/monkeystore.jks')​
storePassword 'mon_password'​
}​
}​
buildTypes {​
release {​
signingConfig signingConfigs.release​
minifyEnabled false​
shrinkResources false​
// Proguard si besoin pour l'optimisation du code​
}​
}​
}

3.​ Générer un APK signé​

flutter build apk --release


4.​ Générer un AAB (recommandé)​

flutter build appbundle --release

Tester l’APK

●​ Transférer l’APK sur un téléphone Android pour tester​

●​ Vérifier que tout fonctionne correctement avant de publier​

Remarques

●​ L’AAB est le format recommandé par Google, car il réduit la taille installée par
l’utilisateur.​

●​ La génération d’APK est utile pour la distribution hors Play Store (test,
entreprise, etc.).​

18.4 – Publier sur le Play Store / App Store


Publier sur le Play Store (Android)
1.​ Créer un compte développeur Google Play​

●​ Coût unique (~25 USD)​

●​ Accès à la console Google Play : https://play.google.com/console​

2.​ Préparer la fiche de l’application​

●​ Nom de l’app​

●​ Description courte et longue​

●​ Icônes, captures d’écran, vidéos promotionnelles​

●​ Catégorie, langue, contenu​


3.​ Téléverser le fichier AAB signé​

●​ Dans la console Google Play, créer une nouvelle version​

●​ Télécharger l’AAB généré (flutter build appbundle --release)​

●​ Ajouter les notes de version​

4.​ Configurer la tarification et la distribution​

●​ Gratuit ou payant​

●​ Pays de distribution​

●​ Restrictions d’âge​

5.​ Soumettre pour examen​

●​ Google vérifie la conformité avec les règles​

●​ Cela peut prendre quelques heures à quelques jours​

●​ Une fois approuvé, l’application est disponible au téléchargement​

Publier sur l’App Store (iOS)


1.​ Créer un compte Apple Developer​

●​ Coût annuel (~99 USD)​

●​ Accès à App Store Connect : https://appstoreconnect.apple.com​

2.​ Préparer l’application dans App Store Connect​

●​ Nom, description, mots-clés​

●​ Icônes, captures d’écran​

●​ Catégorie, contact, politique de confidentialité​

3.​ Créer un profil de provisioning et un certificat​

●​ Nécessaires pour signer l’application​

●​ Configurer dans Xcode ou via Apple Developer Center​


4.​ Générer une archive IPA avec Xcode​

●​ Sélectionner le mode Release​

●​ Archiver l’application​

●​ Valider puis uploader sur App Store Connect​

5.​ Soumettre pour révision​

●​ Apple réalise un contrôle qualité strict​

●​ Peut prendre plusieurs jours​

●​ Une fois validée, l’app est disponible sur l’App Store​

Bonnes pratiques
●​ Soigner la fiche descriptive et les visuels​

●​ Tester en profondeur sur plusieurs appareils​

●​ Suivre les retours des utilisateurs pour améliorer l’app​

●​ Mettre à jour régulièrement pour corriger bugs et ajouter fonctionnalités​


PARTIE 5 – Aller plus loin
Chapitre 19 – Architecture propre avec Flutter
19.1 – Clean Architecture simplifiée

Qu’est-ce que la Clean Architecture ?

●​ C’est une manière d’organiser le code pour qu’il soit clair, maintenable,
évolutif.​

●​ L’idée : séparer les responsabilités en couches distinctes, avec des règles


précises pour les dépendances.​

●​ On évite le “code spaghetti” où tout est mélangé.​

Les couches principales :

1.​ Domaine (business logic)​

○​ Le cœur de l’application, ce sont les règles métiers, les cas d’usage.​

○​ Par exemple : ajouter une tâche, marquer une tâche comme terminée.​

2.​ Données (data)​

○​ Gestion de la récupération, sauvegarde, sources externes (base de


données, API, Firebase).​

○​ Transforme les données pour que la couche domaine puisse les


utiliser.​

3.​ Présentation (UI)​

○​ Ce que voit l’utilisateur : les écrans, widgets Flutter.​

○​ Contient la logique liée à l’affichage, navigation, interaction utilisateur.​

Pourquoi cette séparation ?

●​ Pour que chaque partie soit indépendante, testable.​

●​ Pour pouvoir changer facilement une couche sans impacter les autres (ex :
changer Firebase pour SQLite).​

●​ Pour faciliter la collaboration entre développeurs.​


19.2 — Séparation UI, logique métier, données
Pour bien structurer une app Flutter selon la Clean Architecture, il faut clairement
découper le code en trois parties principales :

1. UI (Interface Utilisateur)

●​ Ici, on retrouve tous les widgets Flutter qui affichent les écrans.​

●​ Leur rôle est uniquement d’afficher les données reçues et de capter les
interactions utilisateur (clics, saisies).​

●​ Exemple dans TaskZen : un écran qui affiche la liste des tâches ou un


formulaire pour en créer une.​

2. Logique métier (Domaine)

●​ C’est là que réside la vraie “intelligence” de l’application.​

●​ C’est la couche qui sait comment gérer les tâches : ajouter, modifier, valider,
supprimer.​

●​ Cette logique ne doit pas dépendre de la façon dont les données sont
stockées ou affichées.​

●​ Exemple : une fonction qui valide qu’une tâche a un titre non vide avant de
l’ajouter.​

3. Données (Data)

●​ Cette couche s’occupe de récupérer et sauvegarder les données.​

●​ Elle dialogue avec la base SQLite, Firebase ou une API distante.​

●​ Elle transforme les données brutes (JSON, SQL) en objets utilisables par la
logique métier.​

●​ Exemple : une classe qui fait les requêtes SQL pour stocker les tâches.​
Pourquoi cette séparation est-elle utile ?

●​ Tu peux modifier la manière dont les données sont stockées (ex: passer de
SQLite à Firebase) sans toucher à la logique métier ou à l’UI.​

●​ Tu peux refaire l’interface (ex: passer de Flutter à une autre technologie) sans
réécrire la logique métier.​

●​ Ton code devient plus facile à tester, car chaque couche est indépendante.​
Chapitre 20 – Flutter au-delà du mobile
20.1 – Déployer pour le Web

Flutter n’est pas limité aux apps mobiles (Android/iOS). Il permet aussi de créer des
applications web :

●​ Flutter Web compile ton code Dart en JavaScript, HTML, et CSS.​

●​ Tu peux créer une app responsive qui tourne dans n’importe quel navigateur
moderne.​

Pour lancer une app Flutter sur le Web, il suffit d’utiliser la commande :​

flutter run -d chrome

●​

Ou pour générer les fichiers à déployer sur un serveur web :

flutter build web

●​
●​ Les fichiers sont générés dans le dossier build/web. Tu peux les héberger
sur un serveur classique.​

20.2 – Déployer pour le bureau (desktop)


Flutter supporte aussi la création d’applications desktop multiplateformes :

●​ Windows, macOS, Linux sont supportés officiellement.​

●​ L’app Flutter est compilée en binaire natif (ex: .exe sur Windows).​

Pour tester sur desktop, utilise :

flutter run -d windows​


flutter run -d macos​
flutter run -d linux

Pour générer un exécutable prêt à distribuer, utilise :​


flutter build windows​
flutter build macos​
flutter build linux

●​ C’est idéal pour créer des outils internes ou des apps plus lourdes avec accès
aux ressources système.​

20.3 – Flutter vs autres technologies

Quelques points pour comparer Flutter à d’autres frameworks :

Critère Flutter React Native Native Xamarin/MAU


(Java/Kotlin I
, Swift)

Langage Dart JavaScript/TypeSc Java/Kotlin, C#


ript Swift

Plateformes Mobile, Web, Mobile (Web Mobile Mobile,


cibles Desktop expérimental) Desktop

Performance Très bonne Bonne Excellente Bonne


(compilé natif)

UI et widgets Widgets Utilise Utilise Widgets


personnalisabl composants natifs composants personnalisabl
es natifs es

Courbe Moyenne Facile à moyenne Plus élevée Moyenne


d’apprentissa
ge
Écosystème En croissance Large Mature Moyen
rapide

Préparer TaskZen pour le Web


1. Assure-toi d’avoir Flutter configuré pour le Web

Vérifie que tu as bien le support Web activé :​

flutter config --enable-web

Confirme que les appareils Web sont disponibles :​

flutter devices

●​ Tu devrais voir par exemple Chrome ou Edge listés.​

2. Adapte l’interface pour le Web

●​ Pense à la responsive UI : sur le Web, les tailles d’écran varient énormément


(du smartphone à l’écran géant).​

●​ Utilise des widgets comme LayoutBuilder, Flexible, Expanded,


MediaQuery pour que l’interface s’adapte.​

●​ Pour TaskZen, ça veut dire :​

○​ La liste des tâches s’étire pour utiliser l’espace.​

○​ Les boutons et champs de texte ne sont pas trop petits ni trop grands.​

3. Gestion des interactions

●​ Sur le Web, il y a plus d’interactions possibles : clic souris, scroll, copier/coller,


clavier.​

●​ Flutter gère ça automatiquement mais tu peux personnaliser :​


○​ Ajoute un focus clair sur les champs texte (FocusNode) pour la
navigation au clavier.​

○​ Gère les raccourcis clavier si besoin (par exemple, Ctrl+N pour


nouvelle tâche).​

4. Compilation et déploiement

Compile pour le Web :​

flutter build web

●​
●​ Le dossier build/web contient les fichiers à copier sur ton serveur web
(Apache, Nginx, Firebase Hosting, GitHub Pages...).​

●​ Teste localement en ouvrant index.html dans un navigateur.​

Préparer TaskZen pour le Desktop


1. Active le support desktop si ce n’est pas déjà fait
●​ Flutter supporte Windows, macOS et Linux.​

Active le support desktop si nécessaire :

flutter config --enable-windows-desktop​


flutter config --enable-macos-desktop​
flutter config --enable-linux-desktop

Vérifie les devices disponibles :

flutter devices

2. Adapter l’interface pour desktop

●​ Desktop a généralement un grand écran et entrée clavier/souris.​


●​ Pour TaskZen :​

○​ Utilise plus d’espace horizontal (par exemple, liste à gauche, détail à


droite).​

○​ Ajoute des menus (barres de menu, raccourcis clavier).​

○​ Sois attentif à la gestion des fenêtres (redimensionnement,


multi-fenêtres).​

3. Accès aux fonctionnalités natives

●​ Sur desktop, tu peux utiliser des plugins pour accéder aux fichiers locaux,
notifications système, etc.​

●​ Par exemple, sauvegarder la base SQLite dans un dossier local accessible.​

●​ Gestion des permissions est plus simple (moins restrictive que mobile).​

4. Compilation et distribution
Compile pour desktop :

flutter build windows​


flutter build macos​
flutter build linux

●​ Tu obtiens un exécutable que tu peux distribuer à tes utilisateurs.​

●​ Pense à signer et packager ton app selon la plateforme.​

Résumé rapide

Étape Web Desktop

Activer flutter config flutter config


support --enable-web --enable-windows-desktop (ou
macOS/Linux)
Adapter UI Responsive, clavier & Fenêtres, menus, raccourcis
souris

Compiler flutter build flutter build windows (ou macOS,


web Linux)

Déployer Copier build/web Distribuer exécutable (.exe, etc.)


sur serveur

👋 Salut à toi cher abonné de Aide en Informatique !



Je voulais prendre un petit moment pour clarifier la différence entre l’abonnement
PREMIUM et le PRO, car certains font encore la confusion.

🔹 PREMIUM :
Tu payes chaque ebook séparément, à un prix très réduit (environ 6000 FCFA, soit
moins que 10 euros uniquement pour les livres amazon et 10.000 CFA pour les
livres en version pré-commande), alors que ces livres sont vendus bien plus cher sur
Amazon.

Lorsque tu achètes un ebook PREMIUM, tu reçois une copie personnalisée avec ton
nom et tes références.

📌 Attention : le partage de ces ebooks est strictement interdit. Si quelqu’un partage


sa copie, je suis automatiquement alerté, et la personne est immédiatement bloquée
de toutes nos plateformes.

Pourquoi cette règle ? Parce qu’un livre, c’est des semaines, voire des mois de

🙏
travail. Ceux qui nous soutiennent nous donnent la force de continuer à produire ce
contenu de qualité

🔸 PRO :
C’est un abonnement annuel (12 mois) à 50 000 FCFA seulement, qui te donne
accès illimité à TOUS nos ebooks premium, même ceux vendus sur Amazon.

💬 Soyons francs : ici en Europe où je vis, si je vends juste deux livres sur Amazon,
je gagne déjà plus de 50 000 FCFA.

Mais pour vous, nos abonnés en Afrique, j’ai mis en place cette formule accessible,
pour que personne ne soit bloqué par les moyens financiers.

👉 Ce n’est pas “beaucoup”, c’est un geste concret pour vous aider à apprendre et
progresser avec des livres pro, bien expliqués, étape par étape.

📌 Je vous invite aussi à lire attentivement ces deux posts ÉPINGLÉS sur notre
page Facebook – ils répondent à presque toutes les questions que vous vous posez
:
🔗 Lien 1 –
https://www.facebook.com/permalink.php?story_fbid=pfbid0a5VtECVLF6zdXt99Dke
uNuBSfhikNF5ioq2UMADN9HnYCCrjEeEc47nnUvyNkasWl&id=100083371621191

🔗 Lien 2 –
https://www.facebook.com/permalink.php?story_fbid=pfbid032HNhFWEew7mHXDJ3
LuEhm1rK1FC1sV6joUjzRdfSfBsSekB7V2drbAnVnXkDcZdfl&id=100083371621191

voici les exemples de livres qu’on fera en pré-commande

La Bible de C/C++ – De 0 à Expert

👍
lien
https://www.facebook.com/permalink.php?story_fbid=pfbid02MUtDY4yKNxMc6gr3SnSJE
HXmdBtJ8w3JCwak4i2xYjS9r9ajQzoimy3NgG5i1n9Wl&id=100083371621191
EduSmart Masterclass – Crée une App Complète de Gestion Scolaire pour l’Éducation
Moderne

Lien
https://www.facebook.com/permalink.php?story_fbid=pfbid0233ArxwLqzu5JuYq7maQF5
Kb5ziVdpSJatFaPvy7F3QrEjXkQtLBQ2cK8GX15tvqfl&id=100083371621191
RendezMed Masterclass – Apprends à Développer une App de Réservation de
Rendez-vous Médicaux Moderne"

Lien :
https://www.facebook.com/permalink.php?story_fbid=pfbid02dPvjZnMN7xTXBY16Vx4Y
b6Ma85XvAoTkaow7QGV14juVjVw9keBXrTkxeK4G7bcQl&id=100083371621191
"StockPro Masterclass – Apprends à Coder une App Complète de Gestion de Stock,
Facturation et Ventes"

Lien :
https://www.facebook.com/permalink.php?story_fbid=pfbid095dKrXBnZyeZo436FBcugu
EWcYNbBrQFY4xJZjp4jCBsKpgXgXNgRC3EMvLx4Czql&id=100083371621191

🙏 Merci à tous ceux qui soutiennent ce projet. Chaque like, chaque partage,
chaque message nous motive à continuer cette belle aventure ensemble.

Vous aimerez peut-être aussi