사용자 화면 문구·표본 데이터 · 퀘스트 참여 흐름 · HOME 진입 구획 · 미니맵 · 공통 확인 창 배치 수정

This commit is contained in:
Sn-Kinos
2026-10-05 00:44:39 +09:00
parent c8d29d032b
commit 8a0297eb92
66 changed files with 1812 additions and 959 deletions
+178
View File
@@ -0,0 +1,178 @@
import 'package:flutter/material.dart';
import '../i18n/app_locale.dart';
import '../theme/tokens.dart';
/// 앱 공통 확인 창 — Figma `2066:17799`(행성 생성 확인) · `554:24669`(설정 확인 팝업).
///
/// 카드 312 폭 · #282828 r12 · 안쪽 24/16 · 문구 16/600 가운데 정렬,
/// 그 아래 **취소(흰 테두리) · 실행(분홍 채움)을 가로로 반씩**(48 높이 · 간격 8 · r8).
/// 단일 버튼 알림(Figma `511:86714` · `554:23103`)은 실행 버튼 하나가 폭을 다 쓴다.
///
/// [content] 는 입력 칸이 있는 창(반려 사유 · 인증 제출 등)에서 문구 대신 넣는다.
class AppDialog extends StatelessWidget {
const AppDialog({
super.key,
required this.title,
this.body,
this.content,
this.cancelLabel,
required this.confirmLabel,
});
final String title;
final String? body;
final Widget? content;
/// null 이면 실행 버튼 하나만 둔다.
final String? cancelLabel;
final String confirmLabel;
static const _shape = RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(AppRadius.sm)),
);
static TextStyle _text(double size, FontWeight weight, Color color) =>
TextStyle(
fontFamily: AppFonts.body,
fontSize: size,
fontWeight: weight,
height: 1.5,
letterSpacing: size * -0.025,
color: color,
);
@override
Widget build(BuildContext context) {
Widget button(String label, bool value, {required bool filled}) {
final text = Text(
label,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: _text(
16,
FontWeight.w600,
filled ? AppColors.onCosmicAccent : AppColors.onCosmic,
).copyWith(height: 1),
);
void pop() => Navigator.of(context).pop(value);
return Expanded(
child: filled
? FilledButton(
onPressed: pop,
style: FilledButton.styleFrom(
minimumSize: const Size.fromHeight(48),
padding: const EdgeInsets.symmetric(horizontal: 12),
backgroundColor: AppColors.cosmicAccent,
shape: _shape,
),
child: text,
)
: OutlinedButton(
onPressed: pop,
style: OutlinedButton.styleFrom(
minimumSize: const Size.fromHeight(48),
padding: const EdgeInsets.symmetric(horizontal: 12),
side: const BorderSide(color: AppColors.onCosmic),
shape: _shape,
),
child: text,
),
);
}
return Dialog(
backgroundColor: const Color(0xFF282828), // Figma 2066:17799 fill
insetPadding: const EdgeInsets.symmetric(horizontal: 31.5, vertical: 24),
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(AppRadius.md)),
),
child: Padding(
padding: const EdgeInsets.fromLTRB(16, 24, 16, 24),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Flexible(
child: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(
title,
textAlign: TextAlign.center,
style: _text(16, FontWeight.w600, AppColors.onCosmic),
),
if (body case final b?) ...[
const SizedBox(height: 16),
Text(
b,
textAlign: TextAlign.center,
style: _text(16, FontWeight.w500, AppColors.onCosmic)
.copyWith(height: 1.4),
),
],
if (content case final c?) ...[
const SizedBox(height: 16),
c,
],
],
),
),
),
const SizedBox(height: 16),
Row(
children: [
if (cancelLabel case final c?) ...[
button(c, false, filled: false),
const SizedBox(width: 8),
],
button(confirmLabel, true, filled: true),
],
),
],
),
),
);
}
}
const _barrier = Color(0xB3000000);
/// 취소 · 실행 두 갈래. 실행이면 true. [confirmLabel] 기본은 「변경」.
Future<bool> showConfirmDialog(
BuildContext context,
String title, {
String? body,
Widget? content,
String? cancelLabel,
String? confirmLabel,
}) async {
final r = await showDialog<bool>(
context: context,
barrierColor: _barrier,
builder: (context) => AppDialog(
title: title,
body: body,
content: content,
cancelLabel: cancelLabel ?? context.l10n.commonCancel,
confirmLabel: confirmLabel ?? context.l10n.commonChange,
),
);
return r ?? false;
}
/// 확인 하나뿐인 알림 — Figma `511:86714`(타이틀 가운데 · 확인 버튼 가운데).
Future<void> showAlertDialog(
BuildContext context,
String title, {
String? body,
String? confirmLabel,
}) => showDialog<void>(
context: context,
barrierColor: _barrier,
builder: (context) => AppDialog(
title: title,
body: body,
confirmLabel: confirmLabel ?? context.l10n.commonConfirm,
),
);
-39
View File
@@ -1,39 +0,0 @@
import 'package:flutter/material.dart';
import '../theme/tokens.dart';
/// 표본 데이터 알림(G-08) — 본문을 밀지 않도록 앱바 오른쪽에 작은 「MOCK」 알약으로 둔다.
/// 사유 문구는 길게 눌렀을 때와 스크린리더로 읽힌다.
class MockTag extends StatelessWidget {
const MockTag({super.key, required this.note});
final String note;
@override
Widget build(BuildContext context) => Tooltip(
message: note,
child: Semantics(
label: 'MOCK · $note',
excludeSemantics: true,
child: Container(
margin: const EdgeInsets.only(right: 16),
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(AppRadius.sm),
border: Border.all(color: AppColors.neonViolet),
),
child: Text(
'MOCK',
style: const TextStyle(
fontFamily: AppFonts.body,
fontSize: 10,
fontWeight: FontWeight.w600,
height: 1.2,
letterSpacing: -0.25,
color: AppColors.neonViolet,
),
),
),
),
);
}