Files
fanuzu/app/lib/features/home/widgets/quest_row.dart
T

132 lines
4.4 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import 'dart:math' as math;
import 'package:flutter/material.dart';
import '../../../core/theme/tokens.dart';
import '../../../core/ui/app_icons.dart';
import '../../quest/widgets/quest_card_tile.dart'
show QuestIconBox, questImageFor;
import '../home_data.dart';
/// 홈 「TODAY'S QUEST」 묶음 — Figma 「홈」 `2097:16428`: 343×270 · r12 안에 343×90 줄
/// 세 개를 간격 없이 쌓는다. 앞 두 줄에 `#242424` 1 획(사본 spec) — 줄 사이 구분선으로 보인다.
///
/// 항목은 설명 프레임 `2097:16397` 「관리자 퀘스트 1·2·3 — 최신순 최대 3개 · 클릭 시 해당
/// 퀘스트 상세」. 사용자 생성 퀘스트는 저장소가 거른다.
class QuestRows extends StatelessWidget {
const QuestRows({super.key, required this.quests, required this.onTap});
final List<QuestSummary> quests;
final ValueChanged<QuestSummary> onTap;
/// 시안 줄 수. 나머지는 「더 보기」(퀘스트 목록)에서 본다.
static const maxRows = 3;
@override
Widget build(BuildContext context) => ClipRRect(
borderRadius: BorderRadius.circular(12),
child: Column(
children: [
for (final (i, q) in quests.take(maxRows).indexed)
QuestRow(
quest: q,
divider: i < math.min(quests.length, maxRows) - 1,
onTap: () => onTap(q),
),
],
),
);
}
/// 한 줄 — `2097:16428` 「출석체크 / 오늘의 출석체크」 행: #1A1A1C · 패딩 20 · 왼쪽 50×50
/// #2E2E2E r16 아이콘 상자(42 이미지) · 간격 19 · 제목 18/600 + 보조 14/500(간격 4) ·
/// 오른쪽 chevron 24 #4F4F4F.
///
/// 보조 줄은 카테고리(사본 「오늘의 출석체크」 · 「커뮤니티」)다. 「홈」 프레임에는 보상 숫자 ·
/// 진행 상태가 없어 그리지 않는다. 타임어택 남은 시간도 그리지 않는다 — 설명 프레임
/// `2097:16397` 데이터는 「어드민에서 등록된 퀘스트」뿐이고, FR-QST-06 · 원장 C-94 의 카운트다운
/// 표시 위치는 퀘스트 목록 · 상세다.
class QuestRow extends StatelessWidget {
const QuestRow({
super.key,
required this.quest,
this.onTap,
this.divider = false,
});
final QuestSummary quest;
final VoidCallback? onTap;
/// 아래 변 `#242424` 1 획.
final bool divider;
static const height = 90.0;
@override
Widget build(BuildContext context) {
return Material(
color: AppColors.cosmicSurfaceContainer,
shape: divider
? const Border(bottom: BorderSide(color: Color(0xFF242424)))
: null,
child: InkWell(
onTap: onTap,
child: ConstrainedBox(
// 글자 확대 때는 늘어난다 — 고정하면 잘린다.
constraints: const BoxConstraints(minHeight: height),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 12),
child: Row(
children: [
QuestIconBox(image: questImageFor(quest.type, quest.title)),
const SizedBox(width: 19),
Expanded(
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
quest.title,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: const TextStyle(
fontFamily: AppFonts.body,
fontSize: 18,
fontWeight: FontWeight.w600,
height: 20 / 18,
color: AppColors.onCosmic,
),
),
const SizedBox(height: 4),
_sub(quest.category ?? ''),
],
),
),
const SizedBox(width: AppSpace.s2),
const ImageIcon(
AppIcons.chevronRight,
size: 24,
color: Color(0xFF4F4F4F),
),
],
),
),
),
),
);
}
}
Widget _sub(String text) => Text(
text,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: const TextStyle(
fontFamily: AppFonts.body,
fontSize: 14,
fontWeight: FontWeight.w500,
height: 20 / 14,
color: AppColors.onCosmicVariant,
),
);