Files
enaklo-flutter/lib/presentation/pages/news/news_ui.dart
T
efrilmandClaude Opus 5.5 5ba60747a8 EnakNews: halaman berita dan detail berita
- Filter kategori, berita utama, dan daftar artikel terbaru
- Detail artikel dengan sampul, penulis, isi, dan "Baca juga"
- Data contoh 8 artikel; artikel tips mengikuti aturan di docs
- Jumlah pada AppFilterChip kini opsional

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 02:26:43 +07:00

112 lines
3.2 KiB
Dart

import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import '../../../common/theme/theme.dart';
import '../../../sample/news_sample_data.dart';
extension NewsCategoryUi on NewsCategory {
Color get color => switch (this) {
NewsCategory.menu => const Color(0xFFD9642A),
NewsCategory.promo => AppColor.primary,
NewsCategory.event => const Color(0xFF7D63C9),
NewsCategory.info => const Color(0xFF3B7BBF),
NewsCategory.tips => const Color(0xFF2F8A5B),
};
IconData get icon => switch (this) {
NewsCategory.menu => Icons.restaurant_menu_rounded,
NewsCategory.promo => Icons.local_offer_rounded,
NewsCategory.event => Icons.celebration_rounded,
NewsCategory.info => Icons.campaign_rounded,
NewsCategory.tips => Icons.lightbulb_rounded,
};
}
/// "Baru saja" / "5 menit lalu" / "3 jam lalu" / "Kemarin" / "2 hari lalu",
/// lalu tanggal pendek.
String formatNewsTime(DateTime time) {
final diff = DateTime.now().difference(time);
if (diff.inMinutes < 1) return 'Baru saja';
if (diff.inHours < 1) return '${diff.inMinutes} menit lalu';
if (diff.inDays < 1) return '${diff.inHours} jam lalu';
if (diff.inDays == 1) return 'Kemarin';
if (diff.inDays < 7) return '${diff.inDays} hari lalu';
return DateFormat('d MMM yyyy', 'id_ID').format(time);
}
/// "3 jam lalu · 2 menit baca".
String newsMetaLabel(NewsArticle article) =>
'${formatNewsTime(article.publishedAt)} · '
'${article.readMinutes} menit baca';
/// Sampul artikel: foto, atau warna kategori dengan ikon bila tidak ada foto.
class NewsCover extends StatelessWidget {
const NewsCover({super.key, required this.article, this.iconSize = 40});
final NewsArticle article;
final double iconSize;
@override
Widget build(BuildContext context) {
final image = article.coverImage;
if (image != null) {
return Image.asset(
image,
fit: BoxFit.cover,
errorBuilder: (_, _, _) => _buildFallback(),
);
}
return _buildFallback();
}
Widget _buildFallback() {
final color = article.category.color;
return DecoratedBox(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [color.withOpacity(0.75), color],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
),
child: Center(
child: Icon(
article.category.icon,
size: iconSize,
color: AppColor.white.withOpacity(0.9),
),
),
);
}
}
/// Label kategori berwarna; [onImage] untuk di atas foto sampul.
class NewsCategoryPill extends StatelessWidget {
const NewsCategoryPill({
super.key,
required this.category,
this.onImage = false,
});
final NewsCategory category;
final bool onImage;
@override
Widget build(BuildContext context) {
return Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
decoration: BoxDecoration(
color: onImage ? AppColor.white : category.color.withOpacity(0.1),
borderRadius: BorderRadius.circular(20),
),
child: Text(
category.label,
style: AppStyle.xs.copyWith(
color: category.color,
fontWeight: FontWeight.w800,
),
),
);
}
}