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>
This commit is contained in:
efrilm
2026-10-01 02:26:43 +07:00
co-authored by Claude Opus 5.5
parent c35d0eedbe
commit 5ba60747a8
11 changed files with 1251 additions and 326 deletions
@@ -0,0 +1,78 @@
import 'package:flutter/material.dart';
import '../../../../common/theme/theme.dart';
import '../../../../sample/news_sample_data.dart';
import '../news_ui.dart';
/// Berita utama: sampul lebar di atas, judul dan ringkasan di bawahnya.
///
/// Teks tidak ditumpuk di atas foto karena sampul outlet sering berupa
/// banner bertulisan.
class NewsHeadlineCard extends StatelessWidget {
const NewsHeadlineCard({
super.key,
required this.article,
required this.onTap,
});
final NewsArticle article;
final VoidCallback onTap;
@override
Widget build(BuildContext context) {
return Material(
color: AppColor.white,
borderRadius: BorderRadius.circular(20),
clipBehavior: Clip.antiAlias,
child: InkWell(
onTap: onTap,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
AspectRatio(
aspectRatio: 16 / 9,
child: NewsCover(article: article, iconSize: 64),
),
Padding(
padding: const EdgeInsets.fromLTRB(16, 14, 16, 16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
NewsCategoryPill(category: article.category),
const SizedBox(height: 8),
Text(
article.title,
maxLines: 3,
overflow: TextOverflow.ellipsis,
style: AppStyle.xl.copyWith(
fontWeight: FontWeight.w800,
height: 1.25,
),
),
const SizedBox(height: 6),
Text(
article.summary,
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: AppStyle.sm.copyWith(
color: AppColor.textSecondary,
height: 1.4,
),
),
const SizedBox(height: 10),
Text(
newsMetaLabel(article),
style: AppStyle.xs.copyWith(
color: AppColor.textSecondary,
fontWeight: FontWeight.w600,
),
),
],
),
),
],
),
),
);
}
}
@@ -0,0 +1,69 @@
import 'package:flutter/material.dart';
import '../../../../common/theme/theme.dart';
import '../../../../sample/news_sample_data.dart';
import '../news_ui.dart';
/// Baris artikel: kategori, judul, dan waktu di kiri, sampul kecil di kanan.
class NewsListTile extends StatelessWidget {
const NewsListTile({super.key, required this.article, required this.onTap});
final NewsArticle article;
final VoidCallback onTap;
static const double _thumbSize = 88;
@override
Widget build(BuildContext context) {
return InkWell(
onTap: onTap,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
article.category.label.toUpperCase(),
style: AppStyle.xs.copyWith(
color: article.category.color,
fontWeight: FontWeight.w800,
letterSpacing: 0.4,
),
),
const SizedBox(height: 4),
Text(
article.title,
maxLines: 3,
overflow: TextOverflow.ellipsis,
style: AppStyle.md.copyWith(
fontWeight: FontWeight.w700,
height: 1.3,
),
),
const SizedBox(height: 6),
Text(
newsMetaLabel(article),
style: AppStyle.xs.copyWith(color: AppColor.textSecondary),
),
],
),
),
const SizedBox(width: 12),
ClipRRect(
borderRadius: BorderRadius.circular(12),
child: SizedBox(
width: _thumbSize,
height: _thumbSize,
child: NewsCover(article: article, iconSize: 32),
),
),
],
),
),
);
}
}