Refactoring + Checkpoint

This commit is contained in:
DahoudG
2024-11-17 23:00:18 +00:00
parent 1e888f41e8
commit 77ab8a02a2
56 changed files with 1904 additions and 790 deletions

View File

@@ -0,0 +1,26 @@
import 'package:flutter/material.dart';
class AccessibilityField extends StatelessWidget {
final Function(String?) onSaved;
const AccessibilityField({Key? key, required this.onSaved}) : super(key: key);
@override
Widget build(BuildContext context) {
return TextFormField(
decoration: const InputDecoration(
labelText: 'Accessibilité',
border: OutlineInputBorder(),
filled: true,
fillColor: Colors.white,
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Veuillez entrer des informations sur l\'accessibilité';
}
return null;
},
onSaved: onSaved,
);
}
}

View File

@@ -0,0 +1,26 @@
import 'package:flutter/material.dart';
class AccommodationInfoField extends StatelessWidget {
final Function(String?) onSaved;
const AccommodationInfoField({Key? key, required this.onSaved}) : super(key: key);
@override
Widget build(BuildContext context) {
return TextFormField(
decoration: const InputDecoration(
labelText: 'Informations sur l\'hébergement',
border: OutlineInputBorder(),
filled: true,
fillColor: Colors.white,
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Veuillez entrer des informations sur l\'hébergement';
}
return null;
},
onSaved: onSaved,
);
}
}

View File

@@ -0,0 +1,80 @@
import 'package:flutter/material.dart';
/// Un champ pour saisir le nombre maximum de participants à un événement.
/// Il est conçu pour permettre à l'utilisateur de saisir un nombre entier.
class AttendeesField extends StatelessWidget {
// Définition de la fonction de rappel pour sauver la valeur saisie.
final Function(int) onSaved;
// Le constructeur prend une fonction de rappel pour sauvegarder la valeur saisie.
const AttendeesField({Key? key, required this.onSaved}) : super(key: key);
@override
Widget build(BuildContext context) {
// Retourne un widget de type Column pour organiser le texte et le champ de saisie.
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Texte statique indiquant ce que l'utilisateur doit entrer.
const Text(
'Nombre maximum de participants', // Le texte est en français et indique le champ à remplir.
style: TextStyle(
color: Colors.blueGrey, // Couleur du texte
fontSize: 16,
fontWeight: FontWeight.bold, // Met en gras pour la visibilité
),
),
const SizedBox(height: 8), // Espacement entre le titre et le champ de saisie.
TextFormField(
keyboardType: TextInputType.number, // Le champ attend un nombre entier.
decoration: InputDecoration(
hintStyle: const TextStyle(color: Colors.blueGrey),
hintText: 'Entrez ici le nombre maximum de participants...', // L'invite pour aider l'utilisateur.
filled: true, // Le champ est rempli avec une couleur de fond.
fillColor: Colors.blueGrey.withOpacity(0.1), // Couleur de fond du champ avec opacité.
border: const OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(12.0)), // Bordure arrondie
borderSide: BorderSide.none, // Pas de bordure par défaut
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(12.0)),
borderSide: const BorderSide(
color: Colors.blueGrey, // Bordure de base
width: 1.5,
),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(12.0)),
borderSide: const BorderSide(
color: Colors.blue, // Bordure en bleu lors du focus
width: 2.0,
),
),
prefixIcon: const Icon(
Icons.group,
color: Colors.blueGrey, // Icône assortie
),
),
style: const TextStyle(
color: Colors.blueGrey, // Couleur du texte saisi
fontSize: 16.0, // Taille de police
fontWeight: FontWeight.w600, // Poids de la police pour la lisibilité
),
onChanged: (value) {
// Lors de chaque modification de texte, on tente de convertir la valeur en entier.
int? maxParticipants = int.tryParse(value) ?? 0; // Conversion en entier, avec une valeur par défaut de 0.
print('Nombre maximum de participants saisi : $maxParticipants'); // Log pour suivre la valeur saisie.
onSaved(maxParticipants); // Appel de la fonction onSaved pour transmettre la valeur au formulaire principal.
},
validator: (value) {
// Validation pour vérifier si la valeur est un nombre valide.
if (value == null || value.isEmpty) {
return 'Veuillez entrer un nombre de participants';
}
return null; // La validation est correcte si la valeur est un nombre
},
),
],
);
}
}

