From 397605d5e58ebcce0518a09313f204c4201309a7 Mon Sep 17 00:00:00 2001 From: kyy Date: Fri, 20 Mar 2026 15:10:40 +0900 Subject: [PATCH] =?UTF-8?q?uf=20=ED=9A=8C=EC=9B=90=EA=B0=80=EC=9E=85=20?= =?UTF-8?q?=EC=95=BD=EA=B4=80=EB=8F=99=EC=9D=98=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=81=AC=ED=83=91=20=EB=A0=88=EC=9D=B4=EC=95=84=EC=9B=83=20?= =?UTF-8?q?=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- locales/en.toml | 6 + locales/ko.toml | 7 +- locales/template.toml | 7 +- .../auth/presentation/signup_screen.dart | 408 +++++++++++++----- 4 files changed, 323 insertions(+), 105 deletions(-) diff --git a/locales/en.toml b/locales/en.toml index 1fd73f87..4a5d6f06 100644 --- a/locales/en.toml +++ b/locales/en.toml @@ -680,6 +680,11 @@ privacy_full = "\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ tos_full = "\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n바론 소프트웨어 이용약관\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n제1장 총칙\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n제1조 (목적)\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n이 약관은 바론컨설턴트(이하 \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\"회사\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\"라 합니다)가 제공하는 바론소프트웨어(이하 \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\"서비스\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\"라 합니다)를 이용함에 있어 회사와 이용자 간의 권리, 의무 및 책임사항과 기타 필요한 사항을 정하는 것을 목적으로 합니다.\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n제2조 (용어의 정의)\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n① 본 약관에서 사용하는 용어의 정의는 다음과 같습니다:\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n- “서비스”란 회사가 제공하는 소프트웨어 및 관련 제반 서비스를 의미합니다.\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n- “이용자”란 회사의 서비스에 접속하여 본 약관에 따라 회사가 제공하는 서비스를 이용하는 회원 및 비회원을 말합니다.\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n- “회원”이란 본 약관에 동의하고 회사와 이용계약을 체결한 자를 의미합니다.\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n- “비회원”이란 회원가입을 하지 않고 회사가 제공하는 일부 서비스를 이용하는 자를 말합니다.\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n제3조 (약관의 효력 및 변경)\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n① 본 약관은 이용자가 본 약관에 동의하고, 회사가 이에 대한 승낙을 완료함으로써 효력이 발생합니다. ② 회사는 필요한 경우 본 약관을 변경할 수 있으며, 변경된 약관은 서비스 화면에 공지된 후 효력이 발생합니다.\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n제4조 (약관 외 준칙)\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n본 약관에 명시되지 않은 사항에 대해서는 대한민국의 관련 법령과 상관습에 따릅니다.\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n제2장 서비스 이용계약\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n제5조 (이용계약의 성립)\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n이용계약은 이용자가 약관의 내용에 동의하고, 회사가 제공하는 소정의 회원가입 신청서를 작성하여 가입을 완료한 후, 회사가 이를 승인함으로써 성립합니다.\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n제6조 (이용계약의 유보와 거절)\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n① 회사는 다음 각 호에 해당하는 경우 이용계약의 성립을 유보하거나 거절할 수 있습니다: - 신청서의 내용이 허위로 판명된 경우 - 서비스 제공이 기술적으로 어려운 경우\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n제7조 (계약사항의 변경)\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n회원은 개인정보 관리 메뉴를 통해 언제든지 자신의 정보를 열람하고 수정할 수 있습니다. 회원의 정보가 변경된 경우 즉시 수정해야 하며, 수정하지 않아 발생하는 문제의 책임은 회원에게 있습니다.\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n제3장 개인정보 보호\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n제8조 (개인정보 보호의 원칙)\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n① 회원의 개인정보는 관련 법령에 따라 보호됩니다. ② 회사는 개인정보 보호와 관련된 세부 사항을 별도로 마련한 개인정보처리방침에 따라 관리하며, 이용자는 언제든지 해당 방침을 통해 개인정보 관리에 대한 자세한 내용을 확인할 수 있습니다.\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n제9조 (개인정보처리방침 준수)\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n① 회사는 개인정보 보호와 관련된 구체적인 사항을 개인정보처리방침에 따라 관리합니다. ② 개인정보의 수집, 이용, 제공, 보관, 보호 등에 관한 사항은 회사의 개인정보처리방침을 따르며, 이용자는 회사 웹사이트에서 이를 확인할 수 있습니다. ③ 회사는 개인정보 보호를 위해 최선을 다하며, 관련 법령에 따라 이용자의 개인정보를 안전하게 관리합니다.\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n제10조 (14세 미만 아동의 개인정보 보호)\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n① 회사는 14세 미만 아동의 개인정보를 수집할 경우, 반드시 법정대리인의 동의를 받아야 합니다. ② 법정대리인은 아동의 개인정보 열람, 수정, 삭제를 요청할 수 있으며, 회사는 이를 신속하게 처리합니다. ③ 14세 미만 아동의 개인정보 보호와 관련된 구체적인 사항은 개인정보처리방침에 명시되어 있습니다.\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n제4장 서비스 제공 및 이용\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n제11조 (서비스 제공)\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n회사는 회원의 이용 신청을 승인한 때부터 서비스를 개시합니다. 서비스 이용은 연중무휴 24시간을 원칙으로 합니다.\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n제12조 (서비스의 변경 및 중단)\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n회사는 서비스 제공이 어려운 경우 사전 고지 후 서비스를 변경하거나 중단할 수 있습니다.\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n제5장 정보 제공 및 광고\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n제13조 (정보 제공 및 광고)\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n① 회사는 서비스 이용 중 필요하다고 인정되는 정보 및 광고를 제공할 수 있습니다. ② 회원은 원치 않는 정보를 수신 거부할 수 있습니다.\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n제6장 게시물 관리\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n제14조 (게시물의 관리)\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n회사는 회원이 게시한 내용이 불법적이거나 약관에 위배될 경우 이를 삭제할 수 있습니다.\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n제15조 (게시물의 저작권)\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n게시물의 저작권은 회원에게 있으며, 회사는 이를 서비스 홍보 및 개선 목적으로 사용할 수 있습니다.\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n제7장 계약 해지 및 이용 제한\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n제16조 (계약 해지)\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n회원은 언제든지 계약 해지를 요청할 수 있으며, 회사는 신속하게 처리합니다.\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n제17조 (이용 제한)\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n회사는 회원이 약관을 위반할 경우 서비스 이용을 제한할 수 있습니다.\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n제8장 손해 배상 및 면책 조항\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n제18조 (손해 배상)\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n회사는 무료로 제공되는 서비스와 관련하여 회원에게 발생한 손해에 대해 책임을 지지 않습니다.\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n제19조 (면책 조항)\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n회사는 천재지변 등 불가항력적인 사유로 인해 서비스를 제공하지 못하는 경우 책임을 지지 않습니다.\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n제9장 유료 서비스\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n20조 (유료 서비스의 이용)\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n① 회사는 회원에게 특정 서비스에 대해 유료로 제공할 수 있습니다. ② 유료 서비스의 이용 요금, 결제 방식, 환불 절차 등에 대한 상세 내용은 서비스 안내 페이지와 결제 화면에 명시합니다. ③ 유료 서비스 이용 요금은 회사가 정한 결제 방식에 따라 결제됩니다. 회원은 신용카드, 계좌이체, 휴대전화 결제 등 회사가 제공하는 다양한 결제 방식을 통해 요금을 납부할 수 있습니다. ④ 유료 서비스의 이용 요금은 선불 결제를 원칙으로 하며, 이용 기간 중 서비스 중지 및 해지 시 남은 이용 기간에 대한 환불은 회사의 환불 정책에 따라 처리됩니다. ⑤ 회사는 회원의 유료 서비스 이용과 관련하여 발생한 문제에 대해 최선을 다해 해결하도록 노력합니다. 다만, 회사의 고의 또는 중대한 과실이 없는 한 회원이 유료 서비스 이용 중 입은 손해에 대해서는 책임을 지지 않습니다.\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n제21조(환불 정책)\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n① 회원은 결제 후 7일 이내에 서비스 이용을 시작하지 않은 경우, 요금 전액을 환불받을 수 있습니다. ② 유료 서비스 이용 중 부득이한 사유로 서비스가 중지된 경우, 회사는 이용하지 않은 부분에 대해 환불 절차를 밟습니다. ③ 회원의 귀책사유로 인해 서비스 이용이 중지된 경우, 환불이 불가능합니다. ④ 환불은 회원이 지정한 계좌로 환불 절차를 거치며, 환불 요청 후 7일 이내에 처리됩니다.\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n제22조 (유료 서비스의 중지 및 해지)\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n① 회원이 유료 서비스를 해지하고자 하는 경우, 회사의 고객 지원 센터에 해지 신청을 해야 합니다. ② 회사는 회원이 약관을 위반하거나 부정한 방법으로 유료 서비스를 이용한 경우, 유료 서비스 이용을 즉시 중지하고 계약을 해지할 수 있습니다.\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n제10장 양도 금지\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n제23조 (양도 금지)\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n회원은 서비스 이용권한, 기타 이용계약상의 지위를 제3자에게 양도, 증여할 수 없으며, 이를 담보로 제공할 수 없습니다.\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n제11장 관할 법원\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n제24조 (분쟁 해결)\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n서비스 이용과 관련하여 분쟁이 발생한 경우, 회사와 회원은 성실히 협의하여 해결합니다.\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n제25조 (관할 법원)\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n본 약관에 따른 분쟁은 서울중앙지방법원을 관할 법원으로 합니다.\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n부칙\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n본 약관은 2024년 10월 1일부터 시행됩니다.\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\n" [msg.userfront.signup.agreement] +all_hint = "Agree to both required documents to continue to the next step." +description = "Review the service terms and privacy collection notice, then agree to continue." +privacy_summary = "Review what personal data is collected, why it is used, and how it is retained." +progress = "{{count}} of {{total}} required agreements completed" +tos_summary = "Review the service terms, usage conditions, and responsibilities." title = "Please review and agree to the terms to continue." [msg.userfront.signup.auth] @@ -1721,6 +1726,7 @@ title = "Sign up" [ui.userfront.signup.agreement] all = "Agree to all" privacy_title = "Privacy Policy (Required)" +required = "Required" tos_title = "Terms of Service (Required)" [ui.userfront.signup.auth] diff --git a/locales/ko.toml b/locales/ko.toml index dce14c55..0ae50573 100644 --- a/locales/ko.toml +++ b/locales/ko.toml @@ -676,6 +676,11 @@ privacy_full = "개인정보 수집 및 이용 동의 전문..." tos_full = "서비스 이용약관 전문..." [msg.userfront.signup.agreement] +all_hint = "필수 약관 2개를 모두 확인하고 동의하면 다음 단계로 진행할 수 있습니다." +description = "계속 진행하려면 서비스 이용 조건과 개인정보 수집·이용 항목을 확인한 뒤 동의해주세요." +privacy_summary = "개인정보 수집 항목, 이용 목적, 보관 기준을 안내합니다." +progress = "필수 약관 {{total}}개 중 {{count}}개 동의 완료" +tos_summary = "서비스 이용 조건과 책임 범위를 확인할 수 있습니다." title = "서비스 이용을 위해\n약관에 동의해주세요" [msg.userfront.signup.auth] @@ -1709,6 +1714,7 @@ title = "회원가입" [ui.userfront.signup.agreement] all = "모두 동의합니다" privacy_title = "개인정보 수집 및 이용 동의 (필수)" +required = "필수" tos_title = "바론 소프트웨어 이용약관 (필수)" [ui.userfront.signup.auth] @@ -1742,4 +1748,3 @@ verify = "본인인증" [ui.userfront.signup.success] action = "로그인하기" - diff --git a/locales/template.toml b/locales/template.toml index a74e4a7d..5337c97f 100644 --- a/locales/template.toml +++ b/locales/template.toml @@ -676,6 +676,11 @@ privacy_full = "" tos_full = "" [msg.userfront.signup.agreement] +all_hint = "" +description = "" +privacy_summary = "" +progress = "" +tos_summary = "" title = "" [msg.userfront.signup.auth] @@ -1709,6 +1714,7 @@ title = "" [ui.userfront.signup.agreement] all = "" privacy_title = "" +required = "" tos_title = "" [ui.userfront.signup.auth] @@ -1742,4 +1748,3 @@ verify = "" [ui.userfront.signup.success] action = "" - diff --git a/userfront/lib/features/auth/presentation/signup_screen.dart b/userfront/lib/features/auth/presentation/signup_screen.dart index 708e4d5e..400106c0 100644 --- a/userfront/lib/features/auth/presentation/signup_screen.dart +++ b/userfront/lib/features/auth/presentation/signup_screen.dart @@ -14,6 +14,13 @@ class SignupScreen extends StatefulWidget { } class _SignupScreenState extends State { + static const _signupInk = Color(0xFF111827); + static const _signupBorder = Color(0xFFE5E7EB); + static const _signupSurface = Color(0xFFF8FAFC); + static const _signupMuted = Color(0xFF6B7280); + static const _agreementDesktopBreakpoint = 960.0; + static const _agreementCardMaxWidth = 880.0; + final _formKey = GlobalKey(); int _currentStep = 1; @@ -417,105 +424,305 @@ class _SignupScreenState extends State { } Widget _buildStepAgreement() { - return Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Text( - tr('msg.userfront.signup.agreement.title'), - style: const TextStyle( - fontSize: 20, - fontWeight: FontWeight.bold, - height: 1.3, - ), - ), - const SizedBox(height: 24), - // 모두 동의 버튼 - Container( - decoration: BoxDecoration( - color: Colors.grey[50], - borderRadius: BorderRadius.circular(8), - border: Border.all(color: Colors.grey[200]!), - ), - child: CheckboxListTile( - title: Text( - tr('ui.userfront.signup.agreement.all'), - style: const TextStyle(fontSize: 15, fontWeight: FontWeight.bold), + return LayoutBuilder( + builder: (context, constraints) { + final isDesktop = constraints.maxWidth >= _agreementDesktopBreakpoint; + + return Align( + alignment: Alignment.topCenter, + child: ConstrainedBox( + constraints: BoxConstraints( + maxWidth: isDesktop + ? _agreementCardMaxWidth + : constraints.maxWidth, + ), + child: DecoratedBox( + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(isDesktop ? 24 : 18), + border: Border.all(color: _signupBorder), + boxShadow: isDesktop + ? const [ + BoxShadow( + color: Color(0x12000000), + blurRadius: 32, + offset: Offset(0, 18), + ), + ] + : const [], + ), + child: Padding( + padding: EdgeInsets.symmetric( + horizontal: isDesktop ? 32 : 20, + vertical: isDesktop ? 32 : 24, + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Text( + tr('msg.userfront.signup.agreement.title'), + style: TextStyle( + fontSize: isDesktop ? 28 : 20, + fontWeight: FontWeight.w700, + height: 1.25, + color: _signupInk, + ), + ), + const SizedBox(height: 12), + Text( + tr('msg.userfront.signup.agreement.description'), + style: TextStyle( + fontSize: isDesktop ? 15 : 14, + height: 1.6, + color: _signupMuted, + ), + ), + SizedBox(height: isDesktop ? 28 : 24), + _buildAgreementSummaryCard(isDesktop: isDesktop), + const SizedBox(height: 18), + _agreementSection( + title: tr('ui.userfront.signup.agreement.tos_title'), + summary: tr('msg.userfront.signup.agreement.tos_summary'), + content: _tosText, + value: _termsAccepted, + isDesktop: isDesktop, + onChanged: (val) => + setState(() => _termsAccepted = val ?? false), + ), + const SizedBox(height: 18), + _agreementSection( + title: tr('ui.userfront.signup.agreement.privacy_title'), + summary: tr( + 'msg.userfront.signup.agreement.privacy_summary', + ), + content: _privacyText, + value: _privacyAccepted, + isDesktop: isDesktop, + onChanged: (val) => + setState(() => _privacyAccepted = val ?? false), + ), + ], + ), + ), ), - value: _termsAccepted && _privacyAccepted, - onChanged: (val) { - setState(() { - _termsAccepted = val!; - _privacyAccepted = val; - }); - }, - controlAffinity: ListTileControlAffinity.leading, - activeColor: Colors.black, ), + ); + }, + ); + } + + Widget _buildAgreementSummaryCard({required bool isDesktop}) { + final acceptedCount = [ + _termsAccepted, + _privacyAccepted, + ].where((accepted) => accepted).length; + + return DecoratedBox( + decoration: BoxDecoration( + color: _signupSurface, + borderRadius: BorderRadius.circular(16), + border: Border.all(color: _signupBorder), + ), + child: Padding( + padding: EdgeInsets.all(isDesktop ? 20 : 16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + CheckboxListTile( + title: Text( + tr('ui.userfront.signup.agreement.all'), + style: TextStyle( + fontSize: isDesktop ? 17 : 15, + fontWeight: FontWeight.w700, + color: _signupInk, + ), + ), + subtitle: Padding( + padding: const EdgeInsets.only(top: 4), + child: Text( + tr('msg.userfront.signup.agreement.all_hint'), + style: const TextStyle( + fontSize: 13, + height: 1.45, + color: _signupMuted, + ), + ), + ), + value: _termsAccepted && _privacyAccepted, + onChanged: (val) { + setState(() { + final next = val ?? false; + _termsAccepted = next; + _privacyAccepted = next; + }); + }, + contentPadding: EdgeInsets.zero, + controlAffinity: ListTileControlAffinity.leading, + activeColor: _signupInk, + ), + const SizedBox(height: 8), + Text( + tr( + 'msg.userfront.signup.agreement.progress', + params: {'count': '$acceptedCount', 'total': '2'}, + ), + style: const TextStyle( + fontSize: 12, + fontWeight: FontWeight.w600, + color: _signupMuted, + ), + ), + ], ), - const SizedBox(height: 16), - _agreementSection( - title: tr('ui.userfront.signup.agreement.tos_title'), - content: _tosText, - value: _termsAccepted, - onChanged: (val) => setState(() => _termsAccepted = val!), - ), - const SizedBox(height: 16), - _agreementSection( - title: tr('ui.userfront.signup.agreement.privacy_title'), - content: _privacyText, - value: _privacyAccepted, - onChanged: (val) => setState(() => _privacyAccepted = val!), - ), - ], + ), ); } Widget _agreementSection({ required String title, + required String summary, required String content, required bool value, + required bool isDesktop, required ValueChanged onChanged, }) { - return Column( - children: [ - CheckboxListTile( - title: Text( - title, - style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w600), - ), - value: value, - onChanged: onChanged, - controlAffinity: ListTileControlAffinity.leading, - contentPadding: EdgeInsets.zero, - activeColor: Colors.black, - ), - Container( - height: 120, - width: double.infinity, - padding: const EdgeInsets.all(12), - decoration: BoxDecoration( - color: Colors.white, - border: Border.all(color: Colors.grey[300]!), - borderRadius: BorderRadius.circular(4), - ), - child: SingleChildScrollView( - child: Text( - content, - style: const TextStyle( - fontSize: 12, - color: Colors.grey, - height: 1.5, + final contentHeight = isDesktop ? 260.0 : 148.0; + + return DecoratedBox( + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(18), + border: Border.all(color: _signupBorder), + ), + child: Padding( + padding: EdgeInsets.all(isDesktop ? 20 : 16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: CheckboxListTile( + title: Text( + title, + style: TextStyle( + fontSize: isDesktop ? 16 : 14, + fontWeight: FontWeight.w700, + color: _signupInk, + ), + ), + subtitle: Padding( + padding: const EdgeInsets.only(top: 6), + child: Text( + summary, + style: const TextStyle( + fontSize: 13, + height: 1.45, + color: _signupMuted, + ), + ), + ), + value: value, + onChanged: onChanged, + controlAffinity: ListTileControlAffinity.leading, + contentPadding: EdgeInsets.zero, + activeColor: _signupInk, + ), + ), + const SizedBox(width: 12), + _buildRequiredBadge(), + ], + ), + const SizedBox(height: 12), + Container( + height: contentHeight, + width: double.infinity, + padding: EdgeInsets.all(isDesktop ? 18 : 14), + decoration: BoxDecoration( + color: _signupSurface, + border: Border.all(color: _signupBorder), + borderRadius: BorderRadius.circular(14), + ), + child: SingleChildScrollView( + child: SelectableText( + content, + style: TextStyle( + fontSize: isDesktop ? 13 : 12, + color: _signupMuted, + height: 1.6, + ), + ), ), ), - ), + ], ), - ], + ), ); } - static String get _tosText => tr( - 'msg.userfront.signup.tos_full', - fallback: """ + Widget _buildRequiredBadge() { + return Container( + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6), + decoration: BoxDecoration( + color: const Color(0xFFEEF2FF), + borderRadius: BorderRadius.circular(999), + border: Border.all(color: const Color(0xFFC7D2FE)), + ), + child: Text( + tr('ui.userfront.signup.agreement.required'), + style: const TextStyle( + fontSize: 11, + fontWeight: FontWeight.w700, + color: Color(0xFF3730A3), + ), + ), + ); + } + + Widget _buildStepBody() { + final stepChild = _currentStep == 1 + ? _buildStepAgreement() + : (_currentStep == 2 + ? _buildStepAuth() + : (_currentStep == 3 ? _buildStepInfo() : _buildStepPassword())); + + return LayoutBuilder( + builder: (context, constraints) { + final isAgreementStep = _currentStep == 1; + final horizontalPadding = + isAgreementStep && + constraints.maxWidth >= _agreementDesktopBreakpoint + ? 40.0 + : 24.0; + + return SingleChildScrollView( + padding: EdgeInsets.fromLTRB( + horizontalPadding, + 24, + horizontalPadding, + 32, + ), + child: Form(key: _formKey, child: stepChild), + ); + }, + ); + } + + static String _resolveAgreementText( + String key, { + required String fallback, + required Set placeholders, + }) { + final localized = tr(key, fallback: '').trim(); + if (localized.isEmpty || placeholders.contains(localized)) { + return fallback; + } + return localized; + } + + static String get _tosText { + const fallback = """ 바론 소프트웨어 이용약관 제1장 총칙 @@ -585,12 +792,16 @@ class _SignupScreenState extends State { 본 약관에 따른 분쟁은 서울중앙지방법원을 관할 법원으로 합니다. 부칙 본 약관은 2024년 10월 1일부터 시행됩니다. -""", - ); +"""; + return _resolveAgreementText( + 'msg.userfront.signup.tos_full', + fallback: fallback, + placeholders: {'서비스 이용약관 전문...', 'Tos Full'}, + ); + } - static String get _privacyText => tr( - 'msg.userfront.signup.privacy_full', - fallback: """ + static String get _privacyText { + const fallback = """ 개인정보 수집 및 이용 동의 바론서비스 개인정보처리방침 @@ -698,8 +909,13 @@ class _SignupScreenState extends State { 회사는 이용자의 개인정보를 국외로 이전하지 않으며, 향후 필요한 경우, 사전에 이용자의 동의를 받습니다. 제8조 (기타) 본 방침에 명시되지 않은 사항은 회사의 내부 방침과 관련 법령에 따릅니다. -""", - ); +"""; + return _resolveAgreementText( + 'msg.userfront.signup.privacy_full', + fallback: fallback, + placeholders: {'개인정보 수집 및 이용 동의 전문...', 'Privacy Full'}, + ); + } Widget _buildStepAuth() { return Column( @@ -1233,21 +1449,7 @@ class _SignupScreenState extends State { padding: const EdgeInsets.symmetric(horizontal: 20), child: _buildStepIndicator(), ), - Expanded( - child: SingleChildScrollView( - padding: const EdgeInsets.all(24), - child: Form( - key: _formKey, - child: _currentStep == 1 - ? _buildStepAgreement() - : (_currentStep == 2 - ? _buildStepAuth() - : (_currentStep == 3 - ? _buildStepInfo() - : _buildStepPassword())), - ), - ), - ), + Expanded(child: _buildStepBody()), Padding( padding: const EdgeInsets.all(24), child: Row(