Appli Flutter se connecte bien à l'API.

This commit is contained in:
DahoudG
2025-09-12 03:15:21 +00:00
parent 8184bc77bb
commit 3df010add7
33 changed files with 3124 additions and 339 deletions

View File

@@ -0,0 +1,244 @@
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:injectable/injectable.dart';
import '../../domain/repositories/membre_repository.dart';
import '../../../../core/errors/failures.dart';
import '../../../../core/models/membre_model.dart';
import 'membres_event.dart';
import 'membres_state.dart';
/// BLoC pour la gestion des membres
@injectable
class MembresBloc extends Bloc<MembresEvent, MembresState> {
final MembreRepository _membreRepository;
MembresBloc(this._membreRepository) : super(const MembresInitial()) {
// Enregistrement des handlers d'événements
on<LoadMembres>(_onLoadMembres);
on<RefreshMembres>(_onRefreshMembres);
on<SearchMembres>(_onSearchMembres);
on<LoadMembreById>(_onLoadMembreById);
on<CreateMembre>(_onCreateMembre);
on<UpdateMembre>(_onUpdateMembre);
on<DeleteMembre>(_onDeleteMembre);
on<LoadMembresStats>(_onLoadMembresStats);
on<ClearMembresError>(_onClearMembresError);
on<ResetMembresState>(_onResetMembresState);
}
/// Handler pour charger la liste des membres
Future<void> _onLoadMembres(
LoadMembres event,
Emitter<MembresState> emit,
) async {
emit(const MembresLoading());
try {
final membres = await _membreRepository.getMembres();
emit(MembresLoaded(membres: membres));
} catch (e) {
final failure = _mapExceptionToFailure(e);
emit(MembresError(failure: failure));
}
}
/// Handler pour rafraîchir la liste des membres
Future<void> _onRefreshMembres(
RefreshMembres event,
Emitter<MembresState> emit,
) async {
// Conserver les données actuelles pendant le refresh
final currentState = state;
List<MembreModel> currentMembres = [];
if (currentState is MembresLoaded) {
currentMembres = currentState.membres;
emit(MembresRefreshing(currentMembres));
} else {
emit(const MembresLoading());
}
try {
final membres = await _membreRepository.getMembres();
emit(MembresLoaded(membres: membres));
} catch (e) {
final failure = _mapExceptionToFailure(e);
// Si on avait des données, les conserver avec l'erreur
if (currentMembres.isNotEmpty) {
emit(MembresErrorWithData(
failure: failure,
membres: currentMembres,
));
} else {
emit(MembresError(failure: failure));
}
}
}
/// Handler pour rechercher des membres
Future<void> _onSearchMembres(
SearchMembres event,
Emitter<MembresState> emit,
) async {
if (event.query.trim().isEmpty) {
// Si la recherche est vide, recharger tous les membres
add(const LoadMembres());
return;
}
emit(const MembresLoading());
try {
final membres = await _membreRepository.searchMembres(event.query);
emit(MembresLoaded(
membres: membres,
isSearchResult: true,
searchQuery: event.query,
));
} catch (e) {
final failure = _mapExceptionToFailure(e);
emit(MembresError(failure: failure));
}
}
/// Handler pour charger un membre par ID
Future<void> _onLoadMembreById(
LoadMembreById event,
Emitter<MembresState> emit,
) async {
emit(const MembresLoading());
try {
final membre = await _membreRepository.getMembreById(event.id);
emit(MembreDetailLoaded(membre));
} catch (e) {
final failure = _mapExceptionToFailure(e);
emit(MembresError(failure: failure));
}
}
/// Handler pour créer un membre
Future<void> _onCreateMembre(
CreateMembre event,
Emitter<MembresState> emit,
) async {
emit(const MembresLoading());
try {
final nouveauMembre = await _membreRepository.createMembre(event.membre);
emit(MembreCreated(nouveauMembre));
// Recharger la liste après création
add(const LoadMembres());
} catch (e) {
final failure = _mapExceptionToFailure(e);
emit(MembresError(failure: failure));
}
}
/// Handler pour mettre à jour un membre
Future<void> _onUpdateMembre(
UpdateMembre event,
Emitter<MembresState> emit,
) async {
emit(const MembresLoading());
try {
final membreMisAJour = await _membreRepository.updateMembre(
event.id,
event.membre,
);
emit(MembreUpdated(membreMisAJour));
// Recharger la liste après mise à jour
add(const LoadMembres());
} catch (e) {
final failure = _mapExceptionToFailure(e);
emit(MembresError(failure: failure));
}
}
/// Handler pour supprimer un membre
Future<void> _onDeleteMembre(
DeleteMembre event,
Emitter<MembresState> emit,
) async {
emit(const MembresLoading());
try {
await _membreRepository.deleteMembre(event.id);
emit(MembreDeleted(event.id));
// Recharger la liste après suppression
add(const LoadMembres());
} catch (e) {
final failure = _mapExceptionToFailure(e);
emit(MembresError(failure: failure));
}
}
/// Handler pour charger les statistiques
Future<void> _onLoadMembresStats(
LoadMembresStats event,
Emitter<MembresState> emit,
) async {
emit(const MembresLoading());
try {
final stats = await _membreRepository.getMembresStats();
emit(MembresStatsLoaded(stats));
} catch (e) {
final failure = _mapExceptionToFailure(e);
emit(MembresError(failure: failure));
}
}
/// Handler pour effacer les erreurs
void _onClearMembresError(
ClearMembresError event,
Emitter<MembresState> emit,
) {
final currentState = state;
if (currentState is MembresError && currentState.previousState != null) {
emit(currentState.previousState!);
} else if (currentState is MembresErrorWithData) {
emit(MembresLoaded(
membres: currentState.membres,
isSearchResult: currentState.isSearchResult,
searchQuery: currentState.searchQuery,
));
} else {
emit(const MembresInitial());
}
}
/// Handler pour réinitialiser l'état
void _onResetMembresState(
ResetMembresState event,
Emitter<MembresState> emit,
) {
emit(const MembresInitial());
}
/// Convertit une exception en Failure approprié
Failure _mapExceptionToFailure(dynamic exception) {
if (exception is Failure) {
return exception;
}
final message = exception.toString();
if (message.contains('connexion') || message.contains('network')) {
return NetworkFailure(message: message);
} else if (message.contains('401') || message.contains('unauthorized')) {
return const AuthFailure(message: 'Session expirée. Veuillez vous reconnecter.');
} else if (message.contains('400') || message.contains('validation')) {
return ValidationFailure(message: message);
} else if (message.contains('500') || message.contains('server')) {
return ServerFailure(message: message);
}
return ServerFailure(message: message);
}
}