View File

@@ -0,0 +1,192 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart' as rootBundle;
class CategoryField extends StatefulWidget {
// Ce callback est utilisé pour enregistrer la valeur sélectionnée dans le formulaire
final FormFieldSetter<String> onSaved;
// Constructeur de la classe CategoryField
const CategoryField({Key? key, required this.onSaved}) : super(key: key);
@override
_CategoryFieldState createState() => _CategoryFieldState();
}
class _CategoryFieldState extends State<CategoryField> {
// Variable pour stocker la catégorie sélectionnée par l'utilisateur
String? _selectedCategory;
// Map pour stocker les catégories et leurs sous-catégories
Map<String, List<String>> _categoryMap = {};
// Liste des éléments du menu déroulant
List<DropdownMenuItem<String>> _dropdownItems = [];
@override
void initState() {
super.initState();
// Chargement des catégories dès que l'état est initialisé
_loadCategories();
}
/// Méthode pour charger les catégories depuis un fichier JSON.
/// Cette méthode récupère les catégories et sous-catégories depuis le fichier JSON
/// et met à jour l'état du widget.
Future<void> _loadCategories() async {
try {
// Chargement du fichier JSON à partir des ressources
final String jsonString = await rootBundle.rootBundle
.loadString('lib/assets/json/event_categories.json');
// Décodage du fichier JSON pour obtenir un Map
final Map<String, dynamic> jsonMap = json.decode(jsonString);
// Map pour stocker les catégories et leurs sous-catégories
final Map<String, List<String>> categoryMap = {};
// Parcours des catégories et ajout des sous-catégories dans le map
jsonMap['categories'].forEach((key, value) {
categoryMap[key] = List<String>.from(value);
});
// Mise à jour de l'état avec les nouvelles données chargées
setState(() {
_categoryMap = categoryMap;
_dropdownItems =
_buildDropdownItems(); // Reconstruction des éléments du menu
});
// Log pour vérifier si les catégories ont bien été chargées
debugPrint("Catégories chargées : $_categoryMap");
} catch (e) {
// Log en cas d'erreur lors du chargement
debugPrint("Erreur lors du chargement des catégories : $e");
// Affichage d'un message d'erreur à l'utilisateur si le chargement échoue
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text(
'Erreur lors du chargement des catégories. Veuillez réessayer plus tard.')));
}
}
/// Méthode pour construire la liste des éléments du menu déroulant avec les catégories et sous-catégories.
/// Cette méthode crée une liste d'éléments DropdownMenuItem pour afficher dans le DropdownButton.
List<DropdownMenuItem<String>> _buildDropdownItems() {
List<DropdownMenuItem<String>> items = [];
// Parcours des catégories et ajout des sous-catégories dans le menu déroulant
_categoryMap.forEach((category, subcategories) {
// Ajouter une catégorie (non sélectionnable) comme en-tête
items.add(
DropdownMenuItem<String>(
enabled: false,
// Cette entrée est désactivée pour qu'elle ne soit pas sélectionnée
child: Text(
category,
style: const TextStyle(
fontSize: 14,
fontWeight: FontWeight.bold,
color: Colors.blueGrey,
),
),
),
);
// Ajouter les sous-catégories associées à cette catégorie
for (String subcategory in subcategories) {
items.add(
DropdownMenuItem<String>(
value: subcategory, // Valeur de la sous-catégorie
child: Padding(
padding: const EdgeInsets.only(left: 16.0),
// Indentation pour les sous-catégories
child: Text(
subcategory,
style: const TextStyle(color: Colors.blueGrey),
),
),
),
);
}
});
// Log pour vérifier le nombre d'éléments créés pour le menu déroulant
debugPrint("Éléments créés pour le menu déroulant : ${items.length}");
return items;
}
@override
Widget build(BuildContext context) {
// Si la liste des éléments est vide, afficher un indicateur de chargement
return _dropdownItems.isEmpty
? const Center(
child:
CircularProgressIndicator()) // Affichage d'un indicateur de chargement pendant le chargement des données
: DropdownButtonFormField<String>(
value: _selectedCategory,
// Valeur sélectionnée par l'utilisateur
decoration: InputDecoration(
labelText: 'Catégorie',
// Libellé du champ
labelStyle: const TextStyle(color: Colors.blueGrey),
// Style du libellé
filled: true,
// Remplissage du champ
fillColor: Colors.blueGrey.withOpacity(0.1),
// Couleur de fond
border: const OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(10.0)),
borderSide: BorderSide(
color: Colors.blueGrey, // Couleur de la bordure par défaut
width: 2.0, // Épaisseur de la bordure
),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(10.0)),
borderSide: BorderSide(
color: Colors.blueGrey,
// Couleur de la bordure quand non sélectionné
width: 2.0,
),
),
focusedBorder: const OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(10.0)),
borderSide: BorderSide(
color: Colors.blue, // Bordure quand le champ est sélectionné
width: 2.0,
),
),
prefixIcon: const Icon(Icons.category,
color: Colors.blueGrey), // Icône du champ
),
style: const TextStyle(color: Colors.blueGrey),
// Style du texte sélectionné
dropdownColor: const Color(0xFF2C2C3E),
// Couleur de fond du menu déroulant
iconEnabledColor: Colors.blueGrey,
// Couleur de l'icône du menu déroulant
items: _dropdownItems,
// Liste des éléments du menu déroulant
onChanged: (String? newValue) {
// Log pour suivre la valeur sélectionnée
debugPrint("Nouvelle catégorie sélectionnée : $newValue");
setState(() {
_selectedCategory =
newValue; // Mise à jour de la catégorie sélectionnée
});
},
onSaved: widget.onSaved,
// Enregistrer la valeur dans le formulaire
hint: const Text(
'Veuillez choisir une catégorie',
// Texte affiché lorsqu'aucune catégorie n'est sélectionnée
style: TextStyle(
color: Colors.blueGrey), // Style du texte par défaut
),
);
}
}

