Créer et valider un formulaire avec Flutter
Sommaire
- 1- Objectif
- 2- Présentation
- 3- Créer un formulaire
- 3.1- Créer un formulaire avec une clé globale (
GlobalKey) - 3.1.1- Définition d'une
GlobalKey - 3.1.2- Exemple
- 3.1.3- Explication du code
- 3.2- Création d'un formulaire avec validation
- 3.2.1- Validation avec déclencheurs
- 3.2.2- Exemple
- 3.2.3- Explication du code
- 4- Applications
- 4.1- App-01
- 4.1.1- Travail demandé
- 4.2- App-02
- 4.2.1- Travail demandé
- 4.3- App-03
- 4.3.1- Travail demandé
- 4.3.2- Cours Flutter
Créer et valider un formulaire avec Flutter
-
Objectif
- Dans ce tutoriel flutter, nous allons apprendre à créer et valider un formulaire avec Flutter.
-
Présentation
- Les formulaires sont désormais un élément essentiel de toute application mobile ou Web. Les formulaires sont utilisés pour recueillir des informations auprès de l’utilisateur.
- Selon vos besoins, vos exigences et votre logique, vous pouvez créer un formulaire pour l’utilisateur, y compris l’authentification de l’utilisateur, la recherche, la commande, le filtrage, la réservation, etc.
- Le formulaire peut comporter des champs de texte, des cases à cocher et des boutons radio.
- Les formulaires font partie intégrante de toutes les applications mobiles et Web modernes.
- Il est utilisé pour recueillir des informations auprès des utilisateurs.
- Dans Flutter le widget Form agit comme un conteneur pour regrouper et valider plusieurs champs de formulaire.
- Lors de la création du formulaire, fournissez un fichier
GlobalKey. Cela identifie de manière unique le Formet permet la validation du formulaire dans une étape ultérieure. - Le widget form utilise le widget enfant TextFormField pour permettre aux utilisateurs de saisir le champ de texte. Ce widget affiche un champ de texte de conception de matériau et nous permet également d’afficher les erreurs de validation lorsqu’elles se produisent.
-
Créer un formulaire
- Un widget de
formest fourni par flutter pour créer des formulaires. - Il agit comme un conteneur, ce qui nous permet de regrouper plusieurs champs de formulaire.
- Le widget de form agit comme un conteneur, ce qui nous permet de regrouper et de valider les multiples champs du formulaire.
- Lorsque vous créez un formulaire, il est nécessaire de fournir le
GlobalKey. Cette clé identifie le formulaire de manière unique et vous permet d’effectuer toute validation dans les champs du formulaire. - Code source de la vidéo
-
Créer un formulaire avec une clé globale (
GlobalKey) - Pour créer un formulaire et pour commencer nous devons fournir une clé globale (
GlobalKey). -
Définition d’une
GlobalKey - Avant tout la clé globale (
GlobalKey) est utilisée pour identifier de manière unique le formulaire et permet la validation du formulaire lors d’une étape ultérieure. GlobalKey, il s’agit d’une clé unique sur l’ensemble de l’application. Grâce à cette clé, on peut trouver et accéder au Élément en question et récupérer sesStateet Widget.final _formKey = GlobalKey(); - Pour créer un formulaire, vous devez d’abord définir une clé globale (
GlobalKey). Dans notre exemple, nous avons défini a _formKey comme une clé globale. - Une clé globale ou
GlobalKeyest une clé qui, lorsqu’elle est transmise en tant que clé dans un widget, peut différencier le widget de tous les widgets d’une arborescence de widgets. -
Exemple
-
Explication du code
- Tout d’abord une classe personnalisée de nom MonFormulaire est créée.
- Ensuite une clé globale est définie à l’intérieur de cette classe avec le nom
_formKey, qui contient FormState, utilisé pour recevoir les widgets de formulaire (Form). - Certains styles personnalisés se trouvent également dans la méthode Build de cette classe et utilisent le périphérique
TextFormFieldpour fournir des champs de formulaire tels que le nom, le numéro de téléphone, la date de naissance ou simplement un champ standard. - InputDecoration est utilisé dans TextFormField pour styliser votre formulaire en modifiant les propriétés telles que les bordures, les étiquettes, les icônes, les conseils, les styles, etc.
- Enfin, nous avons ajouté un bouton pour soumettre le formulaire.
-
Création d’un formulaire avec validation
-
Validation avec déclencheurs
- Les trois étapes suivantes doivent être suivies pour créer un formulaire de validation dans Flutter.
- Étape 1 : Tout d’abord, utilisez le widget de formulaire avec la clé globale.
- Étape 2 : Puis, utilisez TextFormField pour fournir au champ de saisie une propriété de validation.
- Étape 3 : Enfin créez un bouton qui, lorsqu’il est cliqué, valide les champs du formulaire flutter, et s’il y a une erreur, il l’affiche.
-
Exemple
-
Explication du code
- Pour valider les propriétés d’entrée, nous avons utilisé
validator()dans TextFormField. Chaque fois que l’utilisateur saisit la mauvaise entrée, la fonction validator() renvoie une chaîne contenant un message d’erreur ; sinon, il renvoie une valeur nulle . - Cela doit être assuré que le TextFormField n’a pas besoin d’être vide ; sinon, il renverra un message d’erreur.
2-Validation avecautovalidate- Dans Flutter, la validation automatique fait généralement référence à la validation automatique des entrées utilisateur dans les champs de formulaire lorsque l’utilisateur interagit avec eux, sans nécessiter de déclencheurs de validation explicites tels que la soumission d’un formulaire ou l’appui sur un bouton « valider ».
- Flutter fournit un cadre flexible pour la création de formulaires et la validation. Pour obtenir une validation automatique dans Flutter, vous pouvez suivre ces étapes :
- **Étape 1 : Création du projet**
- 1. Créez un nouveau projet Flutter dans votre environnement de développement (par exemple, avec Flutter CLI ou dans un IDE comme VSCode ou Android Studio).
- **Étape 2 : Structure de base de l’application**
- 2. Dans le fichier `main.dart`, créez une application Flutter de base avec une barre de navigation, un titre et un corps.
- **Étape 3 : Ajout de TextFormFields**
- 3. Dans le corps de l’application, ajoutez deux `
TextFormField`. - 4. Pour chaque `
TextFormField`, ajoutez le texte d’invite (`hintText`) pour indiquer ce que l’utilisateur doit saisir. - **Étape 4 : Activation de la validation automatique**
- 5. Pour activer la validation automatique, définissez `
autovalidateMode` sur `AutovalidateMode.always` dans le widget `Form` qui englobe les `TextFormField`. - **Étape 5 : Validation de l’e-mail**
- 6. Pour le premier `
TextFormField`, ajoutez une validation de l’e-mail en utilisant `validator`. - 7. Utilisez une expression régulière pour valider l’e-mail.
- 8. Si l’e-mail n’est pas valide, retournez un message d’erreur.
- **Étape 6 : Validation du mot de passe**
- 9. Pour le deuxième `
TextFormField`, ajoutez une validation de mot de passe. - 10.Vérifiez la longueur du mot de passe, la présence de minuscules, de majuscules, de chiffres et de caractères spéciaux.
- 10.1.Vérifiez que la longueur du mot de passe est comprise entre 8 et 12 caractères.
- 10.2.Assurez-vous que le mot de passe contient au moins une lettre minuscule (a-z).
- 10.3.Assurez-vous que le mot de passe contient au moins une lettre majuscule (A-Z).
- 10.4.Assurez-vous que le mot de passe contient au moins un chiffre (0-9).
- 10.5.Assurez-vous que le mot de passe contient au moins un caractère spécial (par exemple, !@#$%^&*()).
validator: (value) { if (value == null || value.isEmpty) { return 'Veuillez entrer un mot de passe.'; } if (value.length < 8 || value.length > 12) { return 'Le mot de passe doit contenir entre 8 et 12 caractères.'; } // Continuez avec les autres vérifications... },if (!value.contains(RegExp(r'[a-z]'))) { return 'Le mot de passe doit contenir au moins une lettre minuscule.'; }if (!value.contains(RegExp(r'[A-Z]'))) { return 'Le mot de passe doit contenir au moins une lettre majuscule.'; }if (!value.contains(RegExp(r'[0-9]'))) { return 'Le mot de passe doit contenir au moins un chiffre.'; }if (!value.contains(RegExp(r'[!@#$%^&*()]'))) { return 'Le mot de passe doit contenir au moins un caractère spécial.'; } - 11. Si le mot de passe ne respecte pas les critères de validation, retournez un message d’erreur.
- **Étape 7 : Ajout de l’icône pour afficher/masquer le mot de passe**
- 12. À l’intérieur du
TextFormFieldpour le mot de passe, ajoutez unsuffixIconavec unGestureDetectoret une icône (par exemple, une icône « œil »). - 13.Utilisez un état (par exemple,
_obscurePassword) pour suivre si le mot de passe doit être masqué ou non. - 14.Lorsque l’utilisateur clique sur l’icône, utilisez
setStatepour inverser l’état_obscurePassword. - 15.Utilisez
_obscurePasswordcomme valeur pour la propriétéobscureTextduTextFormFieldpour masquer ou afficher le mot de passe en fonction de l’état. - **Étape 8 : Soumission du formulaire**
- 16. Ajoutez un bouton «
Submit» en bas du formulaire. - 17. Lorsque le bouton «
Submit» est cliqué, vérifiez si le formulaire est valide en utilisant `_formKey.currentState.validate()`. - 18. Si le formulaire est valide, affichez un message de confirmation. Sinon, les messages d’erreur seront automatiquement affichés sous les champs de texte invalides.
- En suivant ces étapes, vos apprenants pourront créer une application Flutter avec la validation automatique des champs de texte pour l’e-mail et le mot de passe. Assurez-vous de leur fournir le code d’exemple pour chaque étape et de les encourager à expérimenter et à comprendre comment chaque partie fonctionne

Pour créer un formulaire avec Flutter, vous pouvez utiliser les widgets TextField pour les entrées de texte, DropdownButton pour les menus déroulants, Radio pour les boutons radio, Checkbox pour les cases à cocher, et ElevatedButton pour les boutons d’action.
form_advanced_page.dart
import 'package:flutter/material.dart';
class FormAdvancedPage extends StatefulWidget {
const FormAdvancedPage({super.key});
@override
State<FormAdvancedPage> createState() => _FormAdvancedPageState();
}
class _FormAdvancedPageState extends State<FormAdvancedPage> {
final _formKey = GlobalKey<FormState>();
String? _selectedCountry;
String? _gender;
bool _newsletter = false;
double _age = 18;
final List<String> _countries = [
"Tunisie",
"Algérie",
"Maroc",
"France",
];
void _submit() {
if (!_formKey.currentState!.validate()) return;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
"VALIDÉ ✓ — Pays: $_selectedCountry — Genre: $_gender — Âge: ${_age.toInt()} — Newsletter: $_newsletter"),
),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("Formulaire — Niveau Avancé"),
),
body: SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// --- Dropdown ---
DropdownButtonFormField<String>(
decoration: const InputDecoration(
labelText: "Choisir un pays",
border: OutlineInputBorder(),
),
items: _countries
.map((c) => DropdownMenuItem(
value: c,
child: Text(c),
))
.toList(),
onChanged: (v) => setState(() => _selectedCountry = v),
validator: (v) =>
v == null ? "Veuillez choisir un pays" : null,
),
const SizedBox(height: 20),
// --- Radio ---
const Text("Genre :", style: TextStyle(fontSize: 16)),
RadioListTile(
title: const Text("Homme"),
value: "Homme",
groupValue: _gender,
onChanged: (v) => setState(() => _gender = v),
),
RadioListTile(
title: const Text("Femme"),
value: "Femme",
groupValue: _gender,
onChanged: (v) => setState(() => _gender = v),
),
if (_gender == null)
const Padding(
padding: EdgeInsets.only(left: 16),
child: Text(
"Veuillez choisir un genre",
style: TextStyle(color: Colors.red),
),
),
const SizedBox(height: 20),
// --- Slider ---
Text("Âge : ${_age.toInt()} ans"),
Slider(
value: _age,
min: 10,
max: 80,
divisions: 70,
label: _age.toInt().toString(),
onChanged: (v) => setState(() => _age = v),
),
const SizedBox(height: 20),
// --- Switch ---
SwitchListTile(
value: _newsletter,
title: const Text("Recevoir la newsletter"),
onChanged: (v) => setState(() => _newsletter = v),
),
const SizedBox(height: 30),
// --- Bouton Soumettre ---
SizedBox(
width: double.infinity,
child: ElevatedButton(
onPressed: () {
if (_gender == null) setState(() {});
_submit();
},
child: const Text("Soumettre"),
),
),
],
),
),
),
);
}
}
import 'package:flutter/material.dart';
import 'form_advanced_page.dart';
class FormPage extends StatefulWidget {
const FormPage({super.key});
@override
State<FormPage> createState() => _FormPageState();
}
class _FormPageState extends State<FormPage> {
// Clé globale pour identifier et contrôler le formulaire
final _formKey = GlobalKey<FormState>();
// Controllers pour récupérer les valeurs saisies
final _nameCtrl = TextEditingController();
final _emailCtrl = TextEditingController();
final _passCtrl = TextEditingController();
// Valeur de la checkbox
bool _accepted = false;
// Libération des contrôleurs pour éviter les fuites mémoire
@override
void dispose() {
_nameCtrl.dispose();
_emailCtrl.dispose();
_passCtrl.dispose();
super.dispose();
}
// --- Fonction de validation spécifique à l'email ---
// Cette fonction prend en entrée une valeur de type String? (peut être null)
// et retourne soit un message d'erreur (String), soit null si le champ est valide.
String? _emailValidator(String? value) {
// 1️ Vérification si le champ est vide ou null
//Vide : signifie que la chaîne existe (String non nulle), mais ne contient aucun caractère ou uniquement des espaces. Exemple : "" ou " ".
//Null : signifie que la variable n’a pas de valeur du tout, elle n’existe pas encore ou n’a pas été initialisée.
if (value == null || value.trim().isEmpty) {
// trim() supprime les espaces avant et après la chaîne
// Si le champ est vide, on retourne un message d'erreur
return 'Veuillez entrer un email';
}
// 2️ Définition d'une expression régulière (Regex) simple pour vérifier le format d'un email
final emailRegex = RegExp(r'^[\w\.-]+@[\w\.-]+\.\w+$');
/*
Explication du Regex :
^ → début de la chaîne
[\w\.-]+ → un ou plusieurs caractères alphanumériques, '.' ou '-'
@ → caractère '@' obligatoire
[\w\.-]+ → un ou plusieurs caractères alphanumériques, '.' ou '-'
\. → un point obligatoire
\w+ → un ou plusieurs caractères alphanumériques pour le domaine
$ → fin de la chaîne
*/
// 3️ Vérification si l'email correspond au format attendu
if (!emailRegex.hasMatch(value.trim())) {
// Si la regex ne correspond pas, retourne un message d'erreur
return 'Email invalide';
}
// 4️ Si toutes les vérifications sont passées, retourne null (champ valide)
return null;
}
// --- Fonction appelée lors de l'appui sur le bouton "Soumettre" ---
void _submit() {
// 1️- Validation de tous les champs du formulaire
// _formKey.currentState?.validate() renvoie true si tous les champs sont valides
// Le "?? false" permet de gérer le cas où currentState est null
final isValid = _formKey.currentState?.validate() ?? false;
// 2️- Si un champ est invalide, on arrête la fonction ici
if (!isValid) return;
// 3️- Récupération des valeurs saisies
// trim() supprime les espaces inutiles au début et à la fin
final name = _nameCtrl.text.trim();
final email = _emailCtrl.text.trim();
// 4️- Affichage d'une notification à l'utilisateur
// ScaffoldMessenger permet d'afficher un SnackBar
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Succès ! Bienvenue $name — email : $email')),
);
// Optionnel : tu peux réinitialiser le formulaire après validation
// _formKey.currentState?.reset();
// setState(() => _accepted = false);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
elevation: 4,
title: const Text('Formulaire avec validation'),
backgroundColor: Colors.blueAccent,
foregroundColor: Colors.white,
actions: [IconButton(onPressed: () {}, icon: Icon(Icons.help))],
),
body: SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Form(
key: _formKey,
child: Column(
children: [
// --- Champ Nom ---
TextFormField(
controller: _nameCtrl,
decoration: const InputDecoration(
labelText: 'Nom',
hintText: 'Ex : Riadh Hajji',
border: OutlineInputBorder(),
),
validator: (value) {
if (value == null || value.trim().isEmpty) {
return 'Veuillez entrer votre nom';
}
if (value.trim().length < 2) {
return 'Le nom doit contenir au moins 2 caractères';
}
return null;
},
),
const SizedBox(height: 16),
// --- Champ Email ---
TextFormField(
controller: _emailCtrl,
decoration: const InputDecoration(
labelText: 'Email',
hintText: 'exemple@domaine.com',
border: OutlineInputBorder(),
),
keyboardType: TextInputType.emailAddress,
validator: _emailValidator,
),
const SizedBox(height: 16),
// --- Champ Mot de passe ---
TextFormField(
controller: _passCtrl,
decoration: InputDecoration(
labelText: 'Mot de passe',
hintText: 'Au moins 6 caractères',
border: const OutlineInputBorder(),
suffixIcon: IconButton(
onPressed: () {},
icon: const Icon(Icons.visibility),
),
suffixIconColor: Colors.blue,
),
obscureText: true,
validator: (value) {
if (value == null || value.isEmpty) {
return 'Veuillez entrer un mot de passe';
}
if (value.length < 6) {
return 'Mot de passe trop court (>=6)';
}
return null;
},
),
const SizedBox(height: 16),
// --- Checkbox avec validation ---
// On utilise FormField<bool> pour intégrer la checkbox dans le Form
FormField<bool>(
initialValue:
_accepted, // valeur initiale (bool) de la checkbox
validator: (value) {
// Fonction de validation appelée lors de _formKey.currentState?.validate()
// Si la checkbox n'est pas cochée, on retourne un message d'erreur
if (value != true) {
return 'Vous devez accepter les conditions';
}
return null; // sinon pas d'erreur
},
builder: (state) {
// builder permet de créer l'UI personnalisée de la checkbox
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Checkbox(
value:
state.value, // valeur actuelle de la checkbox
onChanged: (v) {
state.didChange(
v,
); // notifie le FormField du changement
setState(
() => _accepted = v ?? false,
); // met à jour l'état local
},
),
const SizedBox(width: 8),
const Expanded(
child: Text(
"J'accepte les conditions d'utilisation",
style: TextStyle(
color: Colors.blue,
fontWeight: FontWeight.bold,
),
),
),
],
),
// --- Affichage du message d’erreur si validation échoue ---
if (state.hasError)
Padding(
padding: const EdgeInsets.only(left: 12),
child: Text(
state.errorText ??
'', // texte de l'erreur renvoyé par validator
style: TextStyle(
color: Theme.of(
context,
).colorScheme.error, // couleur rouge par défaut
),
),
),
],
);
},
),
const SizedBox(height: 24),
// --- Bouton Soumettre ---
SizedBox(
width: double.infinity,
child: ElevatedButton(
onPressed: _submit,
child: const Padding(
padding: EdgeInsets.symmetric(vertical: 14.0),
child: Text('Soumettre', style: TextStyle(fontSize: 16)),
),
),
),
const SizedBox(height: 20),
// --- Bouton pour aller à la 2e page ---
SizedBox(
width: double.infinity,
child: OutlinedButton(
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (_) => const FormAdvancedPage(),
),
);
},
child: const Text(
"Accéder aux exercices avancés",
style: TextStyle(fontSize: 15),
),
),
),
],
),
),
),
);
}
}
main.dart
import 'package:flutter/material.dart';
import 'form_page.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Form Validation Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
inputDecorationTheme: InputDecorationTheme(
labelStyle: const TextStyle(
color: Colors.blue,
fontWeight: FontWeight.bold,
),
hintStyle: const TextStyle(
color: Colors.grey,
fontStyle: FontStyle.italic,
),
// ===== Bordure avec radius =====
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: const BorderSide(
color: Colors.blue,
width: 2,
),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: const BorderSide(
color: Colors.blueAccent,
width: 1.5,
),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: const BorderSide(
color: Colors.blue,
width: 2,
),
),
errorBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: const BorderSide(
color: Colors.red,
width: 1.5,
),
),
focusedErrorBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: const BorderSide(
color: Colors.red,
width: 2,
),
),
),
),
home: const FormPage(),
);
}
}