View File

@@ -0,0 +1,86 @@
import 'package:equatable/equatable.dart';
import '../../../../core/models/membre_model.dart';
/// Événements pour le BLoC des membres
abstract class MembresEvent extends Equatable {
const MembresEvent();
@override
List<Object?> get props => [];
}
/// Événement pour charger la liste des membres
class LoadMembres extends MembresEvent {
const LoadMembres();
}
/// Événement pour rafraîchir la liste des membres
class RefreshMembres extends MembresEvent {
const RefreshMembres();
}
/// Événement pour rechercher des membres
class SearchMembres extends MembresEvent {
const SearchMembres(this.query);
final String query;
@override
List<Object?> get props => [query];
}
/// Événement pour charger un membre spécifique
class LoadMembreById extends MembresEvent {
const LoadMembreById(this.id);
final String id;
@override
List<Object?> get props => [id];
}
/// Événement pour créer un nouveau membre
class CreateMembre extends MembresEvent {
const CreateMembre(this.membre);
final MembreModel membre;
@override
List<Object?> get props => [membre];
}
/// Événement pour mettre à jour un membre
class UpdateMembre extends MembresEvent {
const UpdateMembre(this.id, this.membre);
final String id;
final MembreModel membre;
@override
List<Object?> get props => [id, membre];
}
/// Événement pour supprimer un membre
class DeleteMembre extends MembresEvent {
const DeleteMembre(this.id);
final String id;
@override
List<Object?> get props => [id];
}
/// Événement pour charger les statistiques des membres
class LoadMembresStats extends MembresEvent {
const LoadMembresStats();
}
/// Événement pour effacer les erreurs
class ClearMembresError extends MembresEvent {
const ClearMembresError();
}
/// Événement pour réinitialiser l'état
class ResetMembresState extends MembresEvent {
const ResetMembresState();
}