View File

@@ -0,0 +1,79 @@
import 'package:flutter/material.dart';
/// `DescriptionField` est un champ de texte utilisé pour saisir une description.
/// Ce champ fait partie d'un formulaire et est conçu pour accepter plusieurs lignes de texte.
/// Il est doté de validations et d'une logique d'enregistrement personnalisée via `onSaved`.
///
/// Ce widget utilise des icônes et un style personnalisé pour correspondre à l'apparence de l'application.
///
/// Arguments :
/// - `onSaved`: Une fonction callback utilisée pour enregistrer la valeur du champ de texte une fois que le formulaire est soumis.
/// ```
class DescriptionField extends StatelessWidget {
// Callback utilisé pour enregistrer la valeur de la description
final FormFieldSetter<String> onSaved;
// Constructeur du widget DescriptionField
const DescriptionField({Key? key, required this.onSaved}) : super(key: key);
@override
Widget build(BuildContext context) {
// Log : Construction du champ DescriptionField
debugPrint('Construction du champ DescriptionField');
return TextFormField(
// Décoration du champ de texte
decoration: InputDecoration(
labelText: 'Description', // Texte étiquette affiché à l'utilisateur
labelStyle: const TextStyle(color: Colors.blueGrey), // Style de l'étiquette
filled: true, // Active le fond coloré
fillColor: Colors.blueGrey.withOpacity(0.1), // Couleur de fond plus douce et plus subtile
hintStyle: const TextStyle(color: Colors.blueGrey),
hintText: 'Entrez un la description ici...',
border: const OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(12.0)), // Bordure arrondie améliorée
borderSide: BorderSide.none, // Pas de bordure visible
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(12.0)),
borderSide: const BorderSide(
color: Colors.blueGrey, // Bordure de base en bleu gris
width: 2.0, // Largeur de la bordure
),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(12.0)),
borderSide: const BorderSide(
color: Colors.blue, // Bordure bleue lors du focus
width: 2.0, // Épaisseur de la bordure lors du focus
),
),
prefixIcon: const Icon(Icons.description, color: Colors.blueGrey), // Icône de description avant le texte
),
// Style du texte dans le champ
style: const TextStyle(color: Colors.blueGrey, fontSize: 16.0),
// Limite le champ à 3 lignes, avec un retour à la ligne automatique
maxLines: 3,
// Autres configurations du champ
textInputAction: TextInputAction.done, // Permet de soumettre avec la touche "Done" du clavier
// Validation du champ : assure que le champ n'est pas vide
validator: (value) {
// Log : Validation du champ DescriptionField
debugPrint('Validation du champ DescriptionField');
if (value == null || value.isEmpty) {
return 'Veuillez entrer une description'; // Message d'erreur si la description est vide
}
return null; // Retourne null si la validation passe
},
// Lors de la soumission du formulaire, enregistre la valeur saisie
onSaved: (value) {
// Log : Sauvegarde de la valeur de la description
debugPrint('Valeur de la description sauvegardée : $value');
onSaved(value); // Appel de la fonction onSaved passée en paramètre
},
);
}
}

