197 lines
6.6 KiB
Dart
197 lines
6.6 KiB
Dart
/// Featured News Card Widget
|
|
///
|
|
/// Large featured article card with full-width image.
|
|
/// Used at the top of news list page for the main featured article.
|
|
library;
|
|
|
|
import 'package:cached_network_image/cached_network_image.dart';
|
|
import 'package:worker/core/widgets/loading_indicator.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
|
import 'package:worker/core/constants/ui_constants.dart';
|
|
import 'package:worker/features/news/domain/entities/news_article.dart';
|
|
|
|
/// Featured News Card
|
|
///
|
|
/// Large card with:
|
|
/// - Full-width 200px height image
|
|
/// - Title (1.125rem, bold)
|
|
/// - Excerpt/description (truncated)
|
|
/// - Metadata: date, views, reading time
|
|
/// - Category badge (primary blue)
|
|
/// - Shadow and rounded corners
|
|
class FeaturedNewsCard extends StatelessWidget {
|
|
|
|
/// Constructor
|
|
const FeaturedNewsCard({super.key, required this.article, this.onTap});
|
|
/// News article to display
|
|
final NewsArticle article;
|
|
|
|
/// Callback when card is tapped
|
|
final VoidCallback? onTap;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final colorScheme = Theme.of(context).colorScheme;
|
|
|
|
return GestureDetector(
|
|
onTap: onTap,
|
|
child: Container(
|
|
margin: const EdgeInsets.symmetric(horizontal: AppSpacing.md),
|
|
decoration: BoxDecoration(
|
|
color: colorScheme.surface,
|
|
borderRadius: BorderRadius.circular(AppRadius.xl),
|
|
border: Border.all(color: colorScheme.outlineVariant),
|
|
boxShadow: [
|
|
BoxShadow(
|
|
color: Colors.black.withValues(alpha: 0.08),
|
|
blurRadius: 16,
|
|
offset: const Offset(0, 4),
|
|
),
|
|
],
|
|
),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
// Featured image (200px height)
|
|
ClipRRect(
|
|
borderRadius: const BorderRadius.vertical(
|
|
top: Radius.circular(AppRadius.xl),
|
|
),
|
|
child: CachedNetworkImage(
|
|
imageUrl: article.imageUrl,
|
|
width: double.infinity,
|
|
height: 200,
|
|
fit: BoxFit.cover,
|
|
placeholder: (context, url) => Container(
|
|
height: 200,
|
|
color: colorScheme.surfaceContainerHighest,
|
|
child: const const CustomLoadingIndicator(),
|
|
),
|
|
errorWidget: (context, url, error) => Container(
|
|
height: 200,
|
|
color: colorScheme.surfaceContainerHighest,
|
|
child: FaIcon(
|
|
FontAwesomeIcons.image,
|
|
size: 48,
|
|
color: colorScheme.onSurfaceVariant,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
|
|
// Content section
|
|
Padding(
|
|
padding: const EdgeInsets.all(20),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
// Title
|
|
Text(
|
|
article.title,
|
|
style: TextStyle(
|
|
fontSize: 18,
|
|
fontWeight: FontWeight.w600,
|
|
color: colorScheme.onSurface,
|
|
height: 1.4,
|
|
),
|
|
),
|
|
|
|
const SizedBox(height: 12),
|
|
|
|
// Excerpt
|
|
Text(
|
|
article.excerpt,
|
|
style: TextStyle(
|
|
fontSize: 14,
|
|
color: colorScheme.onSurfaceVariant,
|
|
height: 1.5,
|
|
),
|
|
maxLines: 3,
|
|
overflow: TextOverflow.ellipsis,
|
|
),
|
|
|
|
const SizedBox(height: 16),
|
|
|
|
// Metadata row
|
|
Row(
|
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
children: [
|
|
// Left metadata (date, views, reading time)
|
|
Expanded(
|
|
child: Wrap(
|
|
spacing: 16,
|
|
runSpacing: 4,
|
|
children: [
|
|
// Date
|
|
_buildMetaItem(
|
|
context,
|
|
icon: FontAwesomeIcons.calendar,
|
|
text: article.formattedDate,
|
|
),
|
|
|
|
// // Views
|
|
// _buildMetaItem(
|
|
// context,
|
|
// icon: Icons.visibility,
|
|
// text: '${article.formattedViewCount} lượt xem',
|
|
// ),
|
|
//
|
|
// // Reading time
|
|
// _buildMetaItem(
|
|
// context,
|
|
// icon: Icons.schedule,
|
|
// text: article.readingTimeText,
|
|
// ),
|
|
],
|
|
),
|
|
),
|
|
|
|
// Category badge
|
|
Container(
|
|
padding: const EdgeInsets.symmetric(
|
|
horizontal: 12,
|
|
vertical: 4,
|
|
),
|
|
decoration: BoxDecoration(
|
|
color: colorScheme.primary,
|
|
borderRadius: BorderRadius.circular(16),
|
|
),
|
|
child: Text(
|
|
article.category.displayName,
|
|
style: TextStyle(
|
|
fontSize: 12,
|
|
fontWeight: FontWeight.w500,
|
|
color: colorScheme.onPrimary,
|
|
),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
/// Build metadata item
|
|
Widget _buildMetaItem(BuildContext context, {required IconData icon, required String text}) {
|
|
final colorScheme = Theme.of(context).colorScheme;
|
|
|
|
return Row(
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
FaIcon(icon, size: 12, color: colorScheme.onSurfaceVariant),
|
|
const SizedBox(width: 4),
|
|
Text(
|
|
text,
|
|
style: TextStyle(fontSize: 12, color: colorScheme.onSurfaceVariant),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
}
|