461 lines
12 KiB
Dart
461 lines
12 KiB
Dart
import 'package:flutter/material.dart';
|
|
|
|
/// Widget réutilisable pour afficher un élément d'activité
|
|
///
|
|
/// Composant standardisé pour les listes d'activités récentes,
|
|
/// notifications, historiques, etc.
|
|
class ActivityItem extends StatelessWidget {
|
|
/// Titre principal de l'activité
|
|
final String title;
|
|
|
|
/// Description ou détails de l'activité
|
|
final String? description;
|
|
|
|
/// Horodatage de l'activité
|
|
final String timestamp;
|
|
|
|
/// Icône représentative de l'activité
|
|
final IconData? icon;
|
|
|
|
/// Couleur thématique de l'activité
|
|
final Color? color;
|
|
|
|
/// Type d'activité pour le style automatique
|
|
final ActivityType? type;
|
|
|
|
/// Callback lors du tap sur l'élément
|
|
final VoidCallback? onTap;
|
|
|
|
/// Style de l'élément d'activité
|
|
final ActivityItemStyle style;
|
|
|
|
/// Afficher ou non l'indicateur de statut
|
|
final bool showStatusIndicator;
|
|
|
|
const ActivityItem({
|
|
super.key,
|
|
required this.title,
|
|
this.description,
|
|
required this.timestamp,
|
|
this.icon,
|
|
this.color,
|
|
this.type,
|
|
this.onTap,
|
|
this.style = ActivityItemStyle.normal,
|
|
this.showStatusIndicator = true,
|
|
});
|
|
|
|
/// Constructeur pour une activité système
|
|
const ActivityItem.system({
|
|
super.key,
|
|
required this.title,
|
|
this.description,
|
|
required this.timestamp,
|
|
this.onTap,
|
|
}) : icon = Icons.settings,
|
|
color = const Color(0xFF6C5CE7),
|
|
type = ActivityType.system,
|
|
style = ActivityItemStyle.normal,
|
|
showStatusIndicator = true;
|
|
|
|
/// Constructeur pour une activité utilisateur
|
|
const ActivityItem.user({
|
|
super.key,
|
|
required this.title,
|
|
this.description,
|
|
required this.timestamp,
|
|
this.onTap,
|
|
}) : icon = Icons.person,
|
|
color = const Color(0xFF00B894),
|
|
type = ActivityType.user,
|
|
style = ActivityItemStyle.normal,
|
|
showStatusIndicator = true;
|
|
|
|
/// Constructeur pour une alerte
|
|
const ActivityItem.alert({
|
|
super.key,
|
|
required this.title,
|
|
this.description,
|
|
required this.timestamp,
|
|
this.onTap,
|
|
}) : icon = Icons.warning,
|
|
color = Colors.orange,
|
|
type = ActivityType.alert,
|
|
style = ActivityItemStyle.alert,
|
|
showStatusIndicator = true;
|
|
|
|
/// Constructeur pour une erreur
|
|
const ActivityItem.error({
|
|
super.key,
|
|
required this.title,
|
|
this.description,
|
|
required this.timestamp,
|
|
this.onTap,
|
|
}) : icon = Icons.error,
|
|
color = Colors.red,
|
|
type = ActivityType.error,
|
|
style = ActivityItemStyle.alert,
|
|
showStatusIndicator = true;
|
|
|
|
/// Constructeur pour une activité de succès
|
|
const ActivityItem.success({
|
|
super.key,
|
|
required this.title,
|
|
this.description,
|
|
required this.timestamp,
|
|
this.onTap,
|
|
}) : icon = Icons.check_circle,
|
|
color = const Color(0xFF00B894),
|
|
type = ActivityType.success,
|
|
style = ActivityItemStyle.normal,
|
|
showStatusIndicator = true;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final effectiveColor = _getEffectiveColor();
|
|
final effectiveIcon = _getEffectiveIcon();
|
|
|
|
return GestureDetector(
|
|
onTap: onTap,
|
|
child: Container(
|
|
margin: const EdgeInsets.only(bottom: 8),
|
|
padding: _getPadding(),
|
|
decoration: _getDecoration(effectiveColor),
|
|
child: _buildContent(effectiveColor, effectiveIcon),
|
|
),
|
|
);
|
|
}
|
|
|
|
/// Contenu principal de l'élément
|
|
Widget _buildContent(Color effectiveColor, IconData effectiveIcon) {
|
|
switch (style) {
|
|
case ActivityItemStyle.minimal:
|
|
return _buildMinimalContent(effectiveColor, effectiveIcon);
|
|
case ActivityItemStyle.normal:
|
|
return _buildNormalContent(effectiveColor, effectiveIcon);
|
|
case ActivityItemStyle.detailed:
|
|
return _buildDetailedContent(effectiveColor, effectiveIcon);
|
|
case ActivityItemStyle.alert:
|
|
return _buildAlertContent(effectiveColor, effectiveIcon);
|
|
}
|
|
}
|
|
|
|
/// Contenu minimal (ligne simple)
|
|
Widget _buildMinimalContent(Color effectiveColor, IconData effectiveIcon) {
|
|
return Row(
|
|
children: [
|
|
if (showStatusIndicator)
|
|
Container(
|
|
width: 8,
|
|
height: 8,
|
|
decoration: BoxDecoration(
|
|
color: effectiveColor,
|
|
shape: BoxShape.circle,
|
|
),
|
|
),
|
|
if (showStatusIndicator) const SizedBox(width: 8),
|
|
Expanded(
|
|
child: Text(
|
|
title,
|
|
style: const TextStyle(
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.w500,
|
|
),
|
|
),
|
|
),
|
|
Text(
|
|
timestamp,
|
|
style: const TextStyle(
|
|
color: Colors.grey,
|
|
fontSize: 10,
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
/// Contenu normal avec icône
|
|
Widget _buildNormalContent(Color effectiveColor, IconData effectiveIcon) {
|
|
return Row(
|
|
children: [
|
|
if (showStatusIndicator) ...[
|
|
Container(
|
|
padding: const EdgeInsets.all(6),
|
|
decoration: BoxDecoration(
|
|
color: effectiveColor.withOpacity(0.1),
|
|
shape: BoxShape.circle,
|
|
),
|
|
child: Icon(
|
|
effectiveIcon,
|
|
color: effectiveColor,
|
|
size: 16,
|
|
),
|
|
),
|
|
const SizedBox(width: 12),
|
|
],
|
|
Expanded(
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
title,
|
|
style: const TextStyle(
|
|
fontSize: 14,
|
|
fontWeight: FontWeight.w600,
|
|
color: Color(0xFF1F2937),
|
|
),
|
|
),
|
|
if (description != null) ...[
|
|
const SizedBox(height: 2),
|
|
Text(
|
|
description!,
|
|
style: TextStyle(
|
|
fontSize: 12,
|
|
color: Colors.grey[600],
|
|
),
|
|
),
|
|
],
|
|
],
|
|
),
|
|
),
|
|
const SizedBox(width: 8),
|
|
Text(
|
|
timestamp,
|
|
style: TextStyle(
|
|
color: Colors.grey[500],
|
|
fontSize: 11,
|
|
fontWeight: FontWeight.w500,
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
/// Contenu détaillé avec plus d'informations
|
|
Widget _buildDetailedContent(Color effectiveColor, IconData effectiveIcon) {
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Row(
|
|
children: [
|
|
Container(
|
|
padding: const EdgeInsets.all(8),
|
|
decoration: BoxDecoration(
|
|
color: effectiveColor.withOpacity(0.1),
|
|
borderRadius: BorderRadius.circular(8),
|
|
),
|
|
child: Icon(
|
|
effectiveIcon,
|
|
color: effectiveColor,
|
|
size: 18,
|
|
),
|
|
),
|
|
const SizedBox(width: 12),
|
|
Expanded(
|
|
child: Text(
|
|
title,
|
|
style: const TextStyle(
|
|
fontSize: 16,
|
|
fontWeight: FontWeight.w600,
|
|
color: Color(0xFF1F2937),
|
|
),
|
|
),
|
|
),
|
|
Text(
|
|
timestamp,
|
|
style: TextStyle(
|
|
color: Colors.grey[500],
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.w500,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
if (description != null) ...[
|
|
const SizedBox(height: 8),
|
|
Padding(
|
|
padding: const EdgeInsets.only(left: 42),
|
|
child: Text(
|
|
description!,
|
|
style: TextStyle(
|
|
fontSize: 14,
|
|
color: Colors.grey[700],
|
|
height: 1.4,
|
|
),
|
|
),
|
|
),
|
|
],
|
|
],
|
|
);
|
|
}
|
|
|
|
/// Contenu pour les alertes avec style spécial
|
|
Widget _buildAlertContent(Color effectiveColor, IconData effectiveIcon) {
|
|
return Row(
|
|
children: [
|
|
Icon(
|
|
effectiveIcon,
|
|
color: effectiveColor,
|
|
size: 18,
|
|
),
|
|
const SizedBox(width: 12),
|
|
Expanded(
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Text(
|
|
title,
|
|
style: TextStyle(
|
|
fontSize: 14,
|
|
fontWeight: FontWeight.w600,
|
|
color: effectiveColor,
|
|
),
|
|
),
|
|
if (description != null) ...[
|
|
const SizedBox(height: 2),
|
|
Text(
|
|
description!,
|
|
style: TextStyle(
|
|
fontSize: 12,
|
|
color: Colors.grey[600],
|
|
),
|
|
),
|
|
],
|
|
],
|
|
),
|
|
),
|
|
const SizedBox(width: 8),
|
|
Text(
|
|
timestamp,
|
|
style: TextStyle(
|
|
color: Colors.grey[500],
|
|
fontSize: 11,
|
|
),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
/// Couleur effective selon le type
|
|
Color _getEffectiveColor() {
|
|
if (color != null) return color!;
|
|
|
|
switch (type) {
|
|
case ActivityType.system:
|
|
return const Color(0xFF6C5CE7);
|
|
case ActivityType.user:
|
|
return const Color(0xFF00B894);
|
|
case ActivityType.organization:
|
|
return const Color(0xFF0984E3);
|
|
case ActivityType.event:
|
|
return const Color(0xFFE17055);
|
|
case ActivityType.alert:
|
|
return Colors.orange;
|
|
case ActivityType.error:
|
|
return Colors.red;
|
|
case ActivityType.success:
|
|
return const Color(0xFF00B894);
|
|
case null:
|
|
return const Color(0xFF6C5CE7);
|
|
}
|
|
}
|
|
|
|
/// Icône effective selon le type
|
|
IconData _getEffectiveIcon() {
|
|
if (icon != null) return icon!;
|
|
|
|
switch (type) {
|
|
case ActivityType.system:
|
|
return Icons.settings;
|
|
case ActivityType.user:
|
|
return Icons.person;
|
|
case ActivityType.organization:
|
|
return Icons.business;
|
|
case ActivityType.event:
|
|
return Icons.event;
|
|
case ActivityType.alert:
|
|
return Icons.warning;
|
|
case ActivityType.error:
|
|
return Icons.error;
|
|
case ActivityType.success:
|
|
return Icons.check_circle;
|
|
case null:
|
|
return Icons.circle;
|
|
}
|
|
}
|
|
|
|
/// Padding selon le style
|
|
EdgeInsets _getPadding() {
|
|
switch (style) {
|
|
case ActivityItemStyle.minimal:
|
|
return const EdgeInsets.symmetric(vertical: 4, horizontal: 8);
|
|
case ActivityItemStyle.normal:
|
|
return const EdgeInsets.all(8);
|
|
case ActivityItemStyle.detailed:
|
|
return const EdgeInsets.all(12);
|
|
case ActivityItemStyle.alert:
|
|
return const EdgeInsets.all(10);
|
|
}
|
|
}
|
|
|
|
/// Décoration selon le style
|
|
BoxDecoration _getDecoration(Color effectiveColor) {
|
|
switch (style) {
|
|
case ActivityItemStyle.minimal:
|
|
return const BoxDecoration();
|
|
case ActivityItemStyle.normal:
|
|
return BoxDecoration(
|
|
color: Colors.white,
|
|
borderRadius: BorderRadius.circular(8),
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: Colors.black.withOpacity(0.02),
|
|
blurRadius: 4,
|
|
offset: const Offset(0, 1),
|
|
),
|
|
],
|
|
);
|
|
case ActivityItemStyle.detailed:
|
|
return BoxDecoration(
|
|
color: Colors.white,
|
|
borderRadius: BorderRadius.circular(12),
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: Colors.black.withOpacity(0.05),
|
|
blurRadius: 8,
|
|
offset: const Offset(0, 2),
|
|
),
|
|
],
|
|
);
|
|
case ActivityItemStyle.alert:
|
|
return BoxDecoration(
|
|
color: effectiveColor.withOpacity(0.05),
|
|
borderRadius: BorderRadius.circular(8),
|
|
border: Border.all(
|
|
color: effectiveColor.withOpacity(0.2),
|
|
width: 1,
|
|
),
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Types d'activité
|
|
enum ActivityType {
|
|
system,
|
|
user,
|
|
organization,
|
|
event,
|
|
alert,
|
|
error,
|
|
success,
|
|
}
|
|
|
|
/// Styles d'élément d'activité
|
|
enum ActivityItemStyle {
|
|
minimal,
|
|
normal,
|
|
detailed,
|
|
alert,
|
|
}
|