130 lines
4.6 KiB
Dart
130 lines
4.6 KiB
Dart
import 'package:flutter/material.dart';
|
||
|
||
import '../../../core/theme/tokens.dart';
|
||
import '../home_data.dart';
|
||
import '../../../core/i18n/app_locale.dart';
|
||
|
||
/// HOT ARCHIVE — 진행 중인 캠페인 한 건.
|
||
///
|
||
/// Figma 「다이브짱 / 92% 달성 / 강남역 초대형 LED 광고 / 48 · 24 · 82」.
|
||
/// 정책 설계서 §P-09 — 실패 시 100% 자동 환불이 이 기능의 검수 기준이다.
|
||
class CampaignCard extends StatelessWidget {
|
||
const CampaignCard({super.key, required this.campaign, this.onTap});
|
||
|
||
final CampaignHighlight campaign;
|
||
final VoidCallback? onTap;
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
final t = Theme.of(context).textTheme;
|
||
return Material(
|
||
color: AppColors.cosmicSurfaceContainer,
|
||
borderRadius: BorderRadius.circular(AppRadius.lg),
|
||
child: InkWell(
|
||
onTap: onTap,
|
||
borderRadius: BorderRadius.circular(AppRadius.lg),
|
||
child: Padding(
|
||
padding: const EdgeInsets.all(AppSpace.s4),
|
||
child: Column(
|
||
crossAxisAlignment: CrossAxisAlignment.start,
|
||
children: [
|
||
// 시안 HOT ARCHIVE 카드(252:159447) — 위에 이미지 342×228.
|
||
ClipRRect(
|
||
borderRadius: BorderRadius.circular(AppRadius.sm),
|
||
child: AspectRatio(
|
||
aspectRatio: 342 / 228,
|
||
child: Image.asset('assets/figma/campaign-sample.png', fit: BoxFit.cover),
|
||
),
|
||
),
|
||
const SizedBox(height: AppSpace.s3),
|
||
Row(
|
||
children: [
|
||
Text(campaign.authorName, style: t.bodySmall),
|
||
const Spacer(),
|
||
Container(
|
||
padding: const EdgeInsets.symmetric(
|
||
horizontal: AppSpace.s2,
|
||
vertical: 2,
|
||
),
|
||
decoration: BoxDecoration(
|
||
color: AppColors.cosmicAccent.withValues(alpha: 0.15),
|
||
borderRadius: BorderRadius.circular(AppRadius.full),
|
||
),
|
||
child: Text(
|
||
context.l10n.campaignAchieved('${campaign.achievedPercent}'),
|
||
style: t.labelSmall?.copyWith(
|
||
color: AppColors.cosmicAccent,
|
||
),
|
||
),
|
||
),
|
||
],
|
||
),
|
||
const SizedBox(height: AppSpace.s3),
|
||
Text(campaign.title, style: t.titleMedium),
|
||
const SizedBox(height: AppSpace.s2),
|
||
Text(
|
||
campaign.description,
|
||
style: t.bodySmall,
|
||
maxLines: 2,
|
||
overflow: TextOverflow.ellipsis,
|
||
),
|
||
const SizedBox(height: AppSpace.s3),
|
||
ClipRRect(
|
||
borderRadius: BorderRadius.circular(AppRadius.full),
|
||
child: LinearProgressIndicator(
|
||
value: (campaign.achievedPercent / 100).clamp(0.0, 1.0),
|
||
minHeight: 6,
|
||
backgroundColor: AppColors.cosmicSurfaceRaised,
|
||
valueColor: const AlwaysStoppedAnimation(
|
||
AppColors.cosmicAccent,
|
||
),
|
||
),
|
||
),
|
||
const SizedBox(height: AppSpace.s3),
|
||
Row(
|
||
children: [
|
||
_Stat(icon: Icons.people_outline, value: campaign.supporters),
|
||
const SizedBox(width: AppSpace.s4),
|
||
_Stat(
|
||
icon: Icons.chat_bubble_outline,
|
||
value: campaign.comments,
|
||
),
|
||
const SizedBox(width: AppSpace.s4),
|
||
_Stat(icon: Icons.favorite_border, value: campaign.likes),
|
||
const Spacer(),
|
||
Text(
|
||
context.l10n.campaignView,
|
||
style: t.bodySmall?.copyWith(color: AppColors.cosmicAccent),
|
||
),
|
||
const Icon(
|
||
Icons.chevron_right,
|
||
size: 16,
|
||
color: AppColors.cosmicAccent,
|
||
),
|
||
],
|
||
),
|
||
],
|
||
),
|
||
),
|
||
),
|
||
);
|
||
}
|
||
}
|
||
|
||
class _Stat extends StatelessWidget {
|
||
const _Stat({required this.icon, required this.value});
|
||
|
||
final IconData icon;
|
||
final int value;
|
||
|
||
@override
|
||
Widget build(BuildContext context) => Row(
|
||
mainAxisSize: MainAxisSize.min,
|
||
children: [
|
||
Icon(icon, size: 14, color: AppColors.onCosmicVariant),
|
||
const SizedBox(width: AppSpace.s1),
|
||
Text('$value', style: Theme.of(context).textTheme.labelSmall),
|
||
],
|
||
);
|
||
}
|