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 showConfirmDialog( BuildContext context, String title, { String? body, Widget? content, String? cancelLabel, String? confirmLabel, }) async { final r = await showDialog( 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 showAlertDialog( BuildContext context, String title, { String? body, String? confirmLabel, }) => showDialog( context: context, barrierColor: _barrier, builder: (context) => AppDialog( title: title, body: body, confirmLabel: confirmLabel ?? context.l10n.commonConfirm, ), ); /// 아래에서 올라오고 닫으면 아래로 내려가는 전체 화면 — Figma 설명 「닫기 버튼 클릭 시, 하단 /// move out 페이지 종료」(`516:87382` 성운 생성 · `516:87457` 행성 생성 · `516:88164` 이클립스 상세). /// 안드로이드 기본 전환(확대)과 iOS 기본 전환(옆으로)이 모두 이 동작이 아니라 따로 둔다. Route slideUpRoute(WidgetBuilder builder) => PageRouteBuilder( pageBuilder: (context, _, _) => builder(context), transitionsBuilder: (_, animation, _, child) => SlideTransition( position: Tween( begin: const Offset(0, 1), end: Offset.zero, ).chain(CurveTween(curve: Curves.easeOutCubic)).animate(animation), child: child, ), );