0% ont trouvé ce document utile (0 vote)
64 vues22 pages

Flutter

Le document présente plusieurs exercices de programmation Flutter, incluant la création de formulaires avec validation, la navigation entre pages avec passage d'arguments, l'utilisation de StreamBuilder pour afficher des données dynamiques, l'utilisation de FutureBuilder pour effectuer des requêtes API, et l'utilisation d'InheritedWidget pour partager des données. Chaque exercice est accompagné de code source et d'explications détaillées sur les concepts utilisés. L'objectif global est d'apprendre à gérer l'état, la navigation et les données asynchrones dans une application Flutter.

Transféré par

bisol72096
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 DOCX, PDF, TXT ou lisez en ligne sur Scribd
0% ont trouvé ce document utile (0 vote)
64 vues22 pages

Flutter

Le document présente plusieurs exercices de programmation Flutter, incluant la création de formulaires avec validation, la navigation entre pages avec passage d'arguments, l'utilisation de StreamBuilder pour afficher des données dynamiques, l'utilisation de FutureBuilder pour effectuer des requêtes API, et l'utilisation d'InheritedWidget pour partager des données. Chaque exercice est accompagné de code source et d'explications détaillées sur les concepts utilisés. L'objectif global est d'apprendre à gérer l'état, la navigation et les données asynchrones dans une application Flutter.

Transféré par

bisol72096
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 DOCX, PDF, TXT ou lisez en ligne sur Scribd

Exercice 1 : Création d'un formulaire avec validation

Objectif : Créer un formulaire avec deux champs (Nom et Email) et valider les données avant
de soumettre le formulaire.

import 'package:flutter/material.dart';

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

// Point d'entrée de l'application Flutter. Le widget MyApp sera exécuté au


démarrage.

