Files
geo/docs/widgets_common.md
pierre 1018b86537 feat: Gestion des secteurs et migration v3.0.4+304
- Ajout système complet de gestion des secteurs avec contours géographiques
- Import des contours départementaux depuis GeoJSON
- API REST pour la gestion des secteurs (/api/sectors)
- Service de géolocalisation pour déterminer les secteurs
- Migration base de données avec tables x_departements_contours et sectors_adresses
- Interface Flutter pour visualisation et gestion des secteurs
- Ajout thème sombre dans l'application
- Corrections diverses et optimisations

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-07 11:01:45 +02:00

14 KiB
Executable File

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:

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:

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:

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:

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<Map<String, dynamic>> 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<String, dynamic>)? Callback à la sélection d'un passage Non
onPassageEdit Function(Map<String, dynamic>)? Callback pour éditer un passage Non
onReceiptView Function(Map<String, dynamic>)? 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:

[
  {
    '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:

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<Map<String, dynamic>>? 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:

[
  {
    'date': '2025-04-01',
    'type_passage': 1,
    'nb': 5
  },
  {
    'date': '2025-04-01',
    'type_passage': 3,
    'nb': 2
  },
  // ...
]

Exemple d'utilisation:

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:

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:

    final size = MediaQuery.of(context).size;
    final isDesktop = size.width > 900;
    
  2. Layouts conditionnels:

    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:

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.