View File

@@ -0,0 +1,163 @@
import 'package:equatable/equatable.dart';
import '../../../../core/models/membre_model.dart';
import '../../../../core/errors/failures.dart';
/// États pour le BLoC des membres
abstract class MembresState extends Equatable {
const MembresState();
@override
List<Object?> get props => [];
}
/// État initial
class MembresInitial extends MembresState {
const MembresInitial();
}
/// État de chargement
class MembresLoading extends MembresState {
const MembresLoading();
}
/// État de chargement avec données existantes (pour le refresh)
class MembresRefreshing extends MembresState {
const MembresRefreshing(this.currentMembres);
final List<MembreModel> currentMembres;
@override
List<Object?> get props => [currentMembres];
}
/// État de succès avec liste des membres
class MembresLoaded extends MembresState {
const MembresLoaded({
required this.membres,
this.isSearchResult = false,
this.searchQuery,
});
final List<MembreModel> membres;
final bool isSearchResult;
final String? searchQuery;
@override
List<Object?> get props => [membres, isSearchResult, searchQuery];
/// Copie avec modifications
MembresLoaded copyWith({
List<MembreModel>? membres,
bool? isSearchResult,
String? searchQuery,
}) {
return MembresLoaded(
membres: membres ?? this.membres,
isSearchResult: isSearchResult ?? this.isSearchResult,
searchQuery: searchQuery ?? this.searchQuery,
);
}
}
/// État de succès pour un membre spécifique
class MembreDetailLoaded extends MembresState {
const MembreDetailLoaded(this.membre);
final MembreModel membre;
@override
List<Object?> get props => [membre];
}
/// État de succès pour les statistiques
class MembresStatsLoaded extends MembresState {
const MembresStatsLoaded(this.stats);
final Map<String, dynamic> stats;
@override
List<Object?> get props => [stats];
}
/// État de succès pour la création d'un membre
class MembreCreated extends MembresState {
const MembreCreated(this.membre);
final MembreModel membre;
@override
List<Object?> get props => [membre];
}
/// État de succès pour la mise à jour d'un membre
class MembreUpdated extends MembresState {
const MembreUpdated(this.membre);
final MembreModel membre;
@override
List<Object?> get props => [membre];
}
/// État de succès pour la suppression d'un membre
class MembreDeleted extends MembresState {
const MembreDeleted(this.membreId);
final String membreId;
@override
List<Object?> get props => [membreId];
}
/// État d'erreur
class MembresError extends MembresState {
const MembresError({
required this.failure,
this.previousState,
});
final Failure failure;
final MembresState? previousState;
@override
List<Object?> get props => [failure, previousState];
/// Message d'erreur formaté
String get message => failure.message;
/// Code d'erreur
String? get code => failure.code;
/// Indique si c'est une erreur réseau
bool get isNetworkError => failure is NetworkFailure;
/// Indique si c'est une erreur serveur
bool get isServerError => failure is ServerFailure;
/// Indique si c'est une erreur d'authentification
bool get isAuthError => failure is AuthFailure;
/// Indique si c'est une erreur de validation
bool get isValidationError => failure is ValidationFailure;
}
/// État d'erreur avec données existantes (pour les erreurs non critiques)
class MembresErrorWithData extends MembresState {
const MembresErrorWithData({
required this.failure,
required this.membres,
this.isSearchResult = false,
this.searchQuery,
});
final Failure failure;
final List<MembreModel> membres;
final bool isSearchResult;
final String? searchQuery;
@override
List<Object?> get props => [failure, membres, isSearchResult, searchQuery];
/// Message d'erreur formaté
String get message => failure.message;
}