HOME · GALAXY · 행성·성운 상세 · 팬덤 라운지 · 이클립스 화면
This commit is contained in:
@@ -0,0 +1,129 @@
|
||||
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),
|
||||
],
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user