View File

@@ -0,0 +1,63 @@
import 'package:flutter/material.dart';
class LinkField extends StatelessWidget {
// Le callback `onSaved` est utilisé pour enregistrer la valeur du champ lorsque le formulaire est soumis.
final FormFieldSetter<String> onSaved;
// Constructeur de la classe LinkField, qui attend le callback `onSaved`.
const LinkField({Key? key, required this.onSaved}) : super(key: key);
@override
Widget build(BuildContext context) {
// Création du champ de texte pour le lien
return TextFormField(
decoration: InputDecoration(
labelText: 'Lien (optionnel)', // Le texte affiché lorsqu'il n'y a pas de valeur
labelStyle: const TextStyle(color: Colors.blueGrey), // Style du texte du label
filled: true, // Remplissage du champ
fillColor: Colors.blueGrey.withOpacity(0.1), // Couleur de fond avec une légère opacité
border: const OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(10.0)), // Bords arrondis du champ
borderSide: BorderSide.none, // Pas de bordure visible
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(10.0)),
borderSide: BorderSide(
color: Colors.blueGrey, // Couleur de la bordure quand non sélectionné
width: 2.0,
),
),
focusedBorder: const OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(10.0)),
borderSide: BorderSide(
color: Colors.blue, // Bordure quand le champ est sélectionné
width: 2.0,
),
),
prefixIcon: const Icon(Icons.link, color: Colors.blueGrey), // Icône de lien à gauche
hintText: 'Entrez un lien ici...', // Texte d'indication lorsque le champ est vide
),
style: const TextStyle(color: Colors.blueGrey), // Style du texte saisi par l'utilisateur
onSaved: (value) {
// Log de la valeur du champ lorsqu'on l'enregistre
debugPrint("Lien enregistré : $value");
// Appel du callback `onSaved` pour enregistrer la valeur dans le formulaire
onSaved(value);
},
keyboardType: TextInputType.url, // Permet à l'utilisateur de saisir une URL
validator: (value) {
// Si le champ est rempli, on valide que la valeur est bien une URL correcte
if (value != null && value.isNotEmpty) {
final Uri? uri = Uri.tryParse(value);
if (uri == null || !uri.hasAbsolutePath) {
// Log en cas d'erreur de validation
debugPrint("URL invalide : $value");
return 'Veuillez entrer un lien valide';
}
}
return null;
},
);
}
}

View File

@@ -0,0 +1,72 @@
import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import '../../screens/location/location_picker_Screen.dart';
/// `LocationField` est un champ de saisie permettant de sélectionner une localisation sur une carte.
/// Il utilise la page `LocationPickerScreen` pour permettre à l'utilisateur de choisir un emplacement précis.
/// Ce widget est utilisé dans des formulaires et permet d'afficher la localisation sélectionnée.
///
/// Arguments :
/// - `location`: Une chaîne représentant la localisation actuelle à afficher.
/// - `selectedLatLng`: Une variable de type `LatLng?` représentant la latitude et la longitude de la localisation sélectionnée.
/// - `onLocationPicked`: Un callback pour retourner la localisation choisie par l'utilisateur.
///
class LocationField extends StatelessWidget {
final String location;
final LatLng? selectedLatLng;
final Function(LatLng?) onLocationPicked;
const LocationField({Key? key, required this.location, this.selectedLatLng, required this.onLocationPicked})
: super(key: key);
@override
Widget build(BuildContext context) {
// Log : Construction du champ LocationField
debugPrint('Construction du champ LocationField');
return GestureDetector(
onTap: () async {
// Log : L'utilisateur clique pour choisir une localisation
debugPrint('Utilisateur clique pour choisir une localisation');
final LatLng? pickedLocation = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const LocationPickerScreen(),
),
);
if (pickedLocation != null) {
// Log : L'utilisateur a sélectionné une nouvelle localisation
debugPrint('Nouvelle localisation sélectionnée : $pickedLocation');
onLocationPicked(pickedLocation);
}
},
child: AnimatedContainer(
duration: const Duration(milliseconds: 300), // Animation fluide lors du focus
padding: const EdgeInsets.symmetric(vertical: 12.0, horizontal: 16.0),
decoration: BoxDecoration(
color: Colors.blueGrey.withOpacity(0.1), // Fond plus visible, subtilement coloré
borderRadius: BorderRadius.circular(12.0), // Bordure arrondie améliorée
border: Border.all(
color: selectedLatLng == null ? Colors.blueGrey.withOpacity(0.5) : Colors.blue, // Bordure change selon l'état
width: 2.0,
),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
selectedLatLng == null
? 'Sélectionnez une localisation' // Message par défaut si aucune localisation sélectionnée
: 'Localisation: $location', // Affiche la localisation actuelle
style: const TextStyle(color: Colors.blueGrey, fontSize: 16.0),
),
const Icon(Icons.location_on, color: Colors.blueGrey),
],
),
),
);
}
}

