Files
unionflow-server-api/unionflow-mobile-apps/lib/shared/widgets/sections/unified_quick_actions_section.dart
2025-09-17 17:54:06 +00:00

263 lines
6.9 KiB
Dart

import 'package:flutter/material.dart';
import '../../theme/app_theme.dart';
import '../cards/unified_card_widget.dart';
/// Section d'actions rapides unifiée
///
/// Fournit :
/// - Grille d'actions avec icônes
/// - Animations au tap
/// - Layouts adaptatifs
/// - Badges de notification
class UnifiedQuickActionsSection extends StatelessWidget {
/// Liste des actions rapides
final List<UnifiedQuickAction> actions;
/// Titre de la section
final String? title;
/// Nombre de colonnes dans la grille (par défaut : 3)
final int crossAxisCount;
/// Espacement entre les actions
final double spacing;
/// Callback lors du tap sur une action
final void Function(UnifiedQuickAction action)? onActionTap;
const UnifiedQuickActionsSection({
super.key,
required this.actions,
this.title,
this.crossAxisCount = 3,
this.spacing = 12.0,
this.onActionTap,
});
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (title != null) ...[
Text(
title!,
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.w600,
color: AppTheme.textPrimary,
),
),
const SizedBox(height: 16),
],
_buildActionsGrid(),
],
);
}
Widget _buildActionsGrid() {
return GridView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: crossAxisCount,
crossAxisSpacing: spacing,
mainAxisSpacing: spacing,
childAspectRatio: 1.0,
),
itemCount: actions.length,
itemBuilder: (context, index) {
final action = actions[index];
return _buildActionCard(action);
},
);
}
Widget _buildActionCard(UnifiedQuickAction action) {
return UnifiedCard(
onTap: action.enabled && onActionTap != null
? () => onActionTap!(action)
: null,
variant: UnifiedCardVariant.outlined,
padding: const EdgeInsets.all(12),
child: Stack(
children: [
// Contenu principal
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// Icône avec conteneur coloré
Container(
width: 48,
height: 48,
decoration: BoxDecoration(
color: action.enabled
? action.color.withOpacity(0.1)
: AppTheme.surfaceVariant.withOpacity(0.5),
borderRadius: BorderRadius.circular(12),
),
child: Icon(
action.icon,
color: action.enabled
? action.color
: AppTheme.textSecondary.withOpacity(0.5),
size: 24,
),
),
const SizedBox(height: 8),
// Titre de l'action
Text(
action.title,
style: TextStyle(
fontSize: 12,
fontWeight: FontWeight.w500,
color: action.enabled
? AppTheme.textPrimary
: AppTheme.textSecondary.withOpacity(0.5),
),
textAlign: TextAlign.center,
maxLines: 2,
overflow: TextOverflow.ellipsis,
),
],
),
// Badge de notification
if (action.badgeCount != null && action.badgeCount! > 0)
Positioned(
top: 0,
right: 0,
child: _buildBadge(action.badgeCount!),
),
// Indicateur "nouveau"
if (action.isNew)
Positioned(
top: 4,
right: 4,
child: Container(
width: 8,
height: 8,
decoration: const BoxDecoration(
color: AppTheme.accentColor,
shape: BoxShape.circle,
),
),
),
],
),
);
}
Widget _buildBadge(int count) {
final displayCount = count > 99 ? '99+' : count.toString();
return Container(
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
decoration: BoxDecoration(
color: AppTheme.errorColor,
borderRadius: BorderRadius.circular(10),
border: Border.all(color: Colors.white, width: 2),
),
constraints: const BoxConstraints(
minWidth: 20,
minHeight: 20,
),
child: Text(
displayCount,
style: const TextStyle(
color: Colors.white,
fontSize: 10,
fontWeight: FontWeight.w600,
),
textAlign: TextAlign.center,
),
);
}
}
/// Données pour une action rapide unifiée
class UnifiedQuickAction {
/// Identifiant unique de l'action
final String id;
/// Titre de l'action
final String title;
/// Icône représentative
final IconData icon;
/// Couleur thématique
final Color color;
/// Indique si l'action est activée
final bool enabled;
/// Nombre de notifications/badges (optionnel)
final int? badgeCount;
/// Indique si l'action est nouvelle
final bool isNew;
/// Données supplémentaires pour les callbacks
final Map<String, dynamic>? metadata;
const UnifiedQuickAction({
required this.id,
required this.title,
required this.icon,
required this.color,
this.enabled = true,
this.badgeCount,
this.isNew = false,
this.metadata,
});
}
/// Actions rapides prédéfinies communes
class CommonQuickActions {
static const UnifiedQuickAction addMember = UnifiedQuickAction(
id: 'add_member',
title: 'Ajouter\nMembre',
icon: Icons.person_add,
color: AppTheme.primaryColor,
);
static const UnifiedQuickAction addEvent = UnifiedQuickAction(
id: 'add_event',
title: 'Nouvel\nÉvénement',
icon: Icons.event_available,
color: AppTheme.accentColor,
);
static const UnifiedQuickAction collectPayment = UnifiedQuickAction(
id: 'collect_payment',
title: 'Collecter\nCotisation',
icon: Icons.payment,
color: AppTheme.successColor,
);
static const UnifiedQuickAction sendMessage = UnifiedQuickAction(
id: 'send_message',
title: 'Envoyer\nMessage',
icon: Icons.message,
color: AppTheme.infoColor,
);
static const UnifiedQuickAction generateReport = UnifiedQuickAction(
id: 'generate_report',
title: 'Générer\nRapport',
icon: Icons.assessment,
color: AppTheme.warningColor,
);
static const UnifiedQuickAction manageSettings = UnifiedQuickAction(
id: 'manage_settings',
title: 'Paramètres',
icon: Icons.settings,
color: AppTheme.textSecondary,
);
}