From 4ca492b31c4ee1d2506ed915bb14bf2003c93078 Mon Sep 17 00:00:00 2001 From: kyy Date: Fri, 22 May 2026 16:03:25 +0900 Subject: [PATCH] =?UTF-8?q?=ED=9A=8C=EC=9B=90=EA=B0=80=EC=9E=85=20UI=20?= =?UTF-8?q?=EC=9D=BC=EA=B4=80=EC=84=B1=20=EB=B0=8F=20=EA=B0=80=EC=8B=9C?= =?UTF-8?q?=EC=84=B1=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../auth/presentation/signup_screen.dart | 297 ++++++++++-------- 1 file changed, 168 insertions(+), 129 deletions(-) diff --git a/userfront/lib/features/auth/presentation/signup_screen.dart b/userfront/lib/features/auth/presentation/signup_screen.dart index 321f011c..1ed643de 100644 --- a/userfront/lib/features/auth/presentation/signup_screen.dart +++ b/userfront/lib/features/auth/presentation/signup_screen.dart @@ -16,12 +16,6 @@ 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 _signupDone = Color(0xFF0F766E); - static const _signupDoneSurface = Color(0xFFECFDF5); static const _agreementDesktopBreakpoint = 1024.0; static const _agreementCardMaxWidth = 880.0; static const _stepIndicatorDesktopBreakpoint = 1024.0; @@ -69,6 +63,64 @@ class _SignupScreenState extends State { Timer? _phoneTimer; int _phoneSeconds = 0; + ColorScheme get _scheme => Theme.of(context).colorScheme; + bool get _isDark => _scheme.brightness == Brightness.dark; + Color get _signupInk => _scheme.onSurface; + Color get _signupBorder => _scheme.outlineVariant; + Color get _signupSurface => _scheme.surfaceContainerLow; + Color get _signupCard => _scheme.surface; + Color get _signupMuted => _scheme.onSurfaceVariant; + Color get _signupDone => _signupAccent.withValues(alpha: _isDark ? 0.78 : 0.72); + Color get _signupDoneSurface => + _signupAccent.withValues(alpha: _isDark ? 0.18 : 0.12); + Color get _signupAccent => + _isDark ? const Color(0xFF93C5FD) : const Color(0xFF1E3A8A); + Color get _signupOnAccent => + _isDark ? const Color(0xFF082F49) : Colors.white; + Color get _signupAccentSurface => + _isDark ? const Color(0xFF172554) : const Color(0xFFDBEAFE); + Color get _signupAccentInk => + _isDark ? const Color(0xFFBFDBFE) : const Color(0xFF1D4ED8); + Color get _signupRequiredSurface => + _isDark ? const Color(0xFF312E81) : const Color(0xFFEEF2FF); + Color get _signupRequiredBorder => + _isDark ? const Color(0xFF4F46E5) : const Color(0xFFC7D2FE); + Color get _signupRequiredInk => + _isDark ? const Color(0xFFC7D2FE) : const Color(0xFF3730A3); + Color get _signupSummaryBorder => + _signupAccent.withValues(alpha: _isDark ? 0.28 : 0.16); + + BorderSide get _signupDividerSide => BorderSide(color: _signupBorder); + + CheckboxThemeData get _signupCheckboxTheme => CheckboxThemeData( + side: _signupDividerSide, + fillColor: WidgetStateProperty.resolveWith((states) { + if (states.contains(WidgetState.selected)) { + return _signupAccent; + } + return _signupCard; + }), + checkColor: WidgetStateProperty.all(_signupOnAccent), + ); + + ButtonStyle get _signupPrimaryButtonStyle => FilledButton.styleFrom( + minimumSize: const Size.fromHeight(55), + backgroundColor: _signupAccent, + foregroundColor: _signupOnAccent, + disabledBackgroundColor: _isDark + ? _scheme.surfaceContainerHighest + : const Color(0xFFE5E7EB), + disabledForegroundColor: _isDark + ? _scheme.onSurfaceVariant + : const Color(0xFF9CA3AF), + ); + + ButtonStyle get _signupSecondaryButtonStyle => OutlinedButton.styleFrom( + minimumSize: const Size.fromHeight(55), + foregroundColor: _signupInk, + side: BorderSide(color: _signupBorder), + ); + String _renderTranslatedText( String key, { String? fallback, @@ -454,7 +506,7 @@ class _SignupScreenState extends State { final circleRadius = isDesktop ? 17.0 : 12.0; final labelStyle = TextStyle( fontSize: isDesktop ? 12 : 9, - color: isCurrent ? _signupInk : (isDone ? _signupDone : _signupMuted), + color: isCurrent ? _signupAccent : (isDone ? _signupDone : _signupMuted), fontWeight: isCurrent || isDone ? FontWeight.w700 : FontWeight.w500, height: 1.2, ); @@ -469,13 +521,13 @@ class _SignupScreenState extends State { decoration: BoxDecoration( color: isDone ? _signupDone - : (isCurrent ? _signupInk : _signupSurface), + : (isCurrent ? _signupAccent : _signupSurface), shape: BoxShape.circle, border: Border.all( color: isDone ? _signupDone - : (isCurrent ? _signupInk : _signupBorder), - width: isCurrent ? 1.5 : 1, + : (isCurrent ? _signupAccent : _signupBorder), + width: isDone ? 0 : (isCurrent ? 1.5 : 1), ), boxShadow: isDesktop && (isCurrent || isDone) ? const [ @@ -497,7 +549,7 @@ class _SignupScreenState extends State { : Text( '$step', style: TextStyle( - color: isCurrent ? Colors.white : _signupMuted, + color: isCurrent ? _signupOnAccent : _signupMuted, fontSize: isDesktop ? 13 : 10, fontWeight: FontWeight.w700, ), @@ -564,9 +616,9 @@ class _SignupScreenState extends State { ), child: DecoratedBox( decoration: BoxDecoration( - color: Colors.white, + color: _signupCard, borderRadius: BorderRadius.circular(isDesktop ? 24 : 18), - border: Border.all(color: _signupBorder), + border: Border.all(color: _signupSummaryBorder), boxShadow: isDesktop ? const [ BoxShadow( @@ -647,58 +699,60 @@ class _SignupScreenState extends State { decoration: BoxDecoration( color: _signupSurface, borderRadius: BorderRadius.circular(16), - border: Border.all(color: _signupBorder), + border: Border.all(color: _signupSummaryBorder), ), 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, + child: CheckboxTheme( + data: _signupCheckboxTheme, + 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: 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, ), - 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'}, + const SizedBox(height: 8), + Text( + tr( + 'msg.userfront.signup.agreement.progress', + params: {'count': '$acceptedCount', 'total': '2'}, + ), + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w600, + color: _signupMuted, + ), ), - style: const TextStyle( - fontSize: 12, - fontWeight: FontWeight.w600, - color: _signupMuted, - ), - ), - ], + ], + ), ), ), ); @@ -716,13 +770,15 @@ class _SignupScreenState extends State { return DecoratedBox( decoration: BoxDecoration( - color: Colors.white, + color: _signupCard, borderRadius: BorderRadius.circular(18), - border: Border.all(color: _signupBorder), + border: Border.all(color: _signupSummaryBorder), ), child: Padding( padding: EdgeInsets.all(isDesktop ? 20 : 16), - child: Column( + child: CheckboxTheme( + data: _signupCheckboxTheme, + child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Row( @@ -742,7 +798,7 @@ class _SignupScreenState extends State { padding: const EdgeInsets.only(top: 6), child: Text( summary, - style: const TextStyle( + style: TextStyle( fontSize: 13, height: 1.45, color: _signupMuted, @@ -753,7 +809,6 @@ class _SignupScreenState extends State { onChanged: onChanged, controlAffinity: ListTileControlAffinity.leading, contentPadding: EdgeInsets.zero, - activeColor: _signupInk, ), ), const SizedBox(width: 12), @@ -767,7 +822,7 @@ class _SignupScreenState extends State { padding: EdgeInsets.all(isDesktop ? 18 : 14), decoration: BoxDecoration( color: _signupSurface, - border: Border.all(color: _signupBorder), + border: Border.all(color: _signupSummaryBorder), borderRadius: BorderRadius.circular(14), ), child: SingleChildScrollView( @@ -782,6 +837,7 @@ class _SignupScreenState extends State { ), ), ], + ), ), ), ); @@ -791,16 +847,16 @@ class _SignupScreenState extends State { return Container( padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6), decoration: BoxDecoration( - color: const Color(0xFFEEF2FF), + color: _signupRequiredSurface, borderRadius: BorderRadius.circular(999), - border: Border.all(color: const Color(0xFFC7D2FE)), + border: Border.all(color: _signupRequiredBorder), ), child: Text( tr('ui.userfront.signup.agreement.required'), - style: const TextStyle( + style: TextStyle( fontSize: 11, fontWeight: FontWeight.w700, - color: Color(0xFF3730A3), + color: _signupRequiredInk, ), ), ); @@ -1234,7 +1290,7 @@ Matters not expressly provided in this Policy are governed by the Company's inte ), child: DecoratedBox( decoration: BoxDecoration( - color: Colors.white, + color: _signupCard, borderRadius: BorderRadius.circular(isDesktop ? 24 : 18), border: Border.all(color: _signupBorder), boxShadow: isDesktop @@ -1331,7 +1387,7 @@ Matters not expressly provided in this Policy are governed by the Company's inte Widget _buildAuthNoticeCard({required bool isDesktop}) { return DecoratedBox( decoration: BoxDecoration( - color: const Color(0xFFF8FAFC), + color: _signupSurface, borderRadius: BorderRadius.circular(16), border: Border.all(color: _signupBorder), ), @@ -1344,13 +1400,13 @@ Matters not expressly provided in this Policy are governed by the Company's inte width: isDesktop ? 36 : 32, height: isDesktop ? 36 : 32, decoration: BoxDecoration( - color: const Color(0xFFDBEAFE), + color: _signupAccentSurface, borderRadius: BorderRadius.circular(999), ), - child: const Icon( + child: Icon( Icons.info_outline, size: 18, - color: Color(0xFF1D4ED8), + color: _signupAccentInk, ), ), const SizedBox(width: 12), @@ -1360,7 +1416,7 @@ Matters not expressly provided in this Policy are governed by the Company's inte style: TextStyle( fontSize: isDesktop ? 14 : 12, height: 1.4, - color: const Color(0xFF1E3A8A), + color: _signupAccent, fontWeight: FontWeight.w600, ), ), @@ -1394,9 +1450,9 @@ Matters not expressly provided in this Policy are governed by the Company's inte }) { return DecoratedBox( decoration: BoxDecoration( - color: Colors.white, + color: _signupCard, borderRadius: BorderRadius.circular(18), - border: Border.all(color: _signupBorder), + border: Border.all(color: _signupSummaryBorder), ), child: Padding( padding: EdgeInsets.all(isDesktop ? 20 : 16), @@ -1457,12 +1513,7 @@ Matters not expressly provided in this Policy are governed by the Company's inte width: 108, child: FilledButton( onPressed: buttonEnabled ? onRequestCode : null, - style: FilledButton.styleFrom( - backgroundColor: _signupInk, - foregroundColor: Colors.white, - disabledBackgroundColor: const Color(0xFFE5E7EB), - disabledForegroundColor: const Color(0xFF9CA3AF), - ), + style: _signupPrimaryButtonStyle, child: Text( buttonLabel, style: const TextStyle( @@ -1497,12 +1548,7 @@ Matters not expressly provided in this Policy are governed by the Company's inte height: 52, child: FilledButton( onPressed: buttonEnabled ? onRequestCode : null, - style: FilledButton.styleFrom( - backgroundColor: _signupInk, - foregroundColor: Colors.white, - disabledBackgroundColor: const Color(0xFFE5E7EB), - disabledForegroundColor: const Color(0xFF9CA3AF), - ), + style: _signupPrimaryButtonStyle, child: Text( buttonLabel, style: const TextStyle( @@ -1592,11 +1638,11 @@ Matters not expressly provided in this Policy are governed by the Company's inte decoration: BoxDecoration( color: _signupDoneSurface, borderRadius: BorderRadius.circular(999), - border: Border.all(color: const Color(0xFFA7F3D0)), + border: Border.all(color: _signupDone.withValues(alpha: 0.35)), ), child: Text( text.replaceFirst('✅ ', ''), - style: const TextStyle( + style: TextStyle( fontSize: 11, fontWeight: FontWeight.w700, color: _signupDone, @@ -1611,16 +1657,16 @@ Matters not expressly provided in this Policy are governed by the Company's inte decoration: BoxDecoration( color: _signupDoneSurface, borderRadius: BorderRadius.circular(12), - border: Border.all(color: const Color(0xFFA7F3D0)), + border: Border.all(color: _signupDone.withValues(alpha: 0.35)), ), child: Row( children: [ - const Icon(Icons.check_circle, size: 18, color: _signupDone), + Icon(Icons.check_circle, size: 18, color: _signupDone), const SizedBox(width: 8), Expanded( child: Text( text.replaceFirst('✅ ', ''), - style: const TextStyle( + style: TextStyle( color: _signupDone, fontSize: 13, fontWeight: FontWeight.w700, @@ -1647,9 +1693,9 @@ Matters not expressly provided in this Policy are governed by the Company's inte ), child: DecoratedBox( decoration: BoxDecoration( - color: Colors.white, + color: _signupCard, borderRadius: BorderRadius.circular(isDesktop ? 24 : 18), - border: Border.all(color: _signupBorder), + border: Border.all(color: _signupSummaryBorder), boxShadow: isDesktop ? const [ BoxShadow( @@ -1820,13 +1866,13 @@ Matters not expressly provided in this Policy are governed by the Company's inte width: isDesktop ? 36 : 32, height: isDesktop ? 36 : 32, decoration: BoxDecoration( - color: const Color(0xFFEEF2FF), + color: _signupAccentSurface, borderRadius: BorderRadius.circular(999), ), - child: const Icon( + child: Icon( Icons.badge_outlined, size: 18, - color: Color(0xFF4338CA), + color: _signupAccentInk, ), ), const SizedBox(width: 12), @@ -1836,7 +1882,7 @@ Matters not expressly provided in this Policy are governed by the Company's inte style: TextStyle( fontSize: isDesktop ? 14 : 12, height: 1.4, - color: _signupInk, + color: _signupAccent, fontWeight: FontWeight.w600, ), ), @@ -1856,9 +1902,9 @@ Matters not expressly provided in this Policy are governed by the Company's inte }) { return DecoratedBox( decoration: BoxDecoration( - color: Colors.white, + color: _signupCard, borderRadius: BorderRadius.circular(18), - border: Border.all(color: _signupBorder), + border: Border.all(color: _signupSummaryBorder), ), child: Padding( padding: EdgeInsets.all(isDesktop ? 20 : 16), @@ -1883,7 +1929,7 @@ Matters not expressly provided in this Policy are governed by the Company's inte const SizedBox(height: 6), Text( description, - style: const TextStyle( + style: TextStyle( fontSize: 13, height: 1.45, color: _signupMuted, @@ -2018,9 +2064,9 @@ Matters not expressly provided in this Policy are governed by the Company's inte ), child: DecoratedBox( decoration: BoxDecoration( - color: Colors.white, + color: _signupCard, borderRadius: BorderRadius.circular(isDesktop ? 24 : 18), - border: Border.all(color: _signupBorder), + border: Border.all(color: _signupSummaryBorder), boxShadow: isDesktop ? const [ BoxShadow( @@ -2154,13 +2200,13 @@ Matters not expressly provided in this Policy are governed by the Company's inte width: isDesktop ? 36 : 32, height: isDesktop ? 36 : 32, decoration: BoxDecoration( - color: const Color(0xFFDBEAFE), + color: _signupAccentSurface, borderRadius: BorderRadius.circular(999), ), - child: const Icon( + child: Icon( Icons.security_rounded, size: 18, - color: Color(0xFF1D4ED8), + color: _signupAccentInk, ), ), const SizedBox(width: 12), @@ -2170,7 +2216,7 @@ Matters not expressly provided in this Policy are governed by the Company's inte style: TextStyle( fontSize: isDesktop ? 14 : 12, height: 1.4, - color: const Color(0xFF1E3A8A), + color: _signupAccent, fontWeight: FontWeight.w600, ), ), @@ -2189,9 +2235,9 @@ Matters not expressly provided in this Policy are governed by the Company's inte }) { return DecoratedBox( decoration: BoxDecoration( - color: Colors.white, + color: _signupCard, borderRadius: BorderRadius.circular(18), - border: Border.all(color: _signupBorder), + border: Border.all(color: _signupSummaryBorder), ), child: Padding( padding: EdgeInsets.all(isDesktop ? 20 : 16), @@ -2209,7 +2255,7 @@ Matters not expressly provided in this Policy are governed by the Company's inte const SizedBox(height: 6), Text( description, - style: const TextStyle( + style: TextStyle( fontSize: 13, height: 1.45, color: _signupMuted, @@ -2231,7 +2277,7 @@ Matters not expressly provided in this Policy are governed by the Company's inte decoration: BoxDecoration( color: _signupSurface, borderRadius: BorderRadius.circular(14), - border: Border.all(color: _signupBorder), + border: Border.all(color: _signupSummaryBorder), ), child: Padding( padding: EdgeInsets.all(isDesktop ? 16 : 14), @@ -2283,15 +2329,17 @@ Matters not expressly provided in this Policy are governed by the Company's inte } return Scaffold( - backgroundColor: Colors.white, + backgroundColor: _scheme.surfaceContainerLowest, appBar: AppBar( title: Text( tr('ui.userfront.signup.title'), style: const TextStyle(fontWeight: FontWeight.bold), ), elevation: 0, - backgroundColor: Colors.white, - foregroundColor: Colors.black, + backgroundColor: _signupCard, + foregroundColor: _signupInk, + surfaceTintColor: Colors.transparent, + shape: Border(bottom: _signupDividerSide), ), body: SafeArea( child: Column( @@ -2309,14 +2357,8 @@ Matters not expressly provided in this Policy are governed by the Company's inte Expanded( child: OutlinedButton( onPressed: () => setState(() => _currentStep--), - style: OutlinedButton.styleFrom( - minimumSize: const Size.fromHeight(55), - side: const BorderSide(color: Colors.black), - ), - child: Text( - tr('ui.common.prev'), - style: const TextStyle(color: Colors.black), - ), + style: _signupSecondaryButtonStyle, + child: Text(tr('ui.common.prev')), ), ), const SizedBox(width: 12), @@ -2328,16 +2370,13 @@ Matters not expressly provided in this Policy are governed by the Company's inte ? () => setState(() => _currentStep++) : null) : (_isLoading ? null : _handleSignup), - style: FilledButton.styleFrom( - minimumSize: const Size.fromHeight(55), - backgroundColor: Colors.black, - ), + style: _signupPrimaryButtonStyle, child: _isLoading - ? const SizedBox( + ? SizedBox( height: 20, width: 20, child: CircularProgressIndicator( - color: Colors.white, + color: _scheme.onPrimary, strokeWidth: 2, ), )