View File

@@ -0,0 +1,54 @@
import 'package:flutter/material.dart';
/// Un champ de saisie pour l'organisateur, utilisé dans un formulaire.
class OrganizerField extends StatelessWidget {
// Fonction de rappel pour sauvegarder la valeur de l'organisateur.
final Function(String?) onSaved;
// Constructeur qui prend la fonction onSaved pour transmettre l'organisateur au formulaire.
const OrganizerField({Key? key, required this.onSaved}) : super(key: key);
@override
Widget build(BuildContext context) {
return TextFormField(
decoration: InputDecoration(
labelText: 'Organisateur', // Texte d'étiquette pour le champ de saisie.
labelStyle: const TextStyle(
color: Colors.blueGrey, // Couleur de l'étiquette en blueGrey.
),
prefixIcon: const Icon(
Icons.person, // Icône représentant un organisateur (utilisateur).
color: Colors.blueGrey, // Couleur de l'icône.
),
border: const OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(12.0)), // Bordure arrondie.
borderSide: BorderSide.none, // Pas de bordure par défaut.
),
enabledBorder: OutlineInputBorder(
borderRadius: const BorderRadius.all(Radius.circular(12.0)),
borderSide: const BorderSide(
color: Colors.blueGrey, // Bordure colorée en blueGrey.
width: 1.5, // Largeur de la bordure.
),
),
focusedBorder: OutlineInputBorder(
borderRadius: const BorderRadius.all(Radius.circular(12.0)),
borderSide: const BorderSide(
color: Colors.blue, // Bordure bleue au focus.
width: 2.0,
),
),
filled: true, // Le champ de saisie est rempli de couleur de fond.
fillColor: Colors.blueGrey.withOpacity(0.1), // Couleur de fond avec opacité.
),
validator: (value) {
// Validation pour vérifier que le champ n'est pas vide.
if (value == null || value.isEmpty) {
return 'Veuillez entrer un organisateur'; // Message d'erreur si vide.
}
return null;
},
onSaved: onSaved, // Fonction qui est appelée pour sauvegarder la valeur saisie.
);
}
}

View File

@@ -0,0 +1,26 @@
import 'package:flutter/material.dart';
class ParkingField extends StatelessWidget {
final Function(String?) onSaved;
const ParkingField({Key? key, required this.onSaved}) : super(key: key);
@override
Widget build(BuildContext context) {
return TextFormField(
decoration: const InputDecoration(
labelText: 'Informations sur le parking',
border: OutlineInputBorder(),
filled: true,
fillColor: Colors.white,
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Veuillez entrer des informations sur le parking';
}
return null;
},
onSaved: onSaved,
);
}
}

View File

@@ -0,0 +1,27 @@
import 'package:flutter/material.dart';
class ParticipationFeeField extends StatelessWidget {
final Function(String?) onSaved;
const ParticipationFeeField({Key? key, required this.onSaved}) : super(key: key);
@override
Widget build(BuildContext context) {
return TextFormField(
decoration: const InputDecoration(
labelText: 'Frais de participation',
border: OutlineInputBorder(),
filled: true,
fillColor: Colors.white,
),
keyboardType: TextInputType.number,
validator: (value) {
if (value == null || value.isEmpty) {
return 'Veuillez entrer les frais de participation';
}
return null;
},
onSaved: onSaved,
);
}
}