```dart
autovalidateMode: AutovalidateMode.always,
```
Solution
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(debugShowCheckedModeBanner: false,
home: MyForm(),
);
}
}
class MyForm extends StatefulWidget {
const MyForm({super.key});
@override
_MyFormState createState() => _MyFormState();
}
class _MyFormState extends State<MyForm> {
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
bool _obscurePassword = true; // Par défaut, le mot de passe est masqué
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Formulaire d\'autovalidation'),
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: Form(
key: _formKey,
autovalidateMode: AutovalidateMode.always,
child: Column(
children: [
Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: TextFormField(
decoration: const InputDecoration(
labelText: 'Email',
hintText: 'Entrez votre adresse email',
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Veuillez entrer une adresse email.';
}
// Expression régulière pour la validation de l'adresse e-mail
const pattern = r'^[\w-]+(\.[\w-]+)*@([\w-]+\.)+[a-zA-Z]{2,7}$';
final regex = RegExp(pattern);
if (!regex.hasMatch(value)) {
return 'Veuillez entrer une adresse email valide.';
}
return null;
},
),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 12.0),
child: TextFormField(
decoration: InputDecoration(
labelText: 'Password',
hintText: 'Entrez votre mot de passe',
suffixIcon: GestureDetector(
onTap: () {
setState(() {
_obscurePassword = !_obscurePassword;
});
},
child: Icon(
_obscurePassword
? Icons.visibility_off
: Icons.visibility,
),
),
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Veuillez entrer un mot de passe.';
}
// Vérifier la longueur du mot de passe
if (value.length < 8 || value.length > 12) {
return 'Le mot de passe doit contenir entre 8 et 12 caractères.';
}
// Vérifier la présence d'au moins une minuscule
if (!value.contains(RegExp(r'[a-z]'))) {
return 'Le mot de passe doit contenir au moins une lettre minuscule.';
}
// Vérifier la présence d'au moins une majuscule
if (!value.contains(RegExp(r'[A-Z]'))) {
return 'Le mot de passe doit contenir au moins une lettre majuscule.';
}
// Vérifier la présence d'au moins un chiffre
if (!value.contains(RegExp(r'[0-9]'))) {
return 'Le mot de passe doit contenir au moins un chiffre.';
}
// Vérifier la présence d'au moins un caractère spécial
if (!value.contains(RegExp(r'[!@#$%^&*(),.?":{}|<>]'))) {
return 'Le mot de passe doit contenir au moins un caractère spécial.';
}
return null;
},
obscureText: _obscurePassword,
),
),
ElevatedButton(
child: const Text('Submit'),
onPressed: () {
if (_formKey.currentState?.validate() == true) {
// Form is valid, process the data
// You can access form data using _formKey.currentState!.fields
}
},
),
],
),
),
),
);
}
}
Applications
-
App-01
-
Travail demandé
- Pour commencer, créer une page de connexion simple contenant les champs suivants :
- Nom & Prénom
- Numéro de téléphone
- Mot de passe
- Pour la validation, nous souhaitons que les utilisateurs de notre application remplissent les informations correctes dans chacun de ces champs. La logique sera définie comme telle :
- Tout d’abord, pour le champ du nom, nous souhaitons que l’utilisateur saisisse un prénom et un nom de famille valides, qui peuvent être accompagnés d’initiales.
- Pour le champ email, nous voulons un email valide qui contient quelques caractères avant le signe « @ », ainsi que le domaine email à la fin de l’email.
- Ensuite pour la validation du numéro de téléphone, l’utilisateur doit saisir 11 chiffres en commençant par le chiffre zéro.
- Enfin, pour la validation de notre mot de passe, nous attendons de l’utilisateur qu’il utilise une combinaison d’une lettre majuscule, d’une lettre minuscule, d’un chiffre et d’un caractère spécial.
- Ce n’est que lorsque la saisie de l’utilisateur correspond à ce qui précède que nous voulons accepter sa saisie avant de faire des demandes, telles que l’envoi à un serveur ou l’enregistrement dans une base de données.
-
App-02
-
Travail demandé
- Créez une application Flutter comme l’illustre le schéma suivant:
-
App-03
-
Travail demandé
- Vous allez implémenter une application Flutter simple qui affiche un écran de connexion. L’écran contient deux champs de texte : Email et mot de passe.
- Votre application doive être appliquée selon la conception spécifiée comme indiqué ci-dessous
Solution
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
@override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
AutovalidateMode _autoValidate = AutovalidateMode.disabled;
String? _name;
String? _mobile;
String? _email;
void _validateInputs() {
if (_formKey.currentState!.validate()) {
_formKey.currentState!.save();
} else {
setState(() {
_autoValidate = AutovalidateMode.always;
});
}
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Form Validation'),
),
body: SingleChildScrollView(
child: Container(
margin: const EdgeInsets.all(15.0),
child: Form(
key: _formKey,
autovalidateMode: _autoValidate,
child: formUI(),
),
),
),
),
);
}
Widget formUI() {
return Column(
children: <Widget>[
TextFormField(
decoration: const InputDecoration(labelText: 'Name'),
keyboardType: TextInputType.text,
validator: validateName,
onSaved: (String? val) {
_name = val;
},
),
TextFormField(
decoration: const InputDecoration(labelText: 'Mobile'),
keyboardType: TextInputType.phone,
validator: validateMobile,
onSaved: (String? val) {
_mobile = val;
},
),
TextFormField(
decoration: const InputDecoration(labelText: 'Email'),
keyboardType: TextInputType.emailAddress,
validator: validateEmail,
onSaved: (String? val) {
_email = val;
},
),
const SizedBox(
height: 10.0,
),
OutlinedButton(
onPressed: _validateInputs,
child: const Text('Validate'),
)
],
);
}
String? validateName(String? value) {
if (value!.isEmpty) {
return 'Name cannot be empty';
}
if (value.length < 3) {
return 'Name must be more than 2 charater';
} else {
return null;
}
}
String? validateMobile(String? value) {
if (value!.isEmpty) {
return 'Phone number cannot be empty';
}
if (value.length != 10) {
return 'Mobile Number must be of 10 digit';
} else {
return null;
}
}
String? validateEmail(String? value) {
String pattern =
r'^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$';
RegExp regex = RegExp(pattern);
if (value!.isEmpty) {
return 'Email cannot be empty';
}
if (!regex.hasMatch(value)) {
return 'Enter Valid Email';
} else {
return null;
}
}
}

