Files
unionflow-mobile-apps/lib/features/dashboard/presentation/widgets/connected/connected_stats_card.dart
dahoud 120434aba0 feat(features): refontes adhesions/admin/auth/backup/contributions/dashboard/epargne/events
- adhesions : bloc complet avec events/states/model, dialogs paiement/rejet
- admin : users bloc, user management list/detail pages
- authentication : bloc + keycloak auth service + webview
- backup : bloc complet, repository, models
- contributions : bloc + widgets + export
- dashboard : widgets connectés (activities, events, notifications, search)
  + charts + monitoring + shortcuts
- epargne : repository, transactions, dialogs
- events : bloc complet, pages (detail, connected, wrapper), models
2026-04-15 20:26:48 +00:00

141 lines
4.2 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../../../../../shared/design_system/unionflow_design_system.dart';
import '../../../../../shared/widgets/core_card.dart';
import '../../bloc/dashboard_bloc.dart';
import '../../../domain/entities/dashboard_entity.dart';
/// Widget de carte de statistiques connecté au backend
class ConnectedStatsCard extends StatelessWidget {
final String title;
final IconData icon;
final String Function(DashboardStatsEntity) valueExtractor;
final String? Function(DashboardStatsEntity)? subtitleExtractor;
final Color? customColor;
final VoidCallback? onTap;
const ConnectedStatsCard({
super.key,
required this.title,
required this.icon,
required this.valueExtractor,
this.subtitleExtractor,
this.customColor,
this.onTap,
});
@override
Widget build(BuildContext context) {
return BlocBuilder<DashboardBloc, DashboardState>(
builder: (context, state) {
if (state is DashboardLoading) {
return _buildLoadingCard();
} else if (state is DashboardLoaded || state is DashboardRefreshing) {
final data = state is DashboardLoaded
? state.dashboardData
: (state as DashboardRefreshing).dashboardData;
return _buildDataCard(data.stats);
} else if (state is DashboardError) {
return _buildErrorCard(state.message);
}
return _buildLoadingCard();
},
);
}
Widget _buildDataCard(DashboardStatsEntity stats) {
final value = valueExtractor(stats);
final subtitle = subtitleExtractor?.call(stats);
final color = customColor ?? AppColors.primary;
return CoreCard(
onTap: onTap,
padding: const EdgeInsets.all(8),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Container(
padding: const EdgeInsets.all(5),
decoration: BoxDecoration(
color: color.withOpacity(0.1),
borderRadius: BorderRadius.circular(6),
),
child: Icon(
icon,
color: color,
size: 14,
),
),
const SizedBox(width: 6),
Expanded(
child: Text(
title.toUpperCase(),
style: AppTypography.subtitleSmall.copyWith(
fontWeight: FontWeight.bold,
fontSize: 9,
letterSpacing: 1.0,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
],
),
const SizedBox(height: 4),
Flexible(
child: Text(
value,
style: AppTypography.headerSmall.copyWith(
color: color,
fontWeight: FontWeight.bold,
fontSize: 16,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
if (subtitle != null)
Flexible(
child: Text(
subtitle,
style: AppTypography.subtitleSmall.copyWith(fontSize: 9),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
],
),
);
}
Widget _buildLoadingCard() {
return const CoreCard(
padding: EdgeInsets.all(10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// On peut utiliser un Shimmer ici si disponible
CircularProgressIndicator(strokeWidth: 2),
],
),
);
}
Widget _buildErrorCard(String message) {
return CoreCard(
padding: const EdgeInsets.all(10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Icon(Icons.error_outline, color: AppColors.error, size: 20),
const SizedBox(height: 8),
Text(message, style: AppTypography.subtitleSmall.copyWith(color: AppColors.error)),
],
),
);
}
}