Initialisation du projet geosector complet (web + flutter)
This commit is contained in:
346
docs/widgets_common.md
Normal file
346
docs/widgets_common.md
Normal file
@@ -0,0 +1,346 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user