import 'package:flutter/material.dart'; import 'package:shadcn_ui/shadcn_ui.dart'; import 'package:superport/screens/login/controllers/login_controller.dart'; import 'dart:math' as math; /// shadcn/ui 스타일로 재설계된 로그인 화면 class LoginView extends StatefulWidget { final LoginController controller; final VoidCallback onLoginSuccess; const LoginView({ super.key, required this.controller, required this.onLoginSuccess, }); @override State createState() => _LoginViewState(); } class _LoginViewState extends State with TickerProviderStateMixin { late AnimationController _fadeController; late Animation _fadeAnimation; late AnimationController _slideController; late Animation _slideAnimation; @override void initState() { super.initState(); _setupAnimations(); } void _setupAnimations() { _fadeController = AnimationController( duration: const Duration(milliseconds: 1000), vsync: this, ); _fadeAnimation = Tween(begin: 0.0, end: 1.0).animate( CurvedAnimation(parent: _fadeController, curve: Curves.easeInOut), ); _slideController = AnimationController( duration: const Duration(milliseconds: 800), vsync: this, ); _slideAnimation = Tween( begin: const Offset(0, 0.3), end: Offset.zero, ).animate( CurvedAnimation(parent: _slideController, curve: Curves.easeOutCubic), ); _fadeController.forward(); _slideController.forward(); } @override void dispose() { _fadeController.dispose(); _slideController.dispose(); super.dispose(); } Future _handleLogin() async { final success = await widget.controller.login(); if (success) { widget.onLoginSuccess(); } } @override Widget build(BuildContext context) { final theme = ShadTheme.of(context); return ListenableBuilder( listenable: widget.controller, builder: (context, _) { return Scaffold( backgroundColor: theme.colorScheme.background, body: SafeArea( child: Center( child: SingleChildScrollView( physics: const BouncingScrollPhysics(), child: Padding( padding: const EdgeInsets.all(24), child: ConstrainedBox( constraints: const BoxConstraints(maxWidth: 400), child: FadeTransition( opacity: _fadeAnimation, child: SlideTransition( position: _slideAnimation, child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ _buildHeader(), const SizedBox(height: 48), _buildLoginCard(), const SizedBox(height: 32), _buildFooter(), ], ), ), ), ), ), ), ), ), ); }, ); } Widget _buildHeader() { final theme = ShadTheme.of(context); return Column( children: [ // 로고 및 애니메이션 Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( shape: BoxShape.circle, gradient: LinearGradient( colors: [ theme.colorScheme.primary, theme.colorScheme.primary.withValues(alpha: 0.8), theme.colorScheme.primary.withValues(alpha: 0.6), ], begin: Alignment.topLeft, end: Alignment.bottomRight, ), boxShadow: [ BoxShadow( color: theme.colorScheme.primary.withValues(alpha: 0.3), blurRadius: 20, offset: const Offset(0, 10), ), ], ), child: AnimatedBuilder( animation: _fadeController, builder: (context, child) { return Transform.rotate( angle: _fadeController.value * 2 * math.pi * 0.1, child: Icon( Icons.directions_boat, size: 48, color: Colors.white, ), ); }, ), ), const SizedBox(height: 24), // 앱 이름 Text( 'supERPort', style: theme.textTheme.h1.copyWith( foreground: Paint() ..shader = LinearGradient( colors: [theme.colorScheme.primary, theme.colorScheme.primary.withValues(alpha: 0.7)], ).createShader(const Rect.fromLTWH(0, 0, 200, 70)), ), ), const SizedBox(height: 8), Text('스마트 ERP 시스템', style: theme.textTheme.muted), ], ); } Widget _buildLoginCard() { final controller = widget.controller; final theme = ShadTheme.of(context); return ShadCard( child: Padding( padding: const EdgeInsets.all(32), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // 로그인 헤더 Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('로그인', style: theme.textTheme.h3), const SizedBox(height: 4), Text('계정 정보를 입력하여 로그인하세요.', style: theme.textTheme.muted), ], ), const SizedBox(height: 32), // 아이디/이메일 입력 Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('아이디/이메일', style: theme.textTheme.small), const SizedBox(height: 4), ShadInputFormField( controller: controller.idController, placeholder: const Text('아이디 또는 이메일을 입력하세요'), keyboardType: TextInputType.text, ), ], ), const SizedBox(height: 16), // 비밀번호 입력 Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('비밀번호', style: theme.textTheme.small), const SizedBox(height: 4), ShadInputFormField( controller: controller.pwController, placeholder: const Text('비밀번호를 입력하세요'), obscureText: true, keyboardType: TextInputType.visiblePassword, ), ], ), const SizedBox(height: 16), // 아이디 저장 체크박스 Row( children: [ ShadCheckbox( value: controller.saveId, onChanged: (value) { controller.setSaveId(value); }, ), const SizedBox(width: 8), Text('아이디 저장', style: theme.textTheme.small), ], ), const SizedBox(height: 32), // 에러 메시지 표시 if (controller.errorMessage != null) Padding( padding: const EdgeInsets.only(bottom: 16), child: ShadAlert.destructive( title: const Text('로그인 오류'), description: Text(controller.errorMessage!), ), ), // 로그인 버튼 ShadButton( onPressed: controller.isLoading ? null : _handleLogin, size: ShadButtonSize.lg, child: controller.isLoading ? const SizedBox( width: 16, height: 16, child: CircularProgressIndicator( strokeWidth: 2, valueColor: AlwaysStoppedAnimation(Colors.white), ), ) : const Text('로그인'), ), const SizedBox(height: 16), // 테스트 로그인 버튼 ShadButton.outline( onPressed: controller.isLoading ? null : () async { // 테스트 계정 정보 자동 입력 widget.controller.idController.text = 'admin@example.com'; widget.controller.pwController.text = 'password123'; // 실제 로그인 프로세스 실행 await _handleLogin(); }, size: ShadButtonSize.lg, child: const Text('테스트 로그인'), ), ], ), ), ); } Widget _buildFooter() { final theme = ShadTheme.of(context); return Column( children: [ // 기능 소개 Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: theme.colorScheme.muted, borderRadius: BorderRadius.circular(12), border: Border.all(color: theme.colorScheme.border), ), child: Row( children: [ Container( padding: const EdgeInsets.all(8), decoration: BoxDecoration( color: theme.colorScheme.primary.withValues(alpha: 0.1), borderRadius: BorderRadius.circular(8), ), child: Icon( Icons.info_outline, size: 16, color: theme.colorScheme.primary, ), ), const SizedBox(width: 12), Expanded( child: Text( '장비 관리, 회사 관리, 사용자 관리 등\n포트 운영에 필요한 모든 기능을 제공합니다.', style: theme.textTheme.small, ), ), ], ), ), const SizedBox(height: 24), // 저작권 정보 Text( 'Copyright 2025 NatureBridgeAI. All rights reserved.', style: theme.textTheme.small.copyWith( color: theme.colorScheme.foreground.withValues(alpha: 0.7), fontWeight: FontWeight.w500, ), ), ], ); } }