- 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>
112 lines
3.2 KiB
Dart
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,
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|