Solution
import 'package:flutter/material.dart';
void main() => runApp(MyWidget());
class MyWidget extends StatelessWidget {
const MyWidget({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Application Flutter',
debugShowCheckedModeBanner: false,
home: Scaffold(
appBar: AppBar(title: const Text("Connexion")),
body: const LoginForm(),
),
);
}
}
class LoginForm extends StatefulWidget {
const LoginForm({super.key});
@override
_LoginFormState createState() => _LoginFormState();
}
class _LoginFormState extends State&<LoginForm> {
final _formKey = GlobalKey<FormState>();
String? _email, _password;
@override
Widget build(BuildContext context) {
return Form(
key: _formKey,
child: Column(
children: [
Padding(
padding: const EdgeInsets.fromLTRB(40, 10, 40, 0),
child: TextFormField(
validator: (input) {
if (input!.isEmpty) {
return 'Entrer une adresse email valide';
}
return null;
},
onSaved: (input) => _email = input!,
decoration: const InputDecoration(labelText: 'Email'),
),
),
Padding(
padding: const EdgeInsets.fromLTRB(40, 6, 40, 0),
child: TextFormField(
validator: (input) {
if (input!.length < 6) {
return 'Le mot de passe doit contenir au moins 6 caractères';
}
},
onSaved: (input) => _password = input!,
decoration: const InputDecoration(labelText: 'Mot de passe'),
obscureText: true,
),
),
Padding(
padding: const EdgeInsets.fromLTRB(40, 6, 40, 10),
child: ElevatedButton(
onPressed: () {
if (_formKey.currentState!.validate()) {
_formKey.currentState!.save();
// Appel API de connexion avec _email et _password
}
},
child: const Text('Se connecter'),
),
),
],
),
);
}
}

