QUEST · CAMPAIGN 화면 — 퀘스트 참여·생성·인증 · 캠페인 목록·상세·후원·랭킹
This commit is contained in:
@@ -0,0 +1,122 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../core/theme/tokens.dart';
|
||||
import '../home/home_screen.dart' show formatPop;
|
||||
import 'quest_data.dart';
|
||||
import '../../core/i18n/app_locale.dart';
|
||||
|
||||
/// 보상 완료 — 화면 대조 원장 F4 · Figma `251:113651`(퀘스트 완료) · `482:15084`(광고 시청 후).
|
||||
///
|
||||
/// 명세 `518:88630` — *보상 완료 아이콘 · 보상 POP 텍스트 · 중앙 정렬*
|
||||
/// 명세 `518:88644` — *클릭 시 내 POP + 보유 POP Update ·*
|
||||
/// *퀘스트 진행 상태값 보상완료 변경 · 퀘스트 상세 화면 이동*
|
||||
/// 명세 `518:88685` — *광고 시청 POP = 보상 POP × 2 or ×3*
|
||||
/// 잔액(명세 `518:88644` *보유 POP Update*)은 시안에 표시 자리가 없어 호출부가 다시 읽는다.
|
||||
///
|
||||
/// **POP 을 여기서 더하지 않는다.** 잔액과 원장은 서버가 쓰며(`fn-quest-attend`
|
||||
/// 등), 이 화면은 결과를 보여 주고 상태 전이를 호출부에 알릴 뿐이다.
|
||||
class QuestRewardScreen extends StatelessWidget {
|
||||
const QuestRewardScreen({
|
||||
super.key,
|
||||
required this.detail,
|
||||
this.reward,
|
||||
this.adBonusPop,
|
||||
});
|
||||
|
||||
final QuestDetail detail;
|
||||
|
||||
/// 서버가 지급한 결과. 있으면 **이 값만 띄운다** — 설정 보상 POP 과 실제 지급액이
|
||||
/// 다를 수 있고(운영값 조정·등급 배수), 화면이 설정값을 띄우면 원장과 어긋난다.
|
||||
final QuestReward? reward;
|
||||
|
||||
/// 광고 추가 보상으로 서버가 지급한 기여 POP. 광고를 안 봤거나 청구가 실패했으면 null.
|
||||
final int? adBonusPop;
|
||||
|
||||
int? get rewardPop => reward?.rewardedPop ?? detail.card.rewardPop;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final t = Theme.of(context).textTheme;
|
||||
return Scaffold(
|
||||
body: SafeArea(
|
||||
// 시안의 본문 구간(위 56 머리 · 아래 72 버튼 자리) 가운데에 놓는다.
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(top: 56),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Center(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
// 120 원 둘레에 그림자 6 을 포함한 132 이미지.
|
||||
Image.asset('assets/figma/quest-done.png', width: 132, height: 132),
|
||||
const SizedBox(height: 18),
|
||||
Text(
|
||||
context.l10n.questRewardDone,
|
||||
style: const TextStyle(
|
||||
fontSize: 15,
|
||||
fontWeight: FontWeight.w600,
|
||||
height: 1.4,
|
||||
color: AppColors.onCosmic,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text.rich(
|
||||
TextSpan(
|
||||
children: [
|
||||
TextSpan(
|
||||
text: rewardPop == null ? 'POP' : '+${formatPop(rewardPop!)}',
|
||||
style: const TextStyle(color: Color(0xFF67D3A9)),
|
||||
),
|
||||
TextSpan(text: context.l10n.questRewardReceived),
|
||||
],
|
||||
),
|
||||
style: const TextStyle(
|
||||
fontSize: 26,
|
||||
fontWeight: FontWeight.w600,
|
||||
height: 1.4,
|
||||
color: AppColors.onCosmic,
|
||||
),
|
||||
),
|
||||
if (adBonusPop != null) ...[
|
||||
const SizedBox(height: AppSpace.s2),
|
||||
Text(
|
||||
context.l10n.questAdBonus(formatPop(adBonusPop!)),
|
||||
style: t.bodySmall,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(16, 0, 16, 16),
|
||||
child: FilledButton(
|
||||
onPressed: () => Navigator.of(context).pop(true),
|
||||
style: FilledButton.styleFrom(
|
||||
minimumSize: const Size.fromHeight(56),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(AppRadius.sm),
|
||||
),
|
||||
backgroundColor: AppColors.cosmicAccent,
|
||||
foregroundColor: Colors.black,
|
||||
textStyle: const TextStyle(
|
||||
fontFamily: AppFonts.body,
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
height: 24 / 16,
|
||||
),
|
||||
),
|
||||
child: Text(context.l10n.commonDone),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user