View File

@@ -0,0 +1,26 @@
import 'package:flutter/material.dart';
class PrivacyRulesField extends StatelessWidget {
final Function(String?) onSaved;
const PrivacyRulesField({Key? key, required this.onSaved}) : super(key: key);
@override
Widget build(BuildContext context) {
return TextFormField(
decoration: const InputDecoration(
labelText: 'Règles de confidentialité',
border: OutlineInputBorder(),
filled: true,
fillColor: Colors.white,
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Veuillez entrer des règles de confidentialité';
}
return null;
},
onSaved: onSaved,
);
}
}

View File

@@ -0,0 +1,26 @@
import 'package:flutter/material.dart';
class SecurityProtocolField extends StatelessWidget {
final Function(String?) onSaved;
const SecurityProtocolField({Key? key, required this.onSaved}) : super(key: key);
@override
Widget build(BuildContext context) {
return TextFormField(
decoration: const InputDecoration(
labelText: 'Protocole de sécurité',
border: OutlineInputBorder(),
filled: true,
fillColor: Colors.white,
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Veuillez entrer un protocole de sécurité';
}
return null;
},
onSaved: onSaved,
);
}
}

View File

@@ -0,0 +1,89 @@
import 'package:flutter/material.dart';
/// Un champ permettant à l'utilisateur de saisir des tags.
/// Il permet également d'afficher les tags saisis sous forme de chips (étiquettes).
class TagsField extends StatefulWidget {
// Fonction de rappel pour sauvegarder la liste des tags saisis.
final Function(List<String>) onSaved;
// Constructeur qui prend la fonction onSaved pour transmettre les tags au formulaire.
const TagsField({Key? key, required this.onSaved}) : super(key: key);
@override
_TagsFieldState createState() => _TagsFieldState(); // Création de l'état pour gérer les tags.
}
class _TagsFieldState extends State<TagsField> {
final TextEditingController _controller = TextEditingController(); // Contrôleur pour gérer l'entrée de texte.
List<String> _tags = []; // Liste pour stocker les tags saisis.
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start, // Alignement à gauche pour les éléments.
children: [
const SizedBox(height: 8), // Espacement entre le titre et le champ de saisie.
TextFormField(
controller: _controller, // Associe le contrôleur à ce champ de texte.
decoration: InputDecoration(
hintStyle: const TextStyle(color: Colors.blueGrey),
hintText: 'Entrez un les tags ici séparés par des virgules...',
labelText: 'Tags', // Texte d'étiquette pour le champ de saisie.
labelStyle: const TextStyle(
color: Colors.blueGrey, // Couleur de l'étiquette en blueGrey.
),
prefixIcon: const Icon(
Icons.tag, // Icône représentant un tag.
color: Colors.blueGrey, // Couleur de l'icône.
),
border: const OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(12.0)), // Bordure arrondie.
borderSide: BorderSide.none, // Pas de bordure par défaut.
),
enabledBorder: OutlineInputBorder(
borderRadius: const BorderRadius.all(Radius.circular(12.0)),
borderSide: const BorderSide(
color: Colors.blueGrey, // Bordure de base.
width: 1.5, // Largeur de la bordure.
),
),
focusedBorder: OutlineInputBorder(
borderRadius: const BorderRadius.all(Radius.circular(12.0)),
borderSide: const BorderSide(
color: Colors.blue, // Bordure bleue au focus.
width: 2.0,
),
),
filled: true, // Le champ est rempli avec une couleur de fond.
fillColor: Colors.blueGrey.withOpacity(0.1), // Couleur de fond du champ de texte avec opacité.
),
onFieldSubmitted: (value) {
print('Tags soumis : $value'); // Log pour suivre ce qui a été saisi avant la soumission.
_addTags(value); // Appel à la méthode _addTags pour ajouter les tags.
},
),
const SizedBox(height: 8), // Espacement entre le champ de saisie et les chips.
Wrap(
spacing: 8.0, // Espacement entre les chips.
children: _tags.map((tag) => Chip(
label: Text(tag), // Texte du tag à afficher.
backgroundColor: Colors.blueGrey.withOpacity(0.2), // Couleur de fond des chips.
labelStyle: const TextStyle(color: Colors.blueGrey), // Couleur du texte dans les chips.
)).toList(), // Génère une liste de Chips pour chaque tag.
),
],
);
}
// Fonction pour ajouter les tags à la liste.
void _addTags(String value) {
setState(() {
_tags = value.split(',') // Sépare les tags par des virgules.
.map((tag) => tag.trim()) // Supprime les espaces autour des tags.
.where((tag) => tag.isNotEmpty) // Exclut les tags vides.
.toList(); // Crée la liste de tags.
});
print('Tags ajoutés : $_tags'); // Log pour vérifier la liste de tags ajoutée.
widget.onSaved(_tags); // Envoie la liste des tags au formulaire principal.
}
}

