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

347 lines
14 KiB
Markdown
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**:
```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<Widget>?| 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<Marker>? | Liste des marqueurs à afficher | Non |
| polygons | List<Polygon>? | 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<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**:
```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<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<int> | 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.