Documentation ajoutée : - ARCHITECTURE.md : Clean Architecture par feature, BLoC pattern - OPTIMISATIONS_PERFORMANCE.md : Cache multi-niveaux, pagination, lazy loading - SECURITE_PRODUCTION.md : FlutterSecureStorage, JWT, HTTPS, ProGuard - CHANGELOG.md : Historique versions - CONTRIBUTING.md : Guide contribution - README.md : Mise à jour (build, env config) Widgets partagés : - file_upload_widget.dart : Upload fichiers (photos/PDFs) Cache : - lib/core/cache/ : Système cache L1/L2 (mémoire/disque) Dependencies : - pubspec.yaml : file_picker 8.1.2, injectable, dio Spec 001 : 27/27 tâches (100%) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
13 KiB
Guide de Contribution - UnionFlow Mobile
Merci de votre intérêt pour contribuer à UnionFlow Mobile! Ce guide vous aidera à démarrer.
Table des Matières
- Code of Conduct
- Comment Contribuer
- Setup Environnement
- Standards de Code
- Architecture
- Workflow Git
- Tests
- Pull Requests
- Code Review
- Bonnes Pratiques
Code of Conduct
En contribuant à ce projet, vous acceptez de respecter notre Code de Conduite:
- 🤝 Respecter tous les contributeurs
- 💬 Communication constructive et professionnelle
- 🎯 Focus sur le projet et ses objectifs
- 🚫 Zéro tolérance pour le harcèlement ou discrimination
Comment Contribuer
Types de Contributions
✅ Bug Fixes - Correction de bugs ✅ Features - Nouvelles fonctionnalités ✅ Documentation - Amélioration de la doc ✅ Tests - Ajout ou amélioration des tests ✅ Performance - Optimisations ✅ Refactoring - Amélioration du code existant
Avant de Commencer
- Vérifier les issues existantes - Peut-être que quelqu'un travaille déjà dessus
- Créer une issue - Si le bug/feature n'existe pas encore
- Discuter - Commenter l'issue pour proposer votre approche
- Assignation - Se faire assigner l'issue avant de commencer
Setup Environnement
Prérequis
| Outil | Version Minimale |
|---|---|
| Flutter | 3.5.3+ |
| Dart | 3.5.3+ |
| Git | Latest |
| Android Studio | Latest (pour Android) |
| Xcode | 14+ (pour iOS, macOS uniquement) |
Installation
# 1. Cloner le repo
git clone https://git.lions.dev/lionsdev/unionflow-mobile-apps.git
cd unionflow-mobile-apps
# 2. Installer les dépendances
flutter pub get
# 3. Générer le code (build_runner)
flutter pub run build_runner build --delete-conflicting-outputs
# 4. Vérifier que tout fonctionne
flutter doctor
flutter analyze
flutter test
Configuration Backend Local
Pour développer en local, vous avez besoin du backend Quarkus:
cd ../unionflow-server-impl-quarkus
mvn clean quarkus:dev
Backend disponible sur: http://localhost:8085
Standards de Code
Linting
Nous utilisons flutter_lints avec configuration stricte.
# Analyser le code
flutter analyze
# Formater le code
flutter format lib/
Conventions de Nommage
Fichiers: snake_case
✅ dashboard_bloc.dart
❌ DashboardBloc.dart
❌ dashboard-bloc.dart
Classes: PascalCase
✅ class DashboardBloc {}
❌ class dashboardBloc {}
❌ class Dashboard_Bloc {}
Variables/Fonctions: camelCase
✅ final userName = 'John';
✅ void getUserData() {}
❌ final user_name = 'John';
❌ void get_user_data() {}
Constantes: lowerCamelCase (pas SCREAMING_CASE en Dart)
✅ static const baseUrl = 'https://api.lions.dev';
❌ static const BASE_URL = 'https://api.lions.dev';
Private members: préfixe _
✅ String _privateVariable;
✅ void _privateMethod() {}
Documentation
Classes publiques:
/// Service pour gérer l'authentification Keycloak.
///
/// Ce service gère le login, logout et refresh token
/// avec stockage sécurisé des credentials.
class KeycloakAuthService {
// ...
}
Méthodes publiques:
/// Authentifie un utilisateur avec username et password.
///
/// Retourne [User] si succès, `null` si échec.
/// Stocke les tokens dans [FlutterSecureStorage].
Future<User?> login(String username, String password) async {
// ...
}
TODO Comments:
// TODO(username): Ajouter support biométrique
// FIXME(username): Bug lors du logout après timeout
// HACK(username): Workaround temporaire, à refactoriser
Architecture
UnionFlow Mobile suit Clean Architecture + BLoC.
Structure d'une Feature
features/
└── my_feature/
├── data/
│ ├── datasources/
│ │ └── my_feature_remote_datasource.dart
│ ├── models/
│ │ ├── my_model.dart
│ │ └── my_model.g.dart
│ └── repositories/
│ └── my_feature_repository_impl.dart
├── domain/
│ ├── entities/
│ │ └── my_entity.dart
│ ├── repositories/
│ │ └── my_feature_repository.dart
│ └── usecases/
│ └── get_my_data.dart
└── presentation/
├── bloc/
│ ├── my_feature_bloc.dart
│ ├── my_feature_event.dart
│ └── my_feature_state.dart
├── pages/
│ └── my_feature_page.dart
└── widgets/
└── my_feature_card.dart
Règles de Dépendance
✅ Presentation peut dépendre de Domain ✅ Data peut dépendre de Domain ❌ Domain ne doit JAMAIS dépendre de Data ou Presentation
Checklist d'une Nouvelle Feature
- Créer les Entities dans
domain/entities/ - Créer le Repository Interface dans
domain/repositories/ - Créer les Use Cases dans
domain/usecases/ - Créer les Models dans
data/models/avec@JsonSerializable - Créer le Data Source dans
data/datasources/ - Implémenter le Repository dans
data/repositories/ - Créer Events dans
presentation/bloc/ - Créer States dans
presentation/bloc/ - Créer le BLoC dans
presentation/bloc/ - Créer les Pages dans
presentation/pages/ - Créer les Widgets dans
presentation/widgets/ - Enregistrer dans DI (
core/di/injection_container.dart) - Ajouter Tests
- Mettre à jour Documentation
Workflow Git
Branches
Branches principales:
main- Production (protégée)develop- Développement (protégée)
Branches de feature:
feature/<issue-number>-<short-description>
Exemples:
feature/123-add-biometric-login
feature/456-fix-dashboard-crash
feature/789-improve-cache-performance
Workflow
# 1. Créer une branche depuis develop
git checkout develop
git pull origin develop
git checkout -b feature/123-add-biometric-login
# 2. Développer et commiter
git add .
git commit -m "feat: add biometric authentication"
# 3. Pousser la branche
git push origin feature/123-add-biometric-login
# 4. Créer une Pull Request vers develop
Commits
Nous suivons Conventional Commits:
Format:
<type>(<scope>): <description>
[optional body]
[optional footer]
Types:
feat- Nouvelle fonctionnalitéfix- Correction de bugdocs- Documentationstyle- Formatage (pas de changement de code)refactor- Refactoring (ni bug ni feature)perf- Optimisation de performancetest- Ajout ou modification de testschore- Maintenance (dépendances, config, etc.)
Exemples:
feat(auth): add biometric login support
fix(dashboard): resolve null pointer on stats refresh
docs(readme): update installation instructions
refactor(cache): simplify cache service implementation
test(members): add unit tests for member repository
perf(dashboard): optimize dashboard loading time
chore(deps): upgrade flutter to 3.5.4
Scope: module concerné (auth, dashboard, members, etc.)
Description:
- Impératif ("add" pas "added" ou "adds")
- Minuscule (pas de majuscule en début)
- Pas de point final
- Max 72 caractères
Tests
Tests Unitaires
Domain Layer:
// test/domain/usecases/get_dashboard_data_test.dart
void main() {
late GetDashboardData useCase;
late MockDashboardRepository mockRepository;
setUp(() {
mockRepository = MockDashboardRepository();
useCase = GetDashboardData(mockRepository);
});
test('should return DashboardEntity when repository call succeeds', () async {
// Arrange
when(mockRepository.getDashboardData())
.thenAnswer((_) async => Right(tDashboardEntity));
// Act
final result = await useCase();
// Assert
expect(result, Right(tDashboardEntity));
verify(mockRepository.getDashboardData());
verifyNoMoreInteractions(mockRepository);
});
}
BLoC Tests:
// test/presentation/bloc/dashboard_bloc_test.dart
void main() {
late DashboardBloc bloc;
late MockGetDashboardData mockGetDashboardData;
setUp(() {
mockGetDashboardData = MockGetDashboardData();
bloc = DashboardBloc(getDashboardData: mockGetDashboardData);
});
blocTest<DashboardBloc, DashboardState>(
'emits [Loading, Loaded] when LoadDashboardData succeeds',
build: () {
when(mockGetDashboardData()).thenAnswer(
(_) async => Right(tDashboard),
);
return bloc;
},
act: (bloc) => bloc.add(LoadDashboardData()),
expect: () => [
DashboardLoading(),
DashboardLoaded(tDashboard),
],
verify: (_) {
verify(mockGetDashboardData()).called(1);
},
);
}
Lancer les Tests
# Tous les tests
flutter test
# Tests avec coverage
flutter test --coverage
# Visualiser coverage
genhtml coverage/lcov.info -o coverage/html
open coverage/html/index.html
Coverage Minimum
Target: 80% de coverage minimum pour les nouvelles features
Pull Requests
Avant de Créer une PR
✅ Code compilé: flutter build apk --debug
✅ Linting: flutter analyze sans erreur
✅ Tests: flutter test 100% pass
✅ Formaté: flutter format lib/
✅ Commits propres: Utiliser Conventional Commits
Template de PR
## Description
Brève description du changement.
## Type de Changement
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [ ] Documentation update
## Issue Liée
Closes #123
## Comment Tester
1. Lancer l'app
2. Naviguer vers Dashboard
3. Vérifier que les stats se chargent
## Checklist
- [ ] Mon code suit les conventions du projet
- [ ] J'ai commenté mon code aux endroits complexes
- [ ] J'ai mis à jour la documentation si nécessaire
- [ ] Mes changements ne génèrent pas de warnings
- [ ] J'ai ajouté des tests qui prouvent mon fix/feature
- [ ] Tous les tests passent localement
- [ ] J'ai vérifié que le code build en release
Taille de PR
Idéal: 200-400 lignes Max acceptable: 800 lignes
Si > 800 lignes, découper en plusieurs PRs.
Code Review
Pour le Reviewer
✅ Architecture - Respecte Clean Architecture + BLoC? ✅ Tests - Coverage suffisant? ✅ Performance - Pas de régressions? ✅ Sécurité - Pas de vulnérabilités? ✅ Lisibilité - Code compréhensible? ✅ Documentation - Suffisamment documenté?
Pour l'Auteur
✅ Répondre rapidement aux commentaires ✅ Argumenter les choix techniques ✅ Ne pas prendre personnellement les critiques ✅ Remercier le reviewer
Approuver une PR
Nécessite 2 approbations minimum:
- 1 approbation d'un Tech Lead
- 1 approbation d'un Peer
Bonnes Pratiques
DRY (Don't Repeat Yourself)
❌ Mauvais:
// Dupliqué dans 5 fichiers
final baseUrl = 'https://api.lions.dev';
✅ Bon:
// core/config/environment.dart
class AppConfig {
static String get apiBaseUrl => _apiBaseUrl;
}
KISS (Keep It Simple, Stupid)
❌ Mauvais (sur-ingénierie):
class AbstractFactoryProviderManagerBuilderFactory {
// 500 lignes de code complexe pour un simple getter
}
✅ Bon:
class CacheService {
T? get<T>(String key) => _prefs.get(key);
}
YAGNI (You Aren't Gonna Need It)
❌ Mauvais (fonctionnalités inutilisées):
class User {
// 50 champs dont 40 jamais utilisés
}
✅ Bon:
class User {
// Seulement les champs nécessaires
final String id;
final String email;
final String name;
}
Const Constructors
✅ Toujours utiliser const quand possible:
const Text('Hello')
const SizedBox(height: 16)
const EdgeInsets.all(8)
const Icon(Icons.home)
Null Safety
✅ Utiliser les opérateurs null-safety:
final name = user?.name ?? 'Unknown';
final length = items?.length ?? 0;
final result = await api.call() ?? defaultValue;
Questions?
Si vous avez des questions:
- Lire la documentation - README, ARCHITECTURE, cette doc
- Chercher dans les issues - Peut-être déjà répondu
- Demander sur Discord - #unionflow-dev channel
- Créer une issue - Si toujours bloqué
Ressources
Merci de contribuer à UnionFlow Mobile! 🎉
Document maintenu par l'équipe UnionFlow Development Team