View File

@@ -0,0 +1,54 @@
import 'package:flutter/material.dart';
class TitleField extends StatelessWidget {
final FormFieldSetter<String> onSaved;
const TitleField({Key? key, required this.onSaved}) : super(key: key);
@override
Widget build(BuildContext context) {
return TextFormField(
decoration: InputDecoration(
labelText: 'Titre',
labelStyle: const TextStyle(color: Colors.blueGrey), // Couleur du label
filled: true,
fillColor: Colors.blueGrey.withOpacity(0.1), // Fond plus doux
hintStyle: const TextStyle(color: Colors.blueGrey),
hintText: 'Entrez un le titre ici...',
border: const OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(12.0)), // Bordure plus arrondie
borderSide: BorderSide.none, // Pas de bordure par défaut
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(12.0)),
borderSide: const BorderSide(
color: Colors.blueGrey, // Bordure de base
width: 1.5,
),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(12.0)),
borderSide: const BorderSide(
color: Colors.blue, // Bordure en bleu lors du focus
width: 2.0,
),
),
prefixIcon: const Icon(
Icons.title,
color: Colors.blueGrey, // Icône assortie
),
),
style: const TextStyle(
color: Colors.blueGrey, // Texte en bleu pour un meilleur contraste
fontSize: 16.0, // Taille de police améliorée
fontWeight: FontWeight.w600, // Poids de la police pour la lisibilité
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Veuillez entrer un titre';
}
return null;
},
onSaved: onSaved,
);
}
}

View File

@@ -0,0 +1,54 @@
import 'package:flutter/material.dart';
/// Un champ de saisie pour les informations de transport, utilisé dans un formulaire.
class TransportInfoField extends StatelessWidget {
// Fonction de rappel pour sauvegarder les informations de transport.
final Function(String?) onSaved;
// Constructeur qui prend la fonction onSaved pour transmettre les informations de transport au formulaire.
const TransportInfoField({Key? key, required this.onSaved}) : super(key: key);
@override
Widget build(BuildContext context) {
return TextFormField(
decoration: InputDecoration( // Suppression du mot-clé 'const'
labelText: 'Informations de transport', // Texte d'étiquette pour le champ de saisie.
labelStyle: const TextStyle(
color: Colors.blueGrey, // Couleur de l'étiquette en blueGrey.
),
prefixIcon: const Icon(
Icons.directions_car, // Icône représentant un moyen de transport (voiture).
color: Colors.blueGrey, // Couleur de l'icône.
),
border: const OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(12.0)), // Bordure arrondie.
borderSide: BorderSide.none, // Pas de bordure par défaut.
),
enabledBorder: const OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(12.0)),
borderSide: BorderSide(
color: Colors.blueGrey, // Bordure colorée en blueGrey.
width: 1.5, // Largeur de la bordure.
),
),
focusedBorder: const OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(12.0)),
borderSide: BorderSide(
color: Colors.blue, // Bordure bleue au focus.
width: 2.0,
),
),
filled: true, // Le champ de saisie est rempli de couleur de fond.
fillColor: Colors.blueGrey.withOpacity(0.1), // Couleur de fond avec opacité.
),
validator: (value) {
// Validation pour vérifier que le champ n'est pas vide.
if (value == null || value.isEmpty) {
return 'Veuillez entrer des informations sur le transport'; // Message d'erreur si vide.
}
return null;
},
onSaved: onSaved, // Fonction qui est appelée pour sauvegarder la valeur saisie.
);
}
}