# Widgets communs GEOSECTOR ## Description générale GEOSECTOR utilise un ensemble de widgets communs réutilisables pour maintenir une cohérence visuelle et simplifier le développement. Ces widgets sont conçus pour s'adapter à la fois à l'interface web et mobile, et respectent les thèmes définis dans l'application. Ce document détaille les widgets principaux, leurs propriétés et leur utilisation. ## Interface utilisateur de base ### CustomButton Un bouton stylisé et réutilisable avec gestion de l'état de chargement. **Fichier**: `lib/presentation/widgets/custom_button.dart` **Propriétés principales**: | Propriété | Type | Description | Requis | |----------------|----------------|---------------------------------------------------|--------| | onPressed | VoidCallback? | Fonction appelée lors du clic | Oui | | text | String | Texte du bouton | Oui | | icon | IconData? | Icône à afficher à gauche du texte | Non | | isLoading | bool | Afficher un indicateur de chargement | Non | | width | double? | Largeur personnalisée | Non | | backgroundColor| Color? | Couleur de fond (utilise primary par défaut) | Non | | textColor | Color? | Couleur du texte (utilise white par défaut) | Non | **Exemple d'utilisation**: ```dart CustomButton( onPressed: () => saveData(), text: 'Enregistrer', icon: Icons.save, isLoading: isSaving, backgroundColor: Colors.green, ) ``` ### CustomTextField Champ de texte stylisé et réutilisable avec gestion des validateurs. **Fichier**: `lib/presentation/widgets/custom_text_field.dart` **Propriétés principales**: | Propriété | Type | Description | Requis | |-----------------|------------------------|------------------------------------------------|--------| | controller | TextEditingController | Contrôleur du champ | Oui | | label | String | Libellé du champ | Oui | | hintText | String? | Texte indicatif lorsque vide | Non | | prefixIcon | IconData? | Icône au début du champ | Non | | suffixIcon | Widget? | Widget à la fin du champ | Non | | obscureText | bool | Masquer le texte (pour les mots de passe) | Non | | keyboardType | TextInputType | Type de clavier | Non | | validator | Function? | Fonction de validation | Non | | maxLines | int? | Nombre maximum de lignes | Non | | minLines | int? | Nombre minimum de lignes | Non | | readOnly | bool | Lecture seule | Non | | onChanged | Function? | Fonction appelée lors des modifications | Non | | fillColor | Color? | Couleur de fond du champ | Non | **Exemple d'utilisation**: ```dart CustomTextField( controller: _emailController, label: 'Adresse email', hintText: 'Saisissez votre email', prefixIcon: Icons.email, keyboardType: TextInputType.emailAddress, validator: (value) => value.isEmpty ? 'Email requis' : null, ) ``` ## Navigation et mise en page ### DashboardAppBar Barre d'applications personnalisée pour les tableaux de bord. **Fichier**: `lib/presentation/widgets/dashboard_app_bar.dart` **Propriétés principales**: | Propriété | Type | Description | Requis | |----------------------|--------------|--------------------------------------------------|--------| | title | String | Titre principal de l'AppBar | Oui | | pageTitle | String? | Titre de la page actuelle | Non | | additionalActions | List?| Actions supplémentaires | Non | | showNewPassageButton | bool | Afficher le bouton "Nouveau passage" | Non | | onNewPassagePressed | VoidCallback?| Fonction appelée pour créer un nouveau passage | Non | | isAdmin | bool | Indique si l'utilisateur est un administrateur | Non | **Exemple d'utilisation**: ```dart DashboardAppBar( title: 'GEOSECTOR', pageTitle: 'Tableau de bord', isAdmin: userRepository.isAdmin(), showNewPassageButton: true, onNewPassagePressed: () => Navigator.pushNamed(context, '/passage/new'), ) ``` ## Visualisation cartographique ### MapboxMap Widget de carte réutilisable utilisant Mapbox pour afficher des marqueurs et des polygones. **Fichier**: `lib/presentation/widgets/mapbox_map.dart` **Propriétés principales**: | Propriété | Type | Description | Requis | |------------------|----------------------------|----------------------------------------------|--------| | initialPosition | LatLng | Position initiale de la carte | Non | | initialZoom | double | Niveau de zoom initial | Non | | markers | List? | Liste des marqueurs à afficher | Non | | polygons | List? | Liste des polygones à afficher | Non | | mapController | MapController? | Contrôleur de carte externe | Non | | onMapEvent | Function(MapEvent)? | Callback lors des événements de carte | Non | | showControls | bool | Afficher les boutons de contrôle | Non | | mapStyle | String? | Style de la carte Mapbox | Non | **Exemple d'utilisation**: ```dart MapboxMap( initialPosition: LatLng(48.1173, -1.6778), initialZoom: 13.0, markers: sectorMarkers, polygons: [ Polygon( points: sector.getCoordinates().map((p) => LatLng(p[0], p[1])).toList(), color: Color(int.parse(sector.color.replaceAll('#', '0xFF'))), borderStrokeWidth: 2, borderColor: Colors.black, ), ], showControls: true, ) ``` ## Affichage des données ### PassagesListWidget Widget réutilisable pour afficher une liste de passages avec filtres et actions. **Fichier**: `lib/presentation/widgets/passages/passages_list_widget.dart` **Propriétés principales**: | Propriété | Type | Description | Requis | |--------------------|----------------------------------------|-----------------------------------------------|--------| | passages | List> | Liste des passages à afficher | Oui | | title | String? | Titre de la section | Non | | maxPassages | int? | Nombre maximum de passages à afficher | Non | | showFilters | bool | Afficher les filtres | Non | | showSearch | bool | Afficher la barre de recherche | Non | | showActions | bool | Afficher les boutons d'action | Non | | onPassageSelected | Function(Map)? | Callback à la sélection d'un passage | Non | | onPassageEdit | Function(Map)? | Callback pour éditer un passage | Non | | onReceiptView | Function(Map)? | Callback pour voir un reçu | Non | | initialTypeFilter | String? | Filtre de type initial | Non | | initialPaymentFilter| String? | Filtre de paiement initial | Non | **Format attendu des passages**: ```dart [ { 'id': 1001, 'address': '12 Rue des Lilas, Paris', 'type': 1, // Correspond à AppKeys.typesPassages 'payment': 2, // Correspond à AppKeys.typesReglements 'date': DateTime(2025, 4, 15), 'amount': 25.50, 'name': 'Martin Dupont', 'notes': 'Premier étage à droite', 'hasError': false, 'fkUser': 123 // ID de l'utilisateur qui a effectué le passage }, // ... ] ``` **Exemple d'utilisation**: ```dart PassagesListWidget( passages: passagesList, title: 'Historique des passages', showFilters: true, showSearch: true, onPassageSelected: (passage) => showPassageDetails(passage), onPassageEdit: (passage) => editPassage(passage), onReceiptView: (passage) => viewReceipt(passage), ) ``` ## Visualisation de données ### ActivityChart Graphique d'activité affichant les passages par jour/semaine/mois. **Fichier**: `lib/presentation/widgets/charts/activity_chart.dart` **Propriétés principales**: | Propriété | Type | Description | Requis | |----------------------|-----------------------------|-----------------------------------------------|--------| | passageData | List>? | Données des passages | Non* | | periodType | String | Type de période (Jour, Semaine, Mois) | Non | | height | double | Hauteur du graphique | Non | | daysToShow | int | Nombre de jours à afficher | Non | | userId | int? | ID de l'utilisateur pour filtrer | Non | | excludePassageTypes | List | Types de passages à exclure | Non | | loadFromHive | bool | Charger depuis Hive au lieu des données fournies | Non | | title | String | Titre du graphique | Non | | showDataLabels | bool | Afficher les étiquettes de valeur | Non | | columnWidth | double | Largeur des colonnes | Non | | columnSpacing | double | Espacement entre les colonnes | Non | | showAllPassages | bool | Afficher tous les passages sans filtrage | Non | \* Soit `passageData` doit être fourni, soit `loadFromHive` doit être `true`. **Format attendu de passageData**: ```dart [ { 'date': '2025-04-01', 'type_passage': 1, 'nb': 5 }, { 'date': '2025-04-01', 'type_passage': 3, 'nb': 2 }, // ... ] ``` **Exemple d'utilisation**: ```dart ActivityChart( loadFromHive: true, periodType: 'Jour', height: 350, daysToShow: 15, userId: userRepository.userId, excludePassageTypes: [2, 6], title: 'Activité des 15 derniers jours', ) ``` ## Autres widgets ### PassagePieChart et PaymentPieChart Graphiques circulaires affichant la répartition des passages et des paiements. **Fichiers**: - `lib/presentation/widgets/charts/passage_pie_chart.dart` - `lib/presentation/widgets/charts/payment_pie_chart.dart` Ces widgets utilisent les mêmes principes que ActivityChart mais avec une visualisation circulaire. **Exemple d'utilisation**: ```dart Row( children: [ Expanded( child: PassagePieChart( loadFromHive: true, title: 'Répartition par type', height: 300, ), ), Expanded( child: PaymentPieChart( loadFromHive: true, title: 'Répartition par règlement', height: 300, ), ), ], ) ``` ## Adaptabilité et responsive design Tous les widgets communs sont conçus pour s'adapter aux différentes tailles d'écran: 1. **Détection de la taille d'écran**: ```dart final size = MediaQuery.of(context).size; final isDesktop = size.width > 900; ``` 2. **Layouts conditionnels**: ```dart isDesktop ? _buildDesktopLayout() : _buildMobileLayout() ``` 3. **Flexibilité des widgets**: - Utilisation fréquente de `Expanded` et `Flexible` - Définition de contraintes minimales et maximales - Ajustement du nombre de colonnes selon l'espace disponible ## Bonnes pratiques implémentées 1. **Gestion des erreurs robuste** - Chaque widget inclut une gestion d'erreurs pour éviter les plantages lors des problèmes de données. 2. **Optimisation des performances** - Évitement des rebuilds inutiles et utilisation d'animations fluides. 3. **Séparation des préoccupations** - Les widgets se concentrent sur l'affichage, laissant la logique métier aux repositories. 4. **Documentation complète** - Chaque widget comporte des commentaires de documentation détaillés. 5. **Paramétrage flexible** - La plupart des widgets peuvent être personnalisés via leurs propriétés. ## Utilisation avec les thèmes Les widgets communs respectent le thème de l'application défini dans `app_theme.dart`. Ils utilisent systématiquement: ```dart final theme = Theme.of(context); // Utilisation des couleurs du thème theme.colorScheme.primary theme.colorScheme.onPrimary // Utilisation des styles de texte du thème theme.textTheme.bodyLarge ``` Cela garantit une apparence cohérente et le support des thèmes clairs et sombres.