Académique Documents
Professionnel Documents
Culture Documents
ENOK-Dev
6 août 2023
Cours Complet Sur Flutter
2 Installation et configuration 1
2.1 Installation de Flutter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
2.2 Configuration de l’environnement . . . . . . . . . . . . . . . . . . . . . . . . 2
2.2.1 Configuration pour Android . . . . . . . . . . . . . . . . . . . . . . . 2
2.2.2 Configuration pour iOS . . . . . . . . . . . . . . . . . . . . . . . . . . 2
2.2.3 Configuration pour le Web . . . . . . . . . . . . . . . . . . . . . . . . 2
4 Concepts fondamentaux 3
4.1 Widgets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
4.2 Hot Reload . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
4.3 Comprendre la fonction main() . . . . . . . . . . . . . . . . . . . . . . . . . 1
4.4 Création d’un widget de base . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
10 Thèmes et personnalisation 12
10.1 Utilisation de thèmes pour personnaliser l’apparence . . . . . . . . . . . . . . 12
10.2 Création de thèmes personnalisés . . . . . . . . . . . . . . . . . . . . . . . . . 13
11 Animation et transitions 14
11.1 Introduction aux animations en Flutter . . . . . . . . . . . . . . . . . . . . . . 14
11.2 Utilisation de l’API d’animation Flutter . . . . . . . . . . . . . . . . . . . . . 16
17 Configuration de l’environnement 33
18 Création du projet 33
24 Introduction 36
25 Configuration du projet 37
31 Conclusion 42
Enok-Dev
6 août 2023
Cours Complet Sur Flutter
1 Introduction à Flutter
1.1 Qu’est-ce que Flutter ?
Flutter est un framework open-source développé par Google pour créer des applications
multiplateformes de haute qualité. Il permet de développer des applications mobiles pour An-
droid, iOS et même le Web à partir d’une seule base de code. Flutter utilise le langage de
programmation Dart, qui est également développé par Google.
2 Installation et configuration
2.1 Installation de Flutter
Avant de commencer le développement avec Flutter, vous devez installer Flutter sur votre
système. Voici les étapes pour l’installation :
1. Rendez-vous sur le site officiel de Flutter : https://flutter.dev
2. Téléchargez la dernière version de Flutter pour votre système d’exploitation (Windows,
macOS ou Linux).
3. Extrayez l’archive téléchargée dans un répertoire de votre choix.
4. Ajoutez le chemin d’installation de Flutter à votre variable d’environnement PATH pour
pouvoir y accéder depuis n’importe quel répertoire dans votre terminal.
5. Vérifiez l’installation en exécutant la commande suivante dans votre terminal :
flutter doctor
Cette commande vérifie si tout est configuré correctement et vous indique si des dépendances
supplémentaires doivent être installées.
2. Vous pouvez désormais créer des applications Flutter pour le Web en utilisant les mêmes
commandes que pour Android et iOS.
4 Concepts fondamentaux
4.1 Widgets
Dans Flutter, tout est un widget. Un widget est un élément de l’interface utilisateur, tel
qu’un bouton, un champ de texte, une image, une mise en page, etc. Les widgets définissent
la structure et l’apparence de votre application. Il existe deux types de widgets : les widgets
Stateless et les widgets Stateful.
Les widgets Stateless sont des widgets dont l’état ne change pas, ce qui signifie qu’ils sont
immuables. Ils sont utilisés pour afficher des informations statiques dans votre application.
Voici un exemple de widget Stateless affichant un texte :
import ’package:flutter/material.dart’;
Les widgets Stateful, en revanche, peuvent changer d’état au fil du temps, ce qui signifie
qu’ils sont mutables. Ils sont utilisés pour afficher des informations qui peuvent être modifiées.
Voici un exemple de widget Stateful affichant un compteur :
import ’package:flutter/material.dart’;
void _incrementCounter() {
setState(() {
_counter++;
});
}
@override
Widget build(BuildContext context) {
return Column(
children: [
Text(’Counter: $_counter’),
ElevatedButton(
onPressed: _incrementCounter,
child: Text(’Increment’),
),
],
);
}
}
Dans cet exemple, nous utilisons un widget Stateful pour afficher un compteur qui s’incrémente
à chaque fois que le bouton est pressé.
Enok-Dev
6 août 2023
Cours Complet Sur Flutter
import ’package:flutter/material.dart’;
void main() {
runApp(MyApp());
}
Dans cet exemple, la fonction main() appelle la fonction runApp() en lui passant une
instance de MyApp(), qui est le widget racine de l’application.
import ’package:flutter/material.dart’;
void main() {
runApp(MyApp());
}
Dans cet exemple, nous avons créé un widget MyApp qui est de type StatelessWidget. Le
widget retourne un MaterialApp comme widget racine, qui fournit une structure de base pour
notre application. Le MaterialApp contient un Scaffold qui fournit un appbar et un corps
pour afficher le texte ”Bienvenue sur Flutter !”.
import ’package:flutter/material.dart’;
void main() {
runApp(MyApp());
}
Dans cet exemple, nous avons utilisé MaterialApp comme le widget racine de l’application
et Scaffold pour définir la structure de base de la page, y compris l’app bar avec le titre ”Mon
Application Flutter” et le corps affichant le texte ”Bienvenue sur Flutter !”.
import ’package:flutter/material.dart’;
void main() {
runApp(MyApp());
Dans cet exemple, nous avons ajouté une FloatingActionButton qui affiche une icône
d’ajout (Icon(Icons.add)). Lorsque le bouton est pressé, une action spécifique peut être
exécutée en ajoutant une fonction à l’attribut onPressed du FloatingActionButton.
import ’package:flutter/material.dart’;
void main() {
runApp(MyApp());
}
child: Text(
’Bienvenue sur Flutter !’,
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
color: Colors.blue,
),
),
),
),
);
}
}
Dans cet exemple, nous avons utilisé le widget Text pour afficher le texte ”Bienvenue sur
Flutter !” au centre de l’écran. Nous avons également appliqué un style personnalisé au texte
en utilisant le widget TextStyle, définissant la taille de la police, le poids de la police et la
couleur du texte.
5.4 Image
Le widget Image est utilisé pour afficher des images dans votre application Flutter. Vous
pouvez charger des images à partir de différentes sources telles que le réseau, le disque local
ou les ressources de l’application.
Voici un exemple d’utilisation du widget Image pour afficher une image à partir d’une URL
réseau :
import ’package:flutter/material.dart’;
void main() {
runApp(MyApp());
}
);
}
}
Dans cet exemple, nous avons utilisé le widget Image.network pour afficher une image à
partir de l’URL ”https ://example.com/mon image.jpg”. Nous avons également défini la largeur
et la hauteur de l’image pour la redimensionner.
6.2 Container
Le widget Container est un conteneur rectangulaire qui peut être utilisé pour décorer ou
aligner son enfant. Il peut également être utilisé pour ajouter un remplissage, des marges et des
bordures autour de son enfant.
Voici un exemple d’utilisation du widget Container :
import ’package:flutter/material.dart’;
void main() {
runApp(MyApp());
}
}
}
Dans cet exemple, nous avons utilisé le widget Container pour créer un conteneur rectan-
gulaire de taille 200x200 avec un fond bleu et affichant le texte ”Mon Conteneur”.
void main() {
runApp(MyApp());
}
6.4 ListView
Le widget ListView est utilisé pour afficher une liste de widgets, généralement pour affi-
cher des données qui dépassent la taille de l’écran.
Voici un exemple d’utilisation de ListView :
import ’package:flutter/material.dart’;
void main() {
runApp(MyApp());
}
Dans cet exemple, nous avons utilisé le widget ListView pour afficher une liste de quatre
éléments à l’aide de ListTile.
import ’package:flutter/material.dart’;
void main() {
runApp(MyApp());
}
void _incrementCounter() {
setState(() {
_counter++;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(’Mon Application Flutter’),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(’Nombre de clics : $_counter’),
ElevatedButton(
onPressed: _incrementCounter,
child: Text(’Cliquez ici’),
),
],
),
),
);
}
Dans cet exemple, nous avons créé un widget StatefulWidget appelé MyStatefulWidget
qui maintient un état counter qui est mis à jour chaque fois que le bouton est cliqué. La
méthode incrementCounter() utilise setState() pour mettre à jour l’état counter et de-
mander à Flutter de reconstruire l’interface utilisateur avec la nouvelle valeur.
import ’package:flutter/material.dart’;
import ’package:provider/provider.dart’;
void main() {
runApp(MyApp());
}
void incrementCounter() {
_counter++;
notifyListeners();
}
}
return Scaffold(
appBar: AppBar(
title: Text(’Mon Application Flutter’),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(’Nombre de clics : ${counterModel.counter}’),
ElevatedButton(
onPressed: () => counterModel.incrementCounter(),
child: Text(’Cliquez ici’),
),
],
),
),
);
}
}
Dans cet exemple, nous avons utilisé le package provider pour gérer l’état counter à
l’aide de ChangeNotifier. Le widget ChangeNotifierProvider fournit CounterModel à
travers l’arborescence de widgets, et le widget MyConsumerWidget accède à l’état counter en
utilisant Provider.of<CounterModel>(context) et met à jour l’état en appelant incrementCounter().
Enok-Dev
6 août 2023
Cours Complet Sur Flutter
import ’package:flutter/material.dart’;
void main() {
runApp(MyApp());
}
Dans cet exemple, nous avons défini deux écrans : FirstScreen et SecondScreen. Nous
avons utilisé la propriété routes de MaterialApp pour définir les itinéraires (routes) entre
les écrans. Le bouton dans FirstScreen utilise Navigator.pushNamed() pour naviguer vers
SecondScreen, et le bouton dans SecondScreen utilise Navigator.pop() pour revenir à
l’écran précédent.
import ’package:flutter/material.dart’;
void main() {
runApp(MyApp());
}
appBar: AppBar(
title: Text(’Premier écran’),
),
body: Center(
child: ElevatedButton(
onPressed: () {
Navigator.pushNamed(
context,
’/second’,
arguments: ’Bonjour depuis le premier écran !’,
);
},
child: Text(’Aller au deuxième écran’),
),
),
);
}
}
return Scaffold(
appBar: AppBar(
title: Text(’Deuxième écran’),
),
body: Center(
child: Text(message),
),
);
}
}
import ’package:flutter/material.dart’;
void main() {
runApp(MyApp());
}
child: ElevatedButton(
onPressed: () {
Navigator.pop(context, ’Données renvoyées au premier écran !’);
},
child: Text(’Retour au premier écran’),
),
),
);
}
}
Dans cet exemple, nous avons utilisé Navigator.pushNamed() pour aller à SecondScreen.
Dans SecondScreen, lorsque l’utilisateur appuie sur le bouton ”Retour au premier écran”, nous
utilisons Navigator.pop() pour renvoyer la chaı̂ne de texte ”Données renvoyées au premier
écran !” à FirstScreen. Dans FirstScreen, nous utilisons await avec Navigator.pushNamed()
pour attendre le résultat renvoyé par SecondScreen. Nous affichons ensuite ce résultat dans un
SnackBar.
import ’package:flutter/material.dart’;
void main() {
runApp(MyApp());
}
color: Colors.blue,
child: Center(
child: Text(’Appuyez-moi !’),
),
),
),
),
),
);
}
}
Dans cet exemple, nous avons utilisé le widget GestureDetector pour détecter le tap sur
le conteneur. Lorsque l’utilisateur appuie sur le conteneur, le message ”Le widget a été tapé !”
sera affiché dans la console.
import ’package:flutter/material.dart’;
void main() {
runApp(MyApp());
}
},
child: Text(’Annuler’),
),
TextButton(
onPressed: () {
Navigator.pop(context);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(’Action confirmée !’),
),
);
},
child: Text(’Continuer’),
),
],
);
},
);
},
child: Text(’Afficher la bo^
ıte de dialogue’),
),
),
),
);
}
}
Dans cet exemple, nous avons utilisé showDialog() pour afficher une boı̂te de dialogue de
confirmation. Lorsque l’utilisateur appuie sur le bouton ”Continuer”, nous utilisons ScaffoldMessenger.of(c
pour afficher un SnackBar avec le message ”Action confirmée !”.
8.3 BottomSheet
Le widget BottomSheet est utilisé pour afficher une feuille d’action en bas de l’écran,
généralement pour afficher des options supplémentaires à l’utilisateur.
Voici un exemple d’utilisation de BottomSheet :
import ’package:flutter/material.dart’;
void main() {
runApp(MyApp());
}
Dans cet exemple, nous avons utilisé showModalBottomSheet() pour afficher une BottomSheet
avec le contenu ”Contenu de la BottomSheet” au centre de l’écran.
import ’package:flutter/material.dart’;
import ’package:http/http.dart’ as http;
void main() {
runApp(MyApp());
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text(’Mon Application Flutter’),
),
body: Center(
child: ElevatedButton(
onPressed: () {
fetchData();
},
child: Text(’Récupérer les données de l\’API’),
),
),
),
);
}
}
Dans cet exemple, nous avons utilisé le package http pour effectuer un appel d’API à
l’URL ”https ://api.example.com/data”. Lorsque l’utilisateur appuie sur le bouton ”Récupérer
les données de l’API”, nous utilisons la fonction fetchData() pour effectuer l’appel d’API et
mettre à jour l’état avec les données reçues.
import ’dart:convert’;
import ’package:flutter/material.dart’;
import ’package:http/http.dart’ as http;
void main() {
runApp(MyApp());
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text(’Mon Application Flutter’),
),
body: Center(
child: ElevatedButton(
onPressed: () {
fetchData();
},
child: Text(’Récupérer les données de l\’API’),
),
),
),
);
}
}
Dans cet exemple, nous avons utilisé le package dart:convert pour décoder les données
JSON retournées par l’API. Nous avons converti les données JSON en une liste d’objets Dart
et mis à jour l’état avec ces données.
import ’dart:convert’;
import ’package:flutter/material.dart’;
import ’package:http/http.dart’ as http;
void main() {
runApp(MyApp());
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text(’Mon Application Flutter’),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () {
fetchData();
},
child: Text(’Récupérer les données de l\’API’),
),
SizedBox(height: 20),
if (_dataList.isNotEmpty)
Text(’Données récupérées depuis l\’API :’),
for (var data in _dataList)
Text(data),
],
),
),
),
);
}
}
Dans cet exemple, nous avons affiché les données récupérées depuis l’API dans des wid-
gets Text. Si les données ont été récupérées avec succès, nous affichons le message ”Données
récupérées depuis l’API :” suivi de chaque élément de la liste dataList.
10 Thèmes et personnalisation
10.1 Utilisation de thèmes pour personnaliser l’apparence
Flutter propose un système de thèmes qui permet de personnaliser l’apparence de votre
application de manière cohérente.
Voici un exemple d’utilisation de thèmes pour personnaliser l’apparence :
import ’package:flutter/material.dart’;
void main() {
runApp(MyApp());
}
home: Scaffold(
appBar: AppBar(
title: Text(’Mon Application Flutter’),
),
body: Center(
child: Text(
’Personnalisation avec thèmes’,
style: TextStyle(fontSize: 24),
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {},
child: Icon(Icons.add),
),
),
);
}
}
Dans cet exemple, nous avons défini un thème pour l’application en utilisant ThemeData.
Nous avons personnalisé les couleurs en définissant primaryColor à Colors.blue et accentColor
à Colors.yellow. Ces couleurs seront utilisées par défaut dans les widgets tels que AppBar et
FloatingActionButton.
import ’package:flutter/material.dart’;
void main() {
runApp(MyApp());
}
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {},
child: Icon(Icons.add),
),
),
);
}
}
Dans cet exemple, nous avons créé un thème personnalisé myTheme en définissant des pro-
priétés telles que les couleurs, la police et le style de texte. Nous avons ensuite utilisé ce thème
dans l’application en l’affectant à la propriété theme de MaterialApp.
11 Animation et transitions
11.1 Introduction aux animations en Flutter
Flutter offre un puissant système d’animation qui vous permet d’animer les propriétés des
widgets pour créer des expériences utilisateur fluides et interactives.
Voici un exemple d’utilisation de l’animation en Flutter :
import ’package:flutter/material.dart’;
void main() {
runApp(MyApp());
}
@override
void initState() {
super.initState();
_animationController = AnimationController(
vsync: this,
duration: Duration(seconds: 2),
);
_animation = Tween<double>(begin: 0, end: 200).animate(_animationController);
}
@override
void dispose() {
_animationController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(’Mon Application Flutter’),
),
body: Center(
child: AnimatedBuilder(
animation: _animation,
builder: (context, child) {
return Container(
width: _animation.value,
height: _animation.value,
color: Colors.blue,
);
},
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
_animationController.forward();
},
child: Icon(Icons.play_arrow),
),
);
}
}
Dans cet exemple, nous avons utilisé AnimationController pour contrôler l’animation,
et AnimatedBuilder pour animer la taille d’un conteneur. Lorsque l’utilisateur appuie sur le
bouton ”Play”, l’animation démarre et le conteneur augmente progressivement de taille.
import ’package:flutter/material.dart’;
void main() {
runApp(MyApp());
}
@override
void initState() {
super.initState();
_animationController = AnimationController(
vsync: this,
duration: Duration(seconds: 2),
);
_animation = Tween<double>(begin: 0, end: 200).animate(_animationController);
@override
void dispose() {
_animationController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(’Mon Application Flutter’),
),
body: Center(
child: AnimatedBuilder(
animation: _animation,
builder: (context, child) {
return Container(
width: _animation.value,
height: _animation.value,
color: Colors.blue,
);
},
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
_animationController.forward();
},
child: Icon(Icons.play_arrow),
),
);
}
}
Dans cet exemple, nous avons utilisé AnimationController pour contrôler l’animation,
et AnimatedBuilder pour animer la taille d’un conteneur. Lorsque l’utilisateur appuie sur le
bouton ”Play”, l’animation démarre et le conteneur augmente progressivement de taille.
import ’package:flutter/material.dart’;
void main() {
runApp(MyApp());
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(’Mon Application Flutter’),
),
body: Padding(
padding: EdgeInsets.all(16.0),
child: Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
TextFormField(
decoration: InputDecoration(labelText: ’Nom’),
validator: (value) {
if (value == null || value.isEmpty) {
return ’Veuillez saisir votre nom.’;
}
return null;
},
onSaved: (value) {
_name = value!;
},
),
TextFormField(
decoration: InputDecoration(labelText: ’^
Age’),
keyboardType: TextInputType.number,
validator: (value) {
if (value == null || value.isEmpty) {
return ’Veuillez saisir votre a^ge.’;
}
final age = int.tryParse(value);
if (age == null || age <= 0) {
return ’Veuillez saisir un a
^ge valide.’;
}
return null;
},
onSaved: (value) {
_age = int.parse(value!);
},
),
SizedBox(height: 16),
ElevatedButton(
onPressed: () {
if (_formKey.currentState!.validate()) {
_formKey.currentState!.save();
showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: Text(’Confirmation’),
content: Text(’Nom : $_name\n^
Age : $_age’),
actions: [
TextButton(
onPressed: () {
Navigator.pop(context);
},
child: Text(’OK’),
),
],
);
},
);
}
},
child: Text(’Valider’),
),
],
),
),
),
);
}
}
Dans cet exemple, nous avons utilisé Form avec deux TextFormField pour collecter le nom
et l’âge de l’utilisateur. Nous avons défini des validateurs pour s’assurer que les champs sont
remplis correctement. Lorsque l’utilisateur appuie sur le bouton ”Valider”, nous vérifions si le
formulaire est valide à l’aide de validate(). Si le formulaire est valide, nous sauvegardons
les données à l’aide de save() et affichons une boı̂te de dialogue de confirmation avec les
informations saisies.
import ’package:flutter/material.dart’;
void main() {
runApp(MyApp());
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(’Mon Application Flutter’),
),
body: Padding(
padding: EdgeInsets.all(16.0),
child: Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
TextFormField(
decoration: InputDecoration(labelText: ’Nom’),
validator: (value) {
if (value == null || value.isEmpty) {
return ’Veuillez saisir votre nom.’;
}
return null;
},
onSaved: (value) {
_name = value!;
},
),
TextFormField(
decoration: InputDecoration(labelText: ’^
Age’),
keyboardType: TextInputType.number,
validator: (value) {
if (value == null || value.isEmpty) {
return ’Veuillez saisir votre a^ge.’;
}
final age = int.tryParse(value);
if (age == null || age <= 0) {
return ’Veuillez saisir un a
^ge valide.’;
}
return null;
},
onSaved: (value) {
_age = int.parse(value!);
},
),
SizedBox(height: 16),
ElevatedButton(
onPressed: () {
if (_formKey.currentState!.validate()) {
_formKey.currentState!.save();
showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: Text(’Confirmation’),
content: Text(’Nom : $_name\n^
Age : $_age’),
actions: [
TextButton(
onPressed: () {
Navigator.pop(context);
},
child: Text(’OK’),
),
],
);
},
);
}
},
child: Text(’Valider’),
),
],
),
),
),
);
}
}
Dans cet exemple, nous avons utilisé Form avec deux TextFormField pour collecter le nom
et l’âge de l’utilisateur. Nous avons défini des validateurs pour s’assurer que les champs sont
remplis correctement. Lorsque l’utilisateur appuie sur le bouton ”Valider”, nous vérifions si le
formulaire est valide à l’aide de validate(). Si le formulaire est valide, nous sauvegardons
les données à l’aide de save() et affichons une boı̂te de dialogue de confirmation avec les
informations saisies.
import ’package:flutter/material.dart’;
void main() {
runApp(MyApp());
}
);
}
}
void _incrementCounter() {
setState(() {
_counter++;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(’Mon Application Flutter’),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
’Nombre de clics :’,
),
Text(
’$_counter’,
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: ’Incrémenter’,
child: Icon(Icons.add),
),
);
}
}
Dans cet exemple, nous avons utilisé StatefulWidget pour créer un widget MyStatefulWidget
qui peut gérer son propre état. Lorsque l’utilisateur appuie sur le bouton flottant, la fonction
import ’package:flutter/material.dart’;
import ’package:provider/provider.dart’;
void main() {
runApp(MyApp());
}
void incrementCounter() {
_counter++;
notifyListeners();
}
}
return Scaffold(
appBar: AppBar(
title: Text(’Mon Application Flutter’),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
’Nombre de clics :’,
),
Text(
’${counterProvider.counter}’,
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
counterProvider.incrementCounter();
},
tooltip: ’Incrémenter’,
child: Icon(Icons.add),
),
);
}
}
Dans cet exemple, nous avons créé une classe CounterProvider qui étend ChangeNotifier.
Cette classe contient le compteur counter et une méthode incrementCounter() pour incrémenter
le compteur. Lorsque le compteur est modifié, nous utilisons notifyListeners() pour infor-
mer les consommateurs (MyConsumerWidget) que l’état a changé.
Nous enveloppons l’application avec ChangeNotifierProvider et fournissons une ins-
tance de CounterProvider. Dans MyConsumerWidget, nous utilisons Provider.of<CounterProvider>(co
pour accéder à l’instance de CounterProvider et afficher la valeur du compteur dans l’inter-
face utilisateur. Lorsque l’utilisateur appuie sur le bouton flottant, nous appelons counterProvider.incremen
pour incrémenter le compteur et mettre à jour l’interface utilisateur.
import ’package:flutter/material.dart’;
void main() {
runApp(MyApp());
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(’Mon Application Flutter’),
),
body: Padding(
padding: EdgeInsets.all(16.0),
child: Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
TextFormField(
decoration: InputDecoration(labelText: ’Nom’),
validator: (value) {
if (value == null || value.isEmpty) {
return ’Veuillez saisir votre nom.’;
}
return null;
},
onSaved: (value) {
_name = value!;
},
),
TextFormField(
decoration: InputDecoration(labelText: ’^
Age’),
keyboardType: TextInputType.number,
validator: (value) {
if (value == null || value.isEmpty) {
return ’Veuillez saisir votre a^ge.’;
}
final age = int.tryParse(value);
if (age == null || age <= 0) {
return ’Veuillez saisir un a
^ge valide.’;
}
return null;
},
onSaved: (value) {
_age = int.parse(value!);
},
),
SizedBox(height: 16),
ElevatedButton(
onPressed: () {
if (_formKey.currentState!.validate()) {
_formKey.currentState!.save();
showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: Text(’Confirmation’),
content: Text(’Nom : $_name\n^
Age : $_age’),
actions: [
TextButton(
onPressed: () {
Navigator.pop(context);
},
child: Text(’OK’),
),
],
);
},
);
}
},
child: Text(’Valider’),
),
],
),
),
),
);
}
}
Dans cet exemple, nous avons utilisé le widget Form avec deux TextFormField pour col-
lecter le nom et l’âge de l’utilisateur. Nous avons défini des validateurs pour s’assurer que les
champs sont remplis correctement. Lorsque l’utilisateur appuie sur le bouton ”Valider”, nous
vérifions si le formulaire est valide à l’aide de validate(). Si le formulaire est valide, nous
sauvegardons les données à l’aide de save() et affichons une boı̂te de dialogue de confirmation
avec les informations saisies.
import ’package:flutter/material.dart’;
void main() {
runApp(MyApp());
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(’Mon Application Flutter’),
),
body: Padding(
padding: EdgeInsets.all(16.0),
child: Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
TextFormField(
decoration: InputDecoration(labelText: ’Nom’),
validator: (value) {
if (value == null || value.isEmpty) {
return ’Veuillez saisir votre nom.’;
}
return null;
},
onSaved: (value) {
_name = value!;
},
),
TextFormField(
decoration: InputDecoration(labelText: ’^
Age’),
keyboardType: TextInputType.number,
validator: (value) {
if (value == null || value.isEmpty) {
return ’Veuillez saisir votre a^ge.’;
}
final age = int.tryParse(value);
if (age == null || age <= 0) {
return ’Veuillez saisir un a
^ge valide.’;
}
return null;
},
onSaved: (value) {
_age = int.parse(value!);
},
),
SizedBox(height: 16),
ElevatedButton(
onPressed: () {
if (_formKey.currentState!.validate()) {
_formKey.currentState!.save();
showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: Text(’Confirmation’),
content: Text(’Nom : $_name\n^
Age : $_age’),
actions: [
TextButton(
onPressed: () {
Navigator.pop(context);
},
child: Text(’OK’),
),
],
);
},
);
}
},
child: Text(’Valider’),
),
],
),
),
),
);
}
}
Dans cet exemple, nous avons utilisé le widget Form avec deux TextFormField pour col-
lecter le nom et l’âge de l’utilisateur. Nous avons défini des validateurs pour s’assurer que les
champs sont remplis correctement. Lorsque l’utilisateur appuie sur le bouton ”Valider”, nous
vérifions si le formulaire est valide à l’aide de validate(). Si le formulaire est valide, nous
sauvegardons les données à l’aide de save() et affichons une boı̂te de dialogue de confirmation
avec les informations saisies.
Cette commande générera un fichier APK (Android Package) dans le répertoire ‘build/app/outputs/apk/relea
Ce fichier APK peut être soumis à Google Play Store pour distribution.
Pour compiler l’application pour iOS, vous devez exécuter la commande suivante dans le
répertoire de votre projet :
Cette commande générera un fichier IPA (iOS App Store Package) dans le répertoire ‘build/ios/iphoneos/‘.
Ce fichier IPA peut être soumis à l’App Store d’Apple pour distribution.
flutter create .
flutter config --enable-web
Cela mettra à jour votre projet pour activer la prise en charge du Web. Vous pouvez ensuite
exécuter votre application sur le Web en utilisant la commande :
Notez que la prise en charge du Web avec Flutter est encore en développement et certaines
fonctionnalités peuvent ne pas être complètement prises en charge.
- Responsive Design : Sur le Web, vous devrez prendre en compte la conception réactive
pour que votre application s’adapte à différentes tailles d’écran, tandis que sur les appareils
mobiles, vous pouvez vous concentrer sur des tailles d’écran spécifiques.
- Plugins : Certains plugins spécifiques aux appareils mobiles peuvent ne pas être dispo-
nibles pour le Web. Vous devrez vérifier la compatibilité des plugins que vous utilisez dans
votre application.
Malgré ces différences, le développement pour le Web avec Flutter offre la possibilité de
partager une grande partie du code avec la version mobile de votre application, ce qui peut
accélérer le développement et réduire les efforts de maintenance.
17 Configuration de l’environnement
Assurez-vous d’avoir installé Flutter sur votre système. Vous pouvez vérifier l’installation
en exécutant flutter doctor dans le terminal.
18 Création du projet
Créez un nouveau projet Flutter en exécutant flutter create task manager dans le
terminal.
dependencies:
flutter:
sdk: flutter
get: ^4.3.8
flutter_launcher_icons: ^0.9.2
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^1.0.0
flutter_launcher_icons: ^0.9.2
33
Cours Complet Sur Flutter
flutter_icons:
android: true
ios: true
image_path: "assets/icon/app_icon.png"
class Task {
final int id;
final String title;
final bool isCompleted;
import ’package:get/get.dart’;
import ’package:task_manager/models/task_model.dart’;
import ’package:flutter/material.dart’;
import ’package:get/get.dart’;
import ’package:task_manager/controllers/task_controller.dart’;
void main() {
runApp(MyApp());
}
@override
Widget build(BuildContext context) {
return MaterialApp(
title: ’Task Manager’,
home: TaskManagerScreen(),
);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(’Task Manager’),
),
body: Padding(
padding: EdgeInsets.all(16),
child: Column(
children: [
Row(
children: [
Expanded(
child: TextField(
controller: _taskTitleController,
decoration: InputDecoration(
hintText: ’Ajouter une t^
ache’,
),
),
),
ElevatedButton(
onPressed: () {
if (_taskTitleController.text.isNotEmpty) {
Get.find<TaskController>().addTask(_taskTitleController.text);
_taskTitleController.clear();
}
},
child: Text(’Ajouter’),
),
],
),
SizedBox(height: 16),
Expanded(
child: Obx(
() => ListView.builder(
itemCount: Get.find<TaskController>().tasks.length,
itemBuilder: (context, index) {
final task = Get.find<TaskController>().tasks[index];
return ListTile(
title: Text(task.title),
leading: Checkbox(
value: task.isCompleted,
onChanged: (value) {
Get.find<TaskController>().toggleTaskStatus(index);
},
),
trailing: IconButton(
icon: Icon(Icons.delete),
onPressed: () {
Get.find<TaskController>().deleteTask(index);
},
),
);
},
),
),
),
],
),
),
);
}
}
24 Introduction
Dans ce cours, nous allons apprendre à créer une application de gestion des tâches en utili-
sant le framework Flutter avec le pattern Bloc. Le pattern Bloc est un moyen puissant de gérer
l’état de l’application et de séparer la logique métier de l’interface utilisateur.
25 Configuration du projet
Assurez-vous d’avoir installé Flutter sur votre système et configuré l’environnement de
développement. Vous pouvez vérifier cela en exécutant flutter doctor dans votre terminal.
Créez un nouveau projet Flutter en utilisant la commande flutter create task manager.
dependencies:
flutter:
sdk: flutter
flutter_bloc: ^7.2.3
equatable: ^2.0.0
Enregistrez le fichier, puis exécutez flutter pub get dans votre terminal pour récupérer
les dépendances.
class Task {
final int id;
final String title;
final bool isCompleted;
Dans le répertoire \texttt{bloc}, nous aurons deux fichiers pour définir les événement
\subsection{Événements (task\_event.dart)}
\begin{verbatim}
import ’package:equatable/equatable.dart’;
@override
List<Object> get props => [];
}
AddTask(this.title);
@override
List<Object> get props => [title];
}
ToggleTaskStatus(this.taskId);
@override
List<Object> get props => [taskId];
}
DeleteTask(this.taskId);
@override
List<Object> get props => [taskId];
}
@override
List<Object> get props => [];
}
TaskLoaded(this.tasks);
@override
List<Object> get props => [tasks];
}
TaskError(this.message);
@override
List<Object> get props => [message];
}
import ’package:flutter_bloc/flutter_bloc.dart’;
import ’package:task_manager/bloc/task_event.dart’;
import ’package:task_manager/bloc/task_state.dart’;
import ’package:task_manager/models/task.dart’;
TaskBloc() : super(TaskInitial());
@override
Stream<TaskState> mapEventToState(TaskEvent event) async* {
if (event is AddTask) {
yield TaskLoading();
try {
tasks.add(Task(id: tasks.length, title: event.title));
yield TaskLoaded(tasks);
} catch (e) {
yield TaskError("Erreur lors de l’ajout de la t^
ache.");
}
} else if (event is ToggleTaskStatus) {
yield TaskLoading();
try {
tasks[event.taskId].isCompleted =
!tasks[event.taskId].isCompleted;
yield TaskLoaded(tasks);
} catch (e) {
yield TaskError("Erreur lors du basculement de l’état de la t^
ache.");
}
} else if (event is DeleteTask) {
yield TaskLoading();
try {
tasks.removeWhere((task) => task.id == event.taskId);
yield TaskLoaded(tasks);
} catch (e) {
yield TaskError("Erreur lors de la suppression de la t^ache.");
}
}
}
}
import ’package:flutter/material.dart’;
import ’package:flutter_bloc/flutter_bloc.dart’;
import ’package:task_manager/bloc/task_bloc.dart’;
import ’package:task_manager/bloc/task_event.dart’;
import ’package:task_manager/bloc/task_state.dart’;
import ’package:task_manager/models/task.dart’;
@override
Widget build(BuildContext context) {
final TaskBloc taskBloc = BlocProvider.of<TaskBloc>(context);
return Scaffold(
appBar: AppBar(
title: Text(’Task Manager’),
),
body: Padding(
padding: EdgeInsets.all(16),
child: Column(
children: [
Row(
children: [
Expanded(
child: TextField(
controller: _taskTitleController,
decoration: InputDecoration(
hintText: ’Ajouter une t^ache’,
),
),
),
ElevatedButton(
onPressed: () {
if (_taskTitleController.text.isNotEmpty) {
taskBloc.add(AddTask(_taskTitleController.text));
_taskTitleController.clear();
}
},
child: Text(’Ajouter’),
),
],
),
SizedBox(height: 16),
BlocBuilder<TaskBloc, TaskState>(
builder: (context, state) {
if (state is TaskLoading) {
return CircularProgressIndicator();
} else if (state is TaskLoaded) {
return Expanded(
child: ListView.builder(
itemCount: state.tasks.length,
itemBuilder: (context, index) {
final Task task = state.tasks[index];
return ListTile(
title: Text(task.title),
leading: Checkbox(
value: task.isCompleted,
onChanged: (_) {
taskBloc.add(ToggleTaskStatus(index));
},
),
trailing: IconButton(
icon: Icon(Icons.delete),
onPressed: () {
taskBloc.add(DeleteTask(index));
},
),
);
},
),
);
} else if (state is TaskError) {
return Text(’Erreur : ${state.message}’);
} else {
return Text(’Aucune t^
ache pour le moment.’);
}
},
),
],
),
),
);
}
}
import ’package:flutter/material.dart’;
import ’package:flutter_bloc/flutter_bloc.dart’;
import ’package:task_manager/bloc/task_bloc.dart’;
import ’package:task_manager/screens/home_screen.dart’;
void main() {
runApp(MyApp());
}
31 Conclusion
Félicitations ! Vous avez terminé la création de l’application de gestion des tâches en utili-
sant Flutter avec le pattern Bloc. Vous avez appris à implémenter un Bloc de gestion des tâches,
à séparer la logique métier de l’interface utilisateur et à afficher les tâches dans l’écran prin-
cipal. Vous pouvez maintenant exécuter votre application et commencer à ajouter, cocher et
supprimer des tâches.