class MyApp extends StatelessWidget {

@override

Widget build(BuildContext context) {

return MaterialApp(

// MaterialApp est le widget racine de l'application, qui contient


toute la structure de l'UI.

home: Scaffold(

appBar: AppBar(

title: Text('Formulaire avec validation'),

// AppBar est la barre d'application en haut, ici on y met le


titre 'Formulaire avec validation'.

),

body: Padding(

padding: const EdgeInsets.all(16.0),

// Padding autour du formulaire pour lui donner un peu d'espace.

child: Formulaire(),

// Affiche le widget Formulaire qui est un StatefulWidget.

),

),

);

1
}

class Formulaire extends StatefulWidget {

@override

_FormulaireState createState() => _FormulaireState();

// Cette méthode crée l'état du formulaire, nécessaire pour gérer l'état


interne du widget (données saisies, validation, etc.).

class _FormulaireState extends State<Formulaire> {

final _formKey = GlobalKey<FormState>();

// _formKey est une clé globale pour le formulaire. Elle permet de


valider et de gérer l'état du formulaire.

final TextEditingController _nomController = TextEditingController();

// _nomController est un contrôleur pour le champ 'Nom'. Il permet de


récupérer et de manipuler le texte saisi.

final TextEditingController _emailController = TextEditingController();

// _emailController est un contrôleur pour le champ 'Email'. Il permet de


récupérer et de manipuler le texte saisi.

@override

Widget build(BuildContext context) {

return Form(

key: _formKey,

// Attache la clé globale (_formKey) au formulaire, permettant de


valider et de soumettre le formulaire.

child: Column(

2
// Le formulaire contient une colonne pour disposer les champs du
formulaire verticalement.

crossAxisAlignment: CrossAxisAlignment.start,

// Aligne les champs à gauche.

children: <Widget>[

TextFormField(

controller: _nomController,

// Lier le contrôleur au champ de texte pour pouvoir récupérer


les données saisies.

decoration: InputDecoration(labelText: 'Nom'),

// Décoration du champ de texte, ici le label du champ est


'Nom'.

validator: (value) {

// Fonction de validation pour vérifier si le champ est


correctement rempli.

if (value == null || value.isEmpty) {

return 'Veuillez entrer un nom';

// Si le champ est vide, on renvoie un message d'erreur.

return null;

// Si le champ est valide (non vide), on retourne null pour


indiquer que la validation est réussie.

},

),

TextFormField(

controller: _emailController,

// Lier le contrôleur au champ de texte pour pouvoir récupérer


les données saisies.

decoration: InputDecoration(labelText: 'Email'),

// Décoration du champ de texte, ici le label du champ est


'Email'.

3
validator: (value) {

// Fonction de validation pour vérifier si l'email est bien


renseigné et valide.

if (value == null || value.isEmpty) {

return 'Veuillez entrer un email';

// Si le champ est vide, on renvoie un message d'erreur.

if (!RegExp(r'^[^@]+@[^@]+\.[^@]+').hasMatch(value)) {

// Si l'email ne correspond pas à une expression régulière


valide (pas de @ ou de point manquants), on renvoie une erreur.

return 'Email invalide';

return null;

// Si l'email est valide, on retourne null pour indiquer que


la validation est réussie.

},

),

Padding(

padding: const EdgeInsets.symmetric(vertical: 16.0),

// Padding pour ajouter de l'espace autour du bouton


'Soumettre'.

child: ElevatedButton(

onPressed: () {

if (_formKey.currentState?.validate() ?? false) {

// On vérifie si le formulaire est valide (tous les


champs sont remplis correctement).

ScaffoldMessenger.of(context).showSnackBar(

SnackBar(content: Text('Formulaire valide'))

// Si la validation réussit, on affiche un message de


confirmation sous forme de SnackBar.

);

4
}

},

child: Text('Soumettre'),

// Texte affiché sur le bouton 'Soumettre'.

),

),

],

),

);

Explication :

 GlobalKey<FormState> _formKey : Permet de gérer l'état du formulaire et de valider


les champs.
 TextFormField : Un champ de texte avec une fonction de validation.
 RegExp : Expression régulière pour valider l'email.
 ScaffoldMessenger.of(context).showSnackBar() : Affiche un message
contextuel si le formulaire est valide.

Exercice 2 : Navigation avec passage d'arguments

Objectif : Créer une application avec deux pages, où la première page passe un argument à la
deuxième page via la navigation.

import 'package:flutter/material.dart';

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

// Lancer l'application Flutter. Cela appelle la méthode runApp pour


afficher l'interface utilisateur définie par MyApp.

class MyApp extends StatelessWidget {

@override

5
Widget build(BuildContext context) {

return MaterialApp(

initialRoute: '/',

// Définit la route initiale lorsque l'application démarre. Ici,


c'est la page d'accueil (route '/').

routes: {

'/': (context) => HomePage(),

// La route '/' est liée à la page d'accueil (HomePage).

'/second': (context) => SecondPage(),

// La route '/second' est liée à la page secondaire (SecondPage).

},

);

class HomePage extends StatelessWidget {

@override

Widget build(BuildContext context) {

return Scaffold(

appBar: AppBar(title: Text('Page d\'accueil')),

// Création de l'AppBar (barre de titre) de la page d'accueil avec le


texte "Page d'accueil".

body: Center(

child: ElevatedButton(

onPressed: () {

6
// Lors du clic sur le bouton, la méthode onPressed est
appelée.

Navigator.pushNamed(

context,

'/second',

arguments: 'Bonjour depuis la page d\'accueil',

// Utilise la méthode pushNamed pour naviguer vers la page


secondaire.

// Elle passe aussi un argument ('Bonjour depuis la page


d\'accueil') à la page secondaire.

);

},

child: Text('Aller à la page secondaire'),

// Le texte affiché sur le bouton : "Aller à la page secondaire".

),

),

);

class SecondPage extends StatelessWidget {

@override

Widget build(BuildContext context) {

final String message = ModalRoute.of(context)!.settings.arguments as


String;

// Récupération de l'argument passé lors de la navigation avec


Navigator.pushNamed.

// ModalRoute.of(context) obtient les paramètres de la route actuelle,


y compris les arguments.

// Ici, on s'assure que l'argument est de type String.

7
return Scaffold(

appBar: AppBar(title: Text('Page secondaire')),

// Création de l'AppBar pour la page secondaire avec le texte "Page


secondaire".

body: Center(

child: Text(message),

// Affiche le message récupéré de la page précédente.

// Ce message est passé en argument via la méthode pushNamed.

),

);

Explication :

 Navigator.pushNamed() : Permet de naviguer vers une nouvelle route avec des


arguments.
 ModalRoute.of(context)!.settings.arguments : Récupère les arguments passés
à la route.

Exercice 3 : Utilisation de StreamBuilder pour afficher une liste dynamique

Objectif : Créer une interface avec un StreamBuilder qui affiche des données en temps réel.

import 'dart:async';
// Importation de la bibliothèque dart:async qui fournit les classes pour
travailler avec des streams.

import 'package:flutter/material.dart';
// Importation de la bibliothèque Flutter Material pour l'UI.

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


// Point d'entrée de l'application Flutter. Cela appelle la méthode runApp
pour afficher l'interface utilisateur définie par MyApp.

class MyApp extends StatelessWidget {


@override
Widget build(BuildContext context) {
return MaterialApp(home: StreamExample());
// MaterialApp est l'élément de base pour l'application. Il affiche le
widget StreamExample comme page d'accueil.

8
}
}

class StreamExample extends StatelessWidget {


final StreamController<String> _controller = StreamController<String>();
// Création d'un contrôleur de stream qui permettra d'ajouter des
éléments à un stream.
// Ce contrôleur est de type String, ce qui signifie que les éléments
ajoutés au stream seront des chaînes de caractères.

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('StreamBuilder Exemple')),
// Scaffold est un widget qui fournit une structure de base avec une
AppBar et un body.
// L'AppBar affiche le titre "StreamBuilder Exemple" en haut de la
page.

body: Column(
// Utilisation de Column pour disposer les widgets (un bouton et un
StreamBuilder) verticalement.
children: [
ElevatedButton(
onPressed: () {
_controller.add('Nouvel élément ${DateTime.now()}');
// Lors du clic sur le bouton, un nouvel élément est ajouté
au stream.
// L'élément ajouté est une chaîne de caractères comprenant
la date et l'heure actuelles.
},
child: Text('Ajouter un élément'),
// Texte affiché sur le bouton : "Ajouter un élément".
),

StreamBuilder<String>(
stream: _controller.stream,
// Le widget StreamBuilder écoute le stream du contrôleur
_controller.
// Il réagit aux événements qui arrivent dans le stream.

builder: (context, snapshot) {


// Le builder fournit le contexte et le snapshot, qui
contient les données ou l'état du stream.
if (snapshot.connectionState == ConnectionState.waiting) {
return CircularProgressIndicator();
// Si le stream est en attente de données (connectionState
est "waiting"),
// on affiche un indicateur de chargement circulaire.
} else if (snapshot.hasError) {
return Text('Erreur: ${snapshot.error}');
// Si une erreur est survenue lors de l'écoute du stream,
on affiche le message d'erreur.
} else if (!snapshot.hasData) {
return Text('Aucun élément');
// Si le stream ne contient aucune donnée, on affiche
"Aucun élément".
} else {
return Text(snapshot.data!);
// Si le stream contient des données, on les affiche sous
forme de texte.

9
// "snapshot.data!" est une chaîne de caractères provenant
du stream.
}
},
),
],
),
);
}
}

Explication :

 StreamController<String> : Permet de gérer et émettre des données à un stream.


 StreamBuilder<String> : Reconstruit l'UI chaque fois que le stream émet de
nouvelles données.
 snapshot.connectionState : Permet de gérer l'état du stream (en attente, succès,
erreur).

Exercice 4 : Utilisation d'un FutureBuilder pour effectuer une requête API

Objectif : Créer une interface qui fait une requête HTTP et affiche la réponse avec un
FutureBuilder.

import 'dart:convert';

// Importation de la bibliothèque 'dart:convert' qui permet de décoder des


données JSON.

import 'package:flutter/material.dart';

// Importation de la bibliothèque Flutter Material pour l'UI.

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

// Importation de la bibliothèque HTTP pour effectuer des requêtes réseau


(comme GET).

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

// Point d'entrée de l'application Flutter. Cette fonction démarre


l'application en appelant la méthode runApp avec MyApp.

class MyApp extends StatelessWidget {

10
@override

Widget build(BuildContext context) {

return MaterialApp(home: ApiExample());

// MaterialApp est l'élément de base pour l'application. Il affiche le


widget ApiExample comme page d'accueil.

class ApiExample extends StatelessWidget {

// Fonction asynchrone qui récupère des données via une requête HTTP GET.

Future<String> fetchData() async {

// On effectue une requête GET sur une API (ici,


jsonplaceholder.typicode.com) pour récupérer un post.

final response = await


http.get(Uri.parse('https://jsonplaceholder.typicode.com/posts/1'));

if (response.statusCode == 200) {

// Si le code de statut HTTP est 200 (succès), on décode la réponse


JSON.

var data = jsonDecode(response.body);

// Le corps de la réponse est décodé en objet Dart (en un Map).

return data['title'];

// On retourne le titre du post (la clé 'title' dans la réponse


JSON).

} else {

throw Exception('Échec de la récupération des données');

// Si le code de statut n'est pas 200, on lance une exception pour


signaler l'erreur.

11
@override

Widget build(BuildContext context) {

return Scaffold(

appBar: AppBar(title: Text('FutureBuilder Exemple')),

// Création de l'AppBar de l'application avec le titre "FutureBuilder


Exemple".

body: Center(

child: FutureBuilder<String>(

// FutureBuilder permet de travailler avec des données récupérées


de façon asynchrone (ici, depuis l'API).

future: fetchData(),

// On passe la fonction future 'fetchData' à FutureBuilder, qui


va exécuter la requête HTTP.

builder: (context, snapshot) {

// Le builder reçoit le contexte et le snapshot (qui contient


l'état de la requête et les données).

if (snapshot.connectionState == ConnectionState.waiting) {

return CircularProgressIndicator();

// Si la connexion est en attente (le future est encore en


cours d'exécution), on affiche un indicateur de chargement.

} else if (snapshot.hasError) {

return Text('Erreur: ${snapshot.error}');

// Si une erreur se produit, on affiche le message d'erreur


dans l'UI.

} else {

return Text('Titre: ${snapshot.data}');

// Si les données sont récupérées avec succès, on affiche le


titre du post récupéré.

12
}

},

),

),

);

Explication :

 Future<String> fetchData() : Fonction qui effectue une requête HTTP


asynchrone.
 FutureBuilder<String> : Permet d'attendre et d'afficher un widget en fonction de
l'état du Future.

Exercice 5 : Utilisation d'un InheritedWidget pour partager des données

Objectif : Utiliser un InheritedWidget pour partager des données entre différents widgets
de l'application.

import 'package:flutter/material.dart';

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

// Point d'entrée de l'application Flutter. Appelle la méthode runApp pour


afficher l'interface utilisateur définie par MyApp.

class MyApp extends StatelessWidget {

@override

Widget build(BuildContext context) {

return MaterialApp(home: InheritedWidgetExample());

// MaterialApp est l'élément de base pour l'application. Il affiche le


widget InheritedWidgetExample comme page d'accueil.

13
class Counter extends InheritedWidget {

final int count; // Déclare la variable count pour stocker la valeur du


compteur.

final Widget child; // Le widget enfant qui peut accéder à l'état de


l'inherited widget.

Counter({required this.count, required this.child}) : super(child:


child);

// Constructeur de Counter, qui prend un int count et un Widget child.

// Le widget child est transmis au constructeur de la classe parente


(InheritedWidget).

@override

bool updateShouldNotify(Counter oldWidget) {

// Cette méthode est appelée pour vérifier si l'état a changé.

// Si l'état a changé (ici, la valeur de 'count'), on retourne true


pour notifier les widgets enfants.

return oldWidget.count != count;

static Counter? of(BuildContext context) {

// Méthode statique qui permet de récupérer l'instance de Counter dans


le widget contextuel actuel.

return context.dependOnInheritedWidgetOfExactType<Counter>();

// dependOnInheritedWidgetOfExactType permet de récupérer un widget


InheritedWidget par son type exact.

class InheritedWidgetExample extends StatelessWidget {

14
@override

Widget build(BuildContext context) {

return Counter(

count: 5,

// On crée un Counter avec la valeur du compteur égale à 5 et le


widget enfant défini en dessous.

child: Scaffold(

appBar: AppBar(title: Text('InheritedWidget Exemple')),

// Création d'un AppBar avec le titre "InheritedWidget Exemple".

body: Center(

child: ElevatedButton(

onPressed: () {

final counter = Counter.of(context);

// Récupération de l'instance de Counter via la méthode


statique 'of', en utilisant le BuildContext.

print('Valeur du compteur: ${counter?.count}');

// Affichage de la valeur du compteur dans la console, si


Counter est trouvé.

},

child: Text('Afficher la valeur du compteur'),

// Texte du bouton : "Afficher la valeur du compteur".

),

),

),

);

Explication :

15
 InheritedWidget : Permet de partager un état (ici, un compteur) dans l'arbre des
widgets.
 updateShouldNotify : Vérifie si l'état du widget a changé.
 Counter.of(context) : Permet d'accéder à l'état dans un widget descendant.

Exercice 6 : Animation avec AnimatedContainer

Objectif : Animer la taille et la couleur d'un Container avec un AnimatedContainer.

import 'package:flutter/material.dart';

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


// Point d'entrée de l'application Flutter. Lance l'application en appelant
la méthode runApp et affiche MyApp.

class MyApp extends StatelessWidget {


@override
Widget build(BuildContext context) {
return MaterialApp(home: AnimatedContainerExample());
// MaterialApp est utilisé pour créer une application avec un thème
material design.
// Ici, la page d'accueil de l'application est
AnimatedContainerExample, qui montre un exemple d'animation.
}
}

class AnimatedContainerExample extends StatefulWidget {


@override
_AnimatedContainerExampleState createState() =>
_AnimatedContainerExampleState();
// Création d'un widget StatefulWidget pour pouvoir manipuler l'état du
container (sa taille et sa couleur).
}

class _AnimatedContainerExampleState extends


State<AnimatedContainerExample> {
// Déclaration des variables d'état pour la largeur, la hauteur et la
couleur du container.
double _width = 100;
double _height = 100;
Color _color = Colors.blue;

@override
void initState() {
super.initState();
// initState est appelé lors de l'initialisation de l'état du widget,
ici pour modifier l'état après 2 secondes.

Future.delayed(Duration(seconds: 2), () {
setState(() {
// Après 2 secondes, la méthode setState est appelée pour modifier
les valeurs d'état.
_width = 200; // Modification de la largeur du container
_height = 200; // Modification de la hauteur du container
_color = Colors.red; // Modification de la couleur du container
});

16
});
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('AnimatedContainer Exemple')),
// Création de la barre d'application avec un titre
"AnimatedContainer Exemple".

body: Center(
child: AnimatedContainer(
width: _width, // Largeur animée. L'animation ajustera cette
largeur à partir de sa valeur initiale.
height: _height, // Hauteur animée. L'animation ajustera cette
hauteur également.
color: _color, // Couleur animée. La couleur du container
changera lors de l'animation.

duration: Duration(seconds: 2),


// Durée de l'animation. L'animation prendra 2 secondes pour se
terminer après que l'état soit modifié.

curve: Curves.easeInOut,
// Courbe d'animation. `easeInOut` permet à l'animation de
démarrer lentement, puis d'accélérer et de ralentir avant la fin.
),
),
);
}
}

Explication :

 AnimatedContainer : Permet d'animer les propriétés du container, comme la taille et


la couleur.
 setState() : Déclenche la reconstruction de l'UI avec les nouvelles valeurs.
 duration et curve : Spécifient la durée et le type d'animation.

Exercice 7 : Utilisation de ListView.builder

Objectif : Créer une liste dynamique avec un ListView.builder qui affiche des éléments
générés.

import 'package:flutter/material.dart';

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

// Point d'entrée de l'application Flutter. La méthode runApp lance


l'application et affiche MyApp.

17
class MyApp extends StatelessWidget {

@override

Widget build(BuildContext context) {

return MaterialApp(home: ListViewExample());

// MaterialApp est le widget de base qui applique le thème Material


Design à l'application.

// Ici, il affiche la page ListViewExample comme écran d'accueil.

class ListViewExample extends StatelessWidget {

final List<String> items = List.generate(20, (index) => 'Élément ${index


+ 1}');

// Génération d'une liste dynamique contenant 20 éléments. Chaque élément


est une chaîne de caractères "Élément 1", "Élément 2", etc.

@override

Widget build(BuildContext context) {

return Scaffold(

appBar: AppBar(title: Text('ListView.builder Exemple')),

// Création de l'AppBar avec le titre "ListView.builder Exemple".

body: ListView.builder(

itemCount: items.length,

// `itemCount` définit le nombre d'éléments dans la liste. Ici, il


est égal à la longueur de la liste `items`.

itemBuilder: (context, index) {

// `itemBuilder` est une fonction qui permet de construire chaque


élément de la liste. Elle prend l'index de l'élément actuel.

18
return ListTile(

title: Text(items[index]),

// Chaque élément de la liste est un `ListTile`, avec le texte


affichant l'élément à l'index donné dans la liste `items`.

);

},

),

);

Explication :

 ListView.builder : Crée une liste paresseuse qui ne construit que les éléments
visibles.
 itemCount : Nombre d'éléments dans la liste.
 itemBuilder : Fonction qui construit chaque élément de la liste.

Exercice 8 : Gestion d'état avec Provider

Objectif : Utiliser le package provider pour gérer l'état et le partager entre plusieurs widgets.

import 'package:flutter/material.dart';

import 'package:provider/provider.dart'; // Importation de la bibliothèque


Provider pour la gestion d'état.

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

// Point d'entrée de l'application Flutter. La méthode runApp lance


l'application et affiche MyApp.

class MyApp extends StatelessWidget {

@override

Widget build(BuildContext context) {

return ChangeNotifierProvider(

19
create: (_) => Counter(), // Création et fourniture d'une instance de
Counter, un modèle de gestion d'état.

child: MaterialApp(home: CounterPage()),

// MaterialApp permet d'appliquer un thème Material à l'application.


La page d'accueil est CounterPage.

);

class Counter extends ChangeNotifier {

int _count = 0; // Variable privée _count pour stocker la valeur du


compteur

int get count => _count;

// Getter pour récupérer la valeur actuelle du compteur (lecture de


_count).

void increment() {

_count++; // Incrémenter la valeur du compteur de 1

notifyListeners();

// Notifier tous les écouteurs que l'état du compteur a changé.

// Cela permet de redessiner tous les widgets qui écoutent ce modèle.

class CounterPage extends StatelessWidget {

@override

Widget build(BuildContext context) {

final counter = Provider.of<Counter>(context);

20
// Utilisation de Provider pour récupérer l'instance actuelle de
Counter.

// Provider.of<Counter>(context) permet d'accéder à l'état du compteur


qui a été fourni par ChangeNotifierProvider.

// Cette instance est automatiquement mise à jour lorsque l'état change


(via notifyListeners).

return Scaffold(

appBar: AppBar(title: Text('Provider Exemple')),

// AppBar avec un titre "Provider Exemple".

body: Center(

child: Column(

mainAxisAlignment: MainAxisAlignment.center,

// Centre les widgets enfants dans l'axe principal (vertical).

children: [

Text('Compteur: ${counter.count}'),

// Affichage de la valeur actuelle du compteur en récupérant


counter.count.

ElevatedButton(

onPressed: () {

counter.increment(); // Lorsque le bouton est pressé, le


compteur est incrémenté.

},

child: Text('Incrémenter'),

// Affichage du texte du bouton "Incrémenter".

),

],

21
),

),

);

Explication :

 ChangeNotifierProvider : Permet de fournir une instance de modèle (Counter) à


l'arbre des widgets.
 notifyListeners() : Avertit les widgets abonnés que l'état a changé.
 Provider.of<Counter>(context) : Récupère l'état dans l'arbre des widgets pour
l'afficher.

22

Vous aimerez peut-être aussi