Figma 설명 프레임 반영 — 로그인·온보딩·스플래시 · HOME · GALAXY · 퀘스트·이클립스·알림

This commit is contained in:
Sn-Kinos
2026-10-06 03:03:20 +09:00
parent 64825e72b0
commit ecf5998725
77 changed files with 2703 additions and 640 deletions
+34 -19
View File
@@ -314,32 +314,42 @@ class _Contribution extends StatelessWidget {
Widget build(BuildContext context) => Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 성운·첫 사용자 상태의 「{닉네임} 님의」는 기여 여부와 무관한 인사 줄이다.
if (growth.hasRecentContribution ||
((!joined || growth.isNebula) &&
growth.contributorName.trim().isNotEmpty)) ...[
// 「{닉네임} 님의」는 로그인 사용자의 인사 줄이다(설명 프레임 `2097:16391`).
if (growth.contributorName.trim().isNotEmpty) ...[
Row(
children: [
// 프로필 사진 자산이 데이터에 없어 이니셜로 그린다.
// 로그인 사용자의 프로필 사진(`2097:16391` 「프로필 - 이미지」). 없으면 이니셜.
Container(
width: 26,
height: 26,
alignment: Alignment.center,
decoration: const BoxDecoration(
clipBehavior: Clip.antiAlias,
decoration: BoxDecoration(
shape: BoxShape.circle,
// 사본 `Ellipse 3` #9BF8A8(프로필 사진이 없을 때의 바탕).
color: Color(0xFF9BF8A8),
boxShadow: [BoxShadow(color: Color(0xB3000000), blurRadius: 4)],
),
child: Text(
growth.contributorName.characters.firstOrNull ?? '—',
style: const TextStyle(
fontFamily: AppFonts.body,
fontSize: 12,
fontWeight: FontWeight.w500,
color: Colors.black,
),
color: const Color(0xFF9BF8A8),
boxShadow: const [
BoxShadow(color: Color(0xB3000000), blurRadius: 4),
],
image: growth.photoUrl == null
? null
: DecorationImage(
image: NetworkImage(growth.photoUrl!),
fit: BoxFit.cover,
onError: (_, _) {},
),
),
child: growth.photoUrl != null
? null
: Text(
growth.contributorName.characters.firstOrNull ?? '—',
style: const TextStyle(
fontFamily: AppFonts.body,
fontSize: 12,
fontWeight: FontWeight.w500,
color: Colors.black,
),
),
),
const SizedBox(width: 9),
Text(
@@ -686,8 +696,10 @@ class _Nebula extends StatelessWidget {
Semantics(
button: ready && onBirth != null,
label: context.l10n.heroNebula,
child: GestureDetector(
onTap: ready ? onBirth : null,
// Figma `2013:15377` 「성운 터치시 흔들림」 — 조건 전에도 흔들림 · 가벼운 햅틱으로 반응하고,
// 탄생 조건을 채웠으면 흔들린 뒤 행성 생성으로 간다.
child: _TapShake(
onTap: ready && onBirth != null ? onBirth : () {},
child: Image.asset(
'assets/figma/nebula-home.png',
width: 212,
@@ -1018,6 +1030,9 @@ class HomeZap extends StatelessWidget {
onItem: onItem,
zapPop: data.zapPop,
myPop: (zap.balance ?? data.myPop) - zap.pending * data.zapPop,
// Figma `516:88102` Zap_버프(아이템 배수 · 지속시간)는 별첨 B B-22 「배수 기능 제거」 ·
// 10/3 통합회신(C-124) 「사용 시 Drop Pool 발생 후 HERO TIME 발동」이 대체한다 —
// 같은 팬덤의 아이템 사용으로 HERO TIME 이 열리면 ZAP 이 Drop POP 참여로 바뀐다.
isHeroTime: hero,
blockedReason: zap.sending
? context.l10n.zapApplying
+4 -19
View File
@@ -2,12 +2,10 @@ import 'dart:math' as math;
import 'package:flutter/material.dart';
import '../../../core/i18n/app_locale.dart';
import '../../../core/theme/tokens.dart';
import '../../../core/ui/app_icons.dart';
import '../../quest/widgets/quest_card_tile.dart'
show QuestIconBox, questImageFor;
import '../../quest/widgets/time_attack_countdown.dart';
import '../home_data.dart';
/// 홈 「TODAY'S QUEST」 묶음 — Figma 「홈」 `2097:16428`: 343×270 · r12 안에 343×90 줄
@@ -45,8 +43,9 @@ class QuestRows extends StatelessWidget {
/// 오른쪽 chevron 24 #4F4F4F.
///
/// 보조 줄은 카테고리(사본 「오늘의 출석체크」 · 「커뮤니티」)다. 「홈」 프레임에는 보상 숫자 ·
/// 진행 상태가 없어 그리지 않는다. 예외는 긴급 타임어택의 남은 시간 하나 — FR-QST-06
/// 「홈 카운트다운」이 요구한다.
/// 진행 상태가 없어 그리지 않는다. 타임어택 남은 시간도 그리지 않는다 — 설명 프레임
/// `2097:16397` 데이터는 「어드민에서 등록된 퀘스트」뿐이고, FR-QST-06 · 원장 C-94 의 카운트다운
/// 표시 위치는 퀘스트 목록 · 상세다.
class QuestRow extends StatelessWidget {
const QuestRow({
super.key,
@@ -65,7 +64,6 @@ class QuestRow extends StatelessWidget {
@override
Widget build(BuildContext context) {
final done = quest.state == QuestState.done;
return Material(
color: AppColors.cosmicSurfaceContainer,
shape: divider
@@ -100,20 +98,7 @@ class QuestRow extends StatelessWidget {
),
),
const SizedBox(height: 4),
// 타임어택은 받기 전까지 남은 시간을 센다(FR-QST-06).
if (quest.endsAt case final endsAt? when !done)
TimeAttackCountdown(
endsAt: endsAt,
builder: (context, left) => _sub(
left == null
? context.l10n.questTimeAttackClosed
: context.l10n.questTimeAttackLeft(
formatLeft(left),
),
),
)
else
_sub(quest.category ?? ''),
_sub(quest.category ?? ''),
],
),
),