Files
LearnIT/lib/features/dashboard/presentation/widgets/dashboard_action_card.dart

309 lines
8.7 KiB
Dart

import 'package:flutter/material.dart';
import '../../../../core/theme/app_theme_extension.dart';
/// Layout variant for dashboard quick-action cards.
enum DashboardActionCardLayout { vertical, horizontal }
/// Reusable action card with flexible height and wrapping subtitles.
class DashboardActionCard extends StatelessWidget {
const DashboardActionCard({
super.key,
required this.title,
required this.subtitle,
required this.icon,
required this.onTap,
this.layout = DashboardActionCardLayout.vertical,
this.minHeight = 150,
this.useGradient = false,
this.badge,
this.iconSize = 24,
this.iconPadding = 10,
this.titleFontSize,
this.subtitleFontSize,
this.padding,
this.leadingIcon,
this.onTapDisabled,
});
final String title;
final String subtitle;
final IconData icon;
final VoidCallback? onTap;
final bool? onTapDisabled;
final DashboardActionCardLayout layout;
final double minHeight;
final bool useGradient;
final String? badge;
final double iconSize;
final double iconPadding;
final double? titleFontSize;
final double? subtitleFontSize;
final EdgeInsetsGeometry? padding;
final Widget? leadingIcon;
@override
Widget build(BuildContext context) {
final cs = Theme.of(context).colorScheme;
final extras = AppThemeExtras.of(context);
final isHorizontal = layout == DashboardActionCardLayout.horizontal;
final effectivePadding =
padding ?? EdgeInsets.all(isHorizontal ? 16 : (useGradient ? 20 : 14));
final decoration = BoxDecoration(
gradient: useGradient
? LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: [
extras.actionCardGradientStart,
extras.actionCardGradientEnd,
],
)
: null,
color: useGradient ? null : cs.surface,
borderRadius: BorderRadius.circular(minHeight <= 110 ? 12 : 16),
border: useGradient
? null
: Border.all(color: cs.outline.withOpacity(0.2), width: 1),
boxShadow: [
BoxShadow(
color: (useGradient ? cs.primary : cs.shadow).withOpacity(
useGradient ? 0.3 : 0.05,
),
blurRadius: useGradient ? 15 : 10,
offset: Offset(0, useGradient ? 8 : 4),
),
],
);
final titleColor = useGradient ? Colors.white : cs.onSurface;
final subtitleColor = useGradient ? Colors.white : cs.onSurfaceVariant;
final iconBgColor = useGradient
? Colors.white.withOpacity(0.2)
: cs.primary.withOpacity(0.1);
final iconColor = useGradient ? Colors.white : cs.primary;
final effectiveMinHeight = minHeight > 0 ? minHeight : null;
return ConstrainedBox(
constraints: effectiveMinHeight != null
? BoxConstraints(minHeight: effectiveMinHeight)
: const BoxConstraints(),
child: Container(
decoration: decoration,
child: Material(
color: Colors.transparent,
child: InkWell(
borderRadius: BorderRadius.circular(minHeight <= 110 ? 12 : 16),
onTap: onTapDisabled == true ? null : onTap,
child: Padding(
padding: effectivePadding,
child: isHorizontal
? _buildHorizontalContent(
context,
titleColor,
subtitleColor,
iconBgColor,
iconColor,
)
: _buildVerticalContent(
context,
titleColor,
subtitleColor,
iconBgColor,
iconColor,
),
),
),
),
),
);
}
Widget _buildHorizontalContent(
BuildContext context,
Color titleColor,
Color subtitleColor,
Color iconBgColor,
Color iconColor,
) {
final cs = Theme.of(context).colorScheme;
return Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
_iconBox(iconBgColor, iconColor),
const SizedBox(width: 16),
Expanded(
child: _titleSubtitleColumn(
titleColor,
subtitleColor,
titleSize: titleFontSize ?? 16,
subtitleSize: subtitleFontSize ?? 13,
),
),
Icon(Icons.arrow_forward_ios, color: cs.primary, size: 16),
],
);
}
Widget _buildVerticalContent(
BuildContext context,
Color titleColor,
Color subtitleColor,
Color iconBgColor,
Color iconColor,
) {
final cs = Theme.of(context).colorScheme;
final isCompact = minHeight <= 130;
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
mainAxisSize: MainAxisSize.min,
children: [
Row(
children: [
leadingIcon ?? _iconBox(iconBgColor, iconColor),
if (badge != null) ...[
const Spacer(),
Container(
padding: EdgeInsets.symmetric(
horizontal: isCompact ? 6 : 10,
vertical: isCompact ? 3 : 4,
),
decoration: BoxDecoration(
color: cs.secondary,
borderRadius: BorderRadius.circular(isCompact ? 10 : 12),
),
child: Text(
badge!,
style: TextStyle(
color: Colors.white,
fontSize: isCompact ? 9 : 10,
fontWeight: FontWeight.bold,
),
),
),
],
],
),
SizedBox(height: isCompact ? 8 : 12),
_titleSubtitleColumn(
titleColor,
subtitleColor,
titleSize: titleFontSize ?? (useGradient ? 18 : 16),
subtitleSize: subtitleFontSize ?? (isCompact ? 11 : 12),
),
],
);
}
Widget _iconBox(Color bgColor, Color iconColor) {
return Container(
padding: EdgeInsets.all(iconPadding),
decoration: BoxDecoration(
color: bgColor,
borderRadius: BorderRadius.circular(10),
),
child: Icon(icon, color: iconColor, size: iconSize),
);
}
Widget _titleSubtitleColumn(
Color titleColor,
Color subtitleColor, {
required double titleSize,
required double subtitleSize,
}) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Text(
title,
style: TextStyle(
color: titleColor,
fontSize: titleSize,
fontWeight: FontWeight.bold,
height: 1.2,
),
),
if (subtitle.isNotEmpty) ...[
SizedBox(height: subtitleSize >= 13 ? 4 : 2),
Text(
subtitle,
style: TextStyle(
color: subtitleColor,
fontSize: subtitleSize,
height: 1.25,
),
),
],
],
);
}
}
/// Surface-styled vertical card (Quiz, Criar Turma, etc.).
class DashboardActionCardSurface extends StatelessWidget {
const DashboardActionCardSurface({
super.key,
required this.title,
required this.subtitle,
required this.icon,
required this.onTap,
this.minHeight = 150,
this.iconColor,
this.leadingWidget,
this.onTapDisabled,
this.titleFontSize = 14,
this.subtitleFontSize = 11,
this.iconSize = 20,
this.padding = const EdgeInsets.all(12),
});
final String title;
final String subtitle;
final IconData icon;
final VoidCallback? onTap;
final bool? onTapDisabled;
final double minHeight;
final Color? iconColor;
final Widget? leadingWidget;
final double titleFontSize;
final double subtitleFontSize;
final double iconSize;
final EdgeInsetsGeometry padding;
@override
Widget build(BuildContext context) {
final cs = Theme.of(context).colorScheme;
final effectiveIconColor = iconColor ?? cs.secondary;
return DashboardActionCard(
title: title,
subtitle: subtitle,
icon: icon,
onTap: onTap,
onTapDisabled: onTapDisabled,
minHeight: minHeight,
useGradient: false,
iconSize: iconSize,
leadingIcon:
leadingWidget ??
Container(
padding: const EdgeInsets.all(10),
decoration: BoxDecoration(
color: effectiveIconColor.withOpacity(0.1),
borderRadius: BorderRadius.circular(10),
),
child: Icon(icon, color: effectiveIconColor, size: iconSize),
),
titleFontSize: titleFontSize,
subtitleFontSize: subtitleFontSize,
padding: padding,
);
}
}