Solution
import 'package:flutter/material.dart';
void main() => runApp(MyWidget());
class MyWidget extends StatelessWidget {
const MyWidget({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Valider Formulaire',
debugShowCheckedModeBanner: false,
home: Scaffold(
appBar: AppBar(title: const Text("Connexion")),
body: const ValiderFormulaire(),
),
);
}
}
class ValiderFormulaire extends StatefulWidget {
const ValiderFormulaire({super.key});
@override
_ValiderFormulaireState createState() => _ValiderFormulaireState();
}
class _ValiderFormulaireState extends State<ValiderFormulaire> {
final _formKey = GlobalKey<FormState>();
bool _accepterCGU = false;
@override
Widget build(BuildContext context) {
return Form(
key: _formKey,
child: Container(
child: Padding(
padding: const EdgeInsets.fromLTRB(40, 4, 40, 4),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
TextFormField(
decoration: const InputDecoration(labelText: 'Nom'),
validator: (value) {
if (value!.isEmpty) {
return 'Veuillez saisir votre nom';
}
return null;
},
),
CheckboxListTile(
title: const Text('J\'accepte les CGU'),
value: _accepterCGU,
onChanged: (value) {
setState(() {
_accepterCGU = value!;
});
},
),
ElevatedButton(
onPressed: () {
if (_formKey.currentState!.validate()) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Données en cours')));
}
},
child: const Text('Submit'),
),
],
),
),
),
);
}
}
