Files

162 lines
5.0 KiB
Dart

import 'package:flutter/material.dart';
import '../../../core/i18n/app_locale.dart';
import '../../../core/theme/tokens.dart';
import '../../home/home_screen.dart' show formatPop;
import 'campaign_style.dart';
/// 캠페인 기여률 카드 — 시안 `272:245738`(상세) · `480:14594`(공유) 공용.
///
/// #1a1a1c r12 안쪽 12/16. 라벨 12/500 · 「92%」(ABeeZee 32 + % 20) · 10 두께 막대
/// (바탕 #000 30%, 채움 그라디언트) · 0 / 목표. 채움 끝 위에 누적 POP 말풍선(100 폭)이 뜬다.
class CampaignGauge extends StatelessWidget {
const CampaignGauge({super.key, required this.raised, required this.goal});
final int raised;
final int goal;
@override
Widget build(BuildContext context) {
final ratio = goal == 0 ? 0.0 : (raised / goal).clamp(0.0, 1.0);
final percent = goal == 0 ? 0 : ((raised / goal) * 100).round();
final axis = campaignText(
12,
400,
const Color(0xFFCDCDCD),
height: 1,
spacing: 0,
family: AppFonts.display,
);
return Container(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
decoration: BoxDecoration(
color: AppColors.cosmicSurfaceContainer,
borderRadius: BorderRadius.circular(AppRadius.md),
),
child: LayoutBuilder(
builder: (context, box) {
final fill = box.maxWidth * ratio;
return Stack(
clipBehavior: Clip.none,
children: [
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
context.l10n.campaignGaugeLabel,
style: campaignText(
12,
500,
AppColors.onCosmicVariant,
height: 20 / 12,
spacing: 0,
),
),
Text.rich(
TextSpan(
text: '$percent',
children: [
TextSpan(
text: '%',
style: TextStyle(fontSize: 20, letterSpacing: -0.5),
),
],
),
style: campaignText(
32,
400,
AppColors.onCosmic,
height: 1.4,
family: AppFonts.display,
),
),
const SizedBox(height: 12),
Stack(
children: [
Container(
height: 10,
decoration: BoxDecoration(
color: Colors.black.withValues(alpha: 0.3),
borderRadius: BorderRadius.circular(AppRadius.full),
),
),
Container(
width: fill,
height: 10,
decoration: BoxDecoration(
gradient: campaignGradient,
borderRadius: BorderRadius.circular(AppRadius.full),
),
),
],
),
const SizedBox(height: 12),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text('0', style: axis),
Text(formatPop(goal), style: axis),
],
),
],
),
Positioned(
top: 31.5,
left: (fill - 50).clamp(0.0, box.maxWidth - 100),
child: _Bubble(label: formatPop(raised)),
),
],
);
},
),
);
}
}
/// 누적 POP 말풍선 — 그라디언트 알약 100×32 + 아래 꼬리.
class _Bubble extends StatelessWidget {
const _Bubble({required this.label});
final String label;
@override
Widget build(BuildContext context) => SizedBox(
width: 100,
height: 38,
child: Stack(
alignment: Alignment.topCenter,
children: [
Positioned(
bottom: 0,
child: Transform.rotate(
angle: 0.785398,
child: Container(
width: 8,
height: 8,
color: const Color(0xFF5388CF),
),
),
),
Container(
height: 32,
alignment: Alignment.center,
decoration: BoxDecoration(
gradient: campaignGradient,
borderRadius: BorderRadius.circular(AppRadius.full),
),
child: Text(
label,
style: campaignText(
12,
500,
AppColors.onCosmic,
height: 1,
spacing: 0,
),
),
),